"Fossies" - the Fresh Open Source Software Archive

Member "seahorse-3.34/pgp/seahorse-gpgme-key-op.c" (1 Oct 2019, 72928 Bytes) of package /linux/privat/seahorse-3.34.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "seahorse-gpgme-key-op.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes reports: 3.31.91_vs_3.32 or 3.30.1.1_vs_3.32.

    1 /*
    2  * Seahorse
    3  *
    4  * Copyright (C) 2003 Jacob Perkins
    5  * Copyright (C) 2004 Stefan Walter
    6  * Copyright (C) 2005 Adam Schreiber
    7  * Copyright (C) 2011 Collabora Ltd.
    8  *
    9  * This program is free software; you can redistribute it and/or modify
   10  * it under the terms of the GNU General Public License as published by
   11  * the Free Software Foundation; either version 2 of the License, or
   12  * (at your option) any later version.
   13  *
   14  * This program is distributed in the hope that it will be useful,
   15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   17  * See the GNU General Public License for more details.
   18  * You should have received a copy of the GNU General Public License
   19  * along with this program; if not, see
   20  * <http://www.gnu.org/licenses/>.
   21  */
   22 
   23 #include "config.h"
   24 
   25 #include "seahorse-gpgme-key-op.h"
   26 
   27 #include "seahorse-gpgme.h"
   28 #include "seahorse-gpgme-data.h"
   29 #include "seahorse-gpg-op.h"
   30 
   31 #include "libseahorse/seahorse-object-list.h"
   32 #include "libseahorse/seahorse-progress.h"
   33 #include "libseahorse/seahorse-util.h"
   34 
   35 #include <glib/gstdio.h>
   36 #include <glib/gi18n.h>
   37 
   38 #include <errno.h>
   39 #include <stdlib.h>
   40 #include <string.h>
   41 #include <unistd.h>
   42 
   43 #define PROMPT "keyedit.prompt"
   44 #define QUIT "quit"
   45 #define SAVE "keyedit.save.okay"
   46 #define YES "Y"
   47 #define NO "N"
   48 
   49 #define PRINT(args)  if(!seahorse_util_print_fd args) return GPG_E (GPG_ERR_GENERAL)
   50 #define PRINTF(args) if(!seahorse_util_printf_fd args) return GPG_E (GPG_ERR_GENERAL)
   51 
   52 #define GPG_UNKNOWN     1
   53 #define GPG_NEVER       2
   54 #define GPG_MARGINAL    3
   55 #define GPG_FULL        4
   56 #define GPG_ULTIMATE    5
   57 
   58 static void
   59 on_key_op_progress (void *opaque,
   60                     const char *what,
   61                     int type,
   62                     int current,
   63                     int total)
   64 {
   65     GTask *task = G_TASK (opaque);
   66     seahorse_progress_update (g_task_get_cancellable (task), task, "%s", what);
   67 }
   68 
   69 static gboolean
   70 on_key_op_generate_complete (gpgme_error_t gerr,
   71                              gpointer user_data)
   72 {
   73     GTask *task = G_TASK (user_data);
   74     g_autoptr(GError) error = NULL;
   75 
   76     if (seahorse_gpgme_propagate_error (gerr, &error)) {
   77         g_task_return_error (task, g_steal_pointer (&error));
   78         return FALSE; /* don't call again */
   79     }
   80 
   81     seahorse_progress_end (g_task_get_cancellable (task), task);
   82     g_task_return_boolean (task, TRUE);
   83     return FALSE; /* don't call again */
   84 }
   85 
   86 
   87 /**
   88  * seahorse_gpgme_key_op_generate:
   89  * @sksrc: #SeahorseSource
   90  * @name: User ID name, must be at least 5 characters long
   91  * @email: Optional user ID email
   92  * @comment: Optional user ID comment
   93  * @passphrase: Passphrase for key
   94  * @type: Key type. Supported types are #DSA_ELGAMAL, #DSA, #RSA_SIGN, and #RSA_RSA
   95  * @length: Length of key, must be within the range of @type specified by #SeahorseKeyLength
   96  * @expires: Expiration date of key
   97  * 
   98  * Tries to generate a new key based on given parameters.
   99  **/
  100 void
  101 seahorse_gpgme_key_op_generate_async (SeahorseGpgmeKeyring *keyring,
  102                                       const gchar *name,
  103                                       const gchar *email,
  104                                       const gchar *comment,
  105                                       const gchar *passphrase,
  106                                       const SeahorseKeyEncType type,
  107                                       const guint length,
  108                                       const time_t expires,
  109                                       GCancellable *cancellable,
  110                                       GAsyncReadyCallback callback,
  111                                       gpointer user_data)
  112 {
  113     const gchar* key_type;
  114     g_autofree gchar *common = NULL, *start = NULL, *expires_date = NULL;
  115     gpgme_ctx_t gctx;
  116     g_autoptr(GTask) task = NULL;
  117     g_autoptr(GError) error = NULL;
  118     const gchar *parms;
  119     gpgme_error_t gerr = 0;
  120     g_autoptr(GSource) gsource = NULL;
  121 
  122     g_return_if_fail (SEAHORSE_IS_GPGME_KEYRING (keyring));
  123     g_return_if_fail (name);
  124     g_return_if_fail (strlen (name) > 4);
  125     g_return_if_fail (passphrase);
  126 
  127     /* Check lengths for each type */
  128     switch (type) {
  129     case DSA_ELGAMAL:
  130         g_return_if_fail (length >= ELGAMAL_MIN || length <= LENGTH_MAX);
  131         break;
  132     case DSA:
  133         g_return_if_fail (length >= DSA_MIN || length <= DSA_MAX);
  134         break;
  135     case RSA_RSA:
  136     case RSA_SIGN:
  137         g_return_if_fail (length >= RSA_MIN || length <= LENGTH_MAX);
  138         break;
  139     default:
  140         g_return_if_reached ();
  141         break;
  142     }
  143 
  144     if (expires != 0)
  145         expires_date = seahorse_util_get_date_string (expires);
  146     else
  147         expires_date = g_strdup ("0");
  148 
  149     /* Common xml */
  150     common = g_strdup_printf ("Name-Real: %s\nExpire-Date: %s\nPassphrase: %s\n"
  151             "</GnupgKeyParms>", name, expires_date, passphrase);
  152     if (email != NULL && strlen (email) > 0)
  153         common = g_strdup_printf ("Name-Email: %s\n%s", email, common);
  154     if (comment != NULL && strlen (comment) > 0)
  155         common = g_strdup_printf ("Name-Comment: %s\n%s", comment, common);
  156 
  157     if (type == DSA || type == DSA_ELGAMAL)
  158         key_type = "Key-Type: DSA\nKey-Usage: sign";
  159     else
  160         key_type = "Key-Type: RSA\nKey-Usage: sign";
  161 
  162     start = g_strdup_printf ("<GnupgKeyParms format=\"internal\">\n%s\nKey-Length: ", key_type);
  163 
  164     /* Subkey xml */
  165     if (type == DSA_ELGAMAL)
  166         parms = g_strdup_printf ("%s%d\nSubkey-Type: ELG-E\nSubkey-Length: %d\nSubkey-Usage: encrypt\n%s",
  167                                  start, (length < DSA_MAX) ? length : DSA_MAX, length, common);
  168     else if (type == RSA_RSA)
  169         parms = g_strdup_printf ("%s%d\nSubkey-Type: RSA\nSubkey-Length: %d\nSubkey-Usage: encrypt\n%s",
  170                                  start, length, length, common);
  171     else
  172         parms = g_strdup_printf ("%s%d\n%s", start, length, common);
  173 
  174     gctx = seahorse_gpgme_keyring_new_context (&gerr);
  175 
  176     task = g_task_new (keyring, cancellable, callback, user_data);
  177     gpgme_set_progress_cb (gctx, on_key_op_progress, task);
  178     g_task_set_task_data (task, gctx, (GDestroyNotify) gpgme_release);
  179 
  180     seahorse_progress_prep_and_begin (cancellable, task, NULL);
  181     gsource = seahorse_gpgme_gsource_new (gctx, cancellable);
  182     g_source_set_callback (gsource, (GSourceFunc)on_key_op_generate_complete,
  183                            g_object_ref (task), g_object_unref);
  184 
  185     if (gerr == 0)
  186         gerr = gpgme_op_genkey_start (gctx, parms, NULL, NULL);
  187 
  188     if (seahorse_gpgme_propagate_error (gerr, &error)) {
  189         g_task_return_error (task, g_steal_pointer (&error));
  190         return;
  191     }
  192 
  193     g_source_attach (gsource, g_main_context_default ());
  194 }
  195 
  196 gboolean
  197 seahorse_gpgme_key_op_generate_finish (SeahorseGpgmeKeyring *keyring,
  198                                        GAsyncResult *result,
  199                                        GError **error)
  200 {
  201     g_return_val_if_fail (g_task_is_valid (result, keyring), FALSE);
  202 
  203     return g_task_propagate_boolean (G_TASK (result), error);
  204 }
  205 
  206 /* helper function for deleting @skey */
  207 static gpgme_error_t
  208 op_delete (SeahorseGpgmeKey *pkey, gboolean secret)
  209 {
  210     SeahorseGpgmeKeyring *keyring;
  211     gpgme_error_t gerr;
  212     gpgme_key_t key;
  213     gpgme_ctx_t ctx;
  214 
  215     keyring = SEAHORSE_GPGME_KEYRING (seahorse_object_get_place (SEAHORSE_OBJECT (pkey)));
  216     g_return_val_if_fail (SEAHORSE_IS_GPGME_KEYRING (keyring), GPG_E (GPG_ERR_INV_KEYRING));
  217 
  218     g_object_ref (pkey);
  219 
  220     seahorse_util_wait_until ((key = seahorse_gpgme_key_get_public (pkey)) != NULL);
  221 
  222     ctx = seahorse_gpgme_keyring_new_context (&gerr);
  223     if (ctx == NULL)
  224         return gerr;
  225 
  226     gerr = gpgme_op_delete (ctx, key, secret);
  227     if (GPG_IS_OK (gerr))
  228         seahorse_gpgme_keyring_remove_key (keyring, SEAHORSE_GPGME_KEY (pkey));
  229 
  230     gpgme_release (ctx);
  231     g_object_unref (pkey);
  232     return gerr;
  233 }
  234 
  235 gpgme_error_t
  236 seahorse_gpgme_key_op_delete (SeahorseGpgmeKey *pkey)
  237 {
  238     return op_delete (pkey, FALSE);
  239 }
  240 
  241 
  242 gpgme_error_t
  243 seahorse_gpgme_key_op_delete_pair (SeahorseGpgmeKey *pkey)
  244 {
  245     return op_delete (pkey, TRUE);
  246 }
  247 
  248 /* Main key edit setup, structure, and a good deal of method content borrowed from gpa */
  249 
  250 /* Edit action function */
  251 typedef gpgme_error_t   (*SeahorseEditAction)   (guint          state,
  252                          gpointer       data,
  253                          gint                   fd);
  254 /* Edit transit function */
  255 typedef guint       (*SeahorseEditTransit)  (guint          current_state,
  256                          gpgme_status_code_t    status,
  257                          const gchar        *args,
  258                          gpointer       data,
  259                          gpgme_error_t      *err);
  260 
  261 /* Edit parameters */
  262 typedef struct
  263 {
  264     guint           state;
  265     gpgme_error_t       err;
  266     SeahorseEditAction  action;
  267     SeahorseEditTransit transit;
  268     gpointer        data;
  269     
  270 } SeahorseEditParm;
  271 
  272 /* Creates new edit parameters with defaults */
  273 static SeahorseEditParm*
  274 seahorse_edit_parm_new (guint state, SeahorseEditAction action,
  275             SeahorseEditTransit transit, gpointer data)
  276 {
  277     SeahorseEditParm *parms;
  278     
  279     parms = g_new0 (SeahorseEditParm, 1);
  280     parms->state = state;
  281     parms->err = GPG_OK;
  282     parms->action = action;
  283     parms->transit = transit;
  284     parms->data = data;
  285     
  286     return parms;
  287 }
  288 
  289 /* Edit callback for gpgme */
  290 static gpgme_error_t
  291 seahorse_gpgme_key_op_edit (gpointer data, gpgme_status_code_t status,
  292                             const gchar *args, int fd)
  293 {
  294     SeahorseEditParm *parms = (SeahorseEditParm*)data;
  295     
  296     /* Ignore these status lines, as they don't require any response */
  297     if (status == GPGME_STATUS_EOF || status == GPGME_STATUS_GOT_IT ||
  298         status == GPGME_STATUS_NEED_PASSPHRASE || status == GPGME_STATUS_GOOD_PASSPHRASE ||
  299         status == GPGME_STATUS_BAD_PASSPHRASE || status == GPGME_STATUS_USERID_HINT ||
  300         status == GPGME_STATUS_SIGEXPIRED || status == GPGME_STATUS_KEYEXPIRED ||
  301         status == GPGME_STATUS_PROGRESS || status == GPGME_STATUS_KEY_CREATED ||
  302         status == GPGME_STATUS_ALREADY_SIGNED || status == GPGME_STATUS_MISSING_PASSPHRASE ||
  303         status == GPGME_STATUS_KEY_CONSIDERED)
  304         return parms->err;
  305 
  306     g_debug ("[edit key] state: %d / status: %d / args: %s",
  307              parms->state, status, args);
  308 
  309     /* Choose the next state based on the current one and the input */
  310     parms->state = parms->transit (parms->state, status, args, parms->data, &parms->err);
  311     
  312     /* Choose the action based on the state */
  313     if (GPG_IS_OK (parms->err))
  314         parms->err = parms->action (parms->state, parms->data, fd);
  315     
  316     return parms->err;
  317 }
  318 
  319 /* Common edit operation */
  320 static gpgme_error_t
  321 edit_gpgme_key (gpgme_ctx_t ctx,
  322                 gpgme_key_t key,
  323                 SeahorseEditParm *parms)
  324 {
  325     gboolean own_context = FALSE;
  326     gpgme_data_t out;
  327     gpgme_error_t gerr;
  328 
  329     g_assert (key);
  330     g_assert (parms);
  331 
  332     gpgme_key_ref (key);
  333 
  334     if (ctx == NULL) {
  335         ctx = seahorse_gpgme_keyring_new_context (&gerr);
  336         if (ctx == NULL)
  337             return gerr;
  338         own_context = TRUE;
  339     }
  340 
  341     out = seahorse_gpgme_data_new ();
  342 
  343     /* do edit callback, release data */
  344     gerr = gpgme_op_edit (ctx, key, seahorse_gpgme_key_op_edit, parms, out);
  345 
  346     if (gpgme_err_code (gerr) == GPG_ERR_BAD_PASSPHRASE) {
  347         seahorse_util_show_error(NULL, _("Wrong password"), _("This was the third time you entered a wrong password. Please try again."));
  348     }
  349 
  350     seahorse_gpgme_data_release (out);
  351     if (own_context)
  352         gpgme_release (ctx);
  353     gpgme_key_unref (key);
  354     return gerr;
  355 }
  356 
  357 static gpgme_error_t
  358 edit_refresh_gpgme_key (gpgme_ctx_t ctx, gpgme_key_t key, SeahorseEditParm *parms)
  359 {
  360     gpgme_error_t gerr;
  361     
  362     gerr = edit_gpgme_key (ctx, key, parms);
  363     if (GPG_IS_OK (gerr))
  364         seahorse_gpgme_key_refresh_matching (key);
  365     
  366     return gerr;
  367 }
  368 
  369 static gpgme_error_t
  370 edit_key (SeahorseGpgmeKey *pkey, SeahorseEditParm *parms)
  371 {
  372     SeahorseGpgmeKeyring *keyring;
  373     gpgme_error_t gerr;
  374     gpgme_key_t key;
  375     gpgme_ctx_t ctx;
  376 
  377     keyring = SEAHORSE_GPGME_KEYRING (seahorse_object_get_place (SEAHORSE_OBJECT (pkey)));
  378     g_return_val_if_fail (SEAHORSE_IS_GPGME_KEYRING (keyring), GPG_E (GPG_ERR_INV_KEYRING));
  379 
  380     g_object_ref (pkey);
  381 
  382     seahorse_util_wait_until ((key = seahorse_gpgme_key_get_public (pkey)) != NULL);
  383 
  384     ctx = seahorse_gpgme_keyring_new_context (&gerr);
  385     if (ctx != NULL) {
  386         gerr = edit_refresh_gpgme_key (ctx, key, parms);
  387         gpgme_release (ctx);
  388     }
  389 
  390     g_object_unref (pkey);
  391     return gerr;
  392 }
  393 
  394 typedef struct
  395 {
  396     guint           index;
  397     gchar           *command;
  398     gboolean        expire;
  399     SeahorseSignCheck   check;
  400 } SignParm;
  401 
  402 typedef enum
  403 {
  404     SIGN_START,
  405     SIGN_UID,
  406     SIGN_COMMAND,
  407     SIGN_EXPIRE,
  408     SIGN_CONFIRM,
  409     SIGN_CHECK,
  410     SIGN_QUIT,
  411     SIGN_ERROR
  412 } SignState;
  413 
  414 /* action helper for signing a key */
  415 static gpgme_error_t
  416 sign_action (guint state, gpointer data, int fd)
  417 {
  418     SignParm *parm = (SignParm*)data;
  419     
  420     switch (state) {
  421         /* select uid */
  422         case SIGN_UID:
  423             PRINTF ((fd, "uid %d", parm->index));
  424             break;
  425         case SIGN_COMMAND:
  426             PRINT ((fd, parm->command));
  427             break;
  428         /* if expires */
  429         case SIGN_EXPIRE:
  430             PRINT ((fd, (parm->expire) ? YES : "N"));
  431             break;
  432         case SIGN_CONFIRM:
  433             PRINT ((fd, YES));
  434             break;
  435         case SIGN_CHECK:
  436             PRINTF ((fd, "%d", parm->check));
  437             break;
  438         case SIGN_QUIT:
  439             PRINT ((fd, QUIT));
  440             break;
  441         default:
  442             return GPG_E (GPG_ERR_GENERAL);
  443     }
  444     
  445     PRINT ((fd, "\n"));
  446     return GPG_OK;
  447 }
  448 
  449 /* transition helper for signing a key */
  450 static guint
  451 sign_transit (guint current_state, gpgme_status_code_t status,
  452           const gchar *args, gpointer data, gpgme_error_t *err)
  453 {
  454     guint next_state;
  455     
  456     switch (current_state) {
  457         /* start state, need to select uid */
  458         case SIGN_START:
  459             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
  460                 next_state = SIGN_UID;
  461             else {
  462                 *err = GPG_E (GPG_ERR_GENERAL);
  463                 g_return_val_if_reached (SIGN_ERROR);
  464             }
  465             break;
  466         /* selected uid, go to command */
  467         case SIGN_UID:
  468             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
  469                 next_state = SIGN_COMMAND;
  470             else {
  471                 *err = GPG_E (GPG_ERR_GENERAL);
  472                 g_return_val_if_reached (SIGN_ERROR);
  473             }
  474             break;
  475         case SIGN_COMMAND:
  476             /* if doing all uids, confirm */
  477             if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, "keyedit.sign_all.okay"))
  478                 next_state = SIGN_CONFIRM;
  479             else if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, "sign_uid.okay"))
  480                 next_state = SIGN_CONFIRM;
  481             /* need to do expires */
  482             else if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "sign_uid.expire"))
  483                 next_state = SIGN_EXPIRE;
  484             /*  need to do check */
  485             else if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "sign_uid.class"))
  486                 next_state = SIGN_CHECK;
  487             /* if it's already signed then send back an error */
  488             else if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT)) {
  489                 next_state = SIGN_ERROR;
  490                 *err = GPG_E (GPG_ERR_EALREADY);
  491             /* All other stuff is unexpected */
  492             } else {
  493                 *err = GPG_E (GPG_ERR_GENERAL);
  494                 g_return_val_if_reached (SIGN_ERROR);
  495             }
  496             break;
  497         /* did expire, go to check */
  498         case SIGN_EXPIRE:
  499             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "sign_uid.class"))
  500                 next_state = SIGN_CHECK;
  501             else {
  502                 *err = GPG_E (GPG_ERR_GENERAL);
  503                 g_return_val_if_reached (SIGN_ERROR);
  504             }
  505             break;
  506         case SIGN_CONFIRM:
  507             /* need to do check */
  508             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "sign_uid.class"))
  509                 next_state = SIGN_CHECK;
  510             else if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, "sign_uid.okay"))
  511                 next_state = SIGN_CONFIRM;
  512             /* need to do expire */
  513             else if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "sign_uid.expire"))
  514                 next_state = SIGN_EXPIRE;
  515             /* quit */
  516             else if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
  517                 next_state = SIGN_QUIT;
  518             else {
  519                 *err = GPG_E (GPG_ERR_GENERAL);
  520                 g_return_val_if_reached (SIGN_ERROR);
  521             }
  522             break;
  523         /* did check, go to confirm */
  524         case SIGN_CHECK:
  525             if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, "sign_uid.okay"))
  526                 next_state = SIGN_CONFIRM;
  527             else {
  528                 *err = GPG_E (GPG_ERR_GENERAL);
  529                 g_return_val_if_reached (SIGN_ERROR);
  530             }
  531             break;
  532         /* quit, go to confirm to save */
  533         case SIGN_QUIT:
  534             if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, SAVE))
  535                 next_state = SIGN_CONFIRM;
  536             else {
  537                 *err = GPG_E (GPG_ERR_GENERAL);
  538                 g_return_val_if_reached (SIGN_ERROR);
  539             }
  540             break;
  541         /* error, go to quit */
  542         case SIGN_ERROR:
  543             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
  544                 next_state = SIGN_QUIT;
  545             else
  546                 next_state = SIGN_ERROR;
  547             break;
  548           
  549         default:
  550             *err = GPG_E (GPG_ERR_GENERAL);
  551             g_return_val_if_reached (SIGN_ERROR);
  552             break;
  553     }
  554     
  555     return next_state;
  556 }
  557 
  558 static gpgme_error_t
  559 sign_process (gpgme_key_t signed_key, gpgme_key_t signing_key, guint sign_index, 
  560               SeahorseSignCheck check, SeahorseSignOptions options)
  561 {
  562     SeahorseEditParm *parms;
  563     SignParm sign_parm;
  564     gpgme_ctx_t ctx;
  565     gpgme_error_t gerr;
  566 
  567     ctx = seahorse_gpgme_keyring_new_context (&gerr);
  568     if (ctx == NULL)
  569         return gerr;
  570 
  571         gerr = gpgme_signers_add (ctx, signing_key);
  572         if (!GPG_IS_OK (gerr))
  573             return gerr;
  574     
  575     sign_parm.index = sign_index;
  576     sign_parm.expire = ((options & SIGN_EXPIRES) != 0);
  577     sign_parm.check = check;
  578     sign_parm.command = g_strdup_printf ("%s%ssign", 
  579                                          (options & SIGN_NO_REVOKE) ? "nr" : "",
  580                                          (options & SIGN_LOCAL) ? "l" : "");
  581 
  582     parms = seahorse_edit_parm_new (SIGN_START, sign_action, sign_transit, &sign_parm);
  583 
  584     gerr =  edit_refresh_gpgme_key (ctx, signed_key, parms);
  585     g_free (sign_parm.command);
  586     g_free (parms);
  587      
  588     gpgme_release (ctx);
  589 
  590     return gerr;
  591 }
  592 
  593 gpgme_error_t
  594 seahorse_gpgme_key_op_sign_uid (SeahorseGpgmeUid *uid,  SeahorseGpgmeKey *signer, 
  595                                 SeahorseSignCheck check, SeahorseSignOptions options)
  596 {
  597     gpgme_key_t signing_key;
  598     gpgme_key_t signed_key;
  599     guint sign_index;
  600     
  601     seahorse_gpgme_key_get_private (signer);
  602 
  603     g_return_val_if_fail (SEAHORSE_GPGME_IS_UID (uid), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
  604     g_return_val_if_fail (SEAHORSE_GPGME_IS_KEY (signer), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
  605     
  606     signing_key = seahorse_gpgme_key_get_private (signer);
  607     g_return_val_if_fail (signing_key, GPG_E (GPG_ERR_WRONG_KEY_USAGE));
  608     
  609     signed_key = seahorse_gpgme_uid_get_pubkey (uid);
  610     g_return_val_if_fail (signing_key, GPG_E (GPG_ERR_INV_VALUE));
  611     
  612     sign_index = seahorse_gpgme_uid_get_actual_index (uid);
  613     
  614     return sign_process (signed_key, signing_key, sign_index, check, options);
  615 }
  616 
  617 gpgme_error_t
  618 seahorse_gpgme_key_op_sign (SeahorseGpgmeKey *pkey, SeahorseGpgmeKey *signer, 
  619                             SeahorseSignCheck check, SeahorseSignOptions options)
  620 {
  621     gpgme_key_t signing_key;
  622     gpgme_key_t signed_key;
  623     
  624     seahorse_gpgme_key_get_private (signer);
  625 
  626     g_return_val_if_fail (SEAHORSE_GPGME_IS_KEY (pkey), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
  627     g_return_val_if_fail (SEAHORSE_GPGME_IS_KEY (signer), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
  628     
  629     signing_key = seahorse_gpgme_key_get_private (signer);
  630     g_return_val_if_fail (signing_key, GPG_E (GPG_ERR_WRONG_KEY_USAGE));
  631     
  632     signed_key = seahorse_gpgme_key_get_public (pkey);
  633 
  634     return sign_process (signed_key, signing_key, 0, check, options);
  635 }
  636 
  637 static gboolean
  638 on_key_op_change_pass_complete (gpgme_error_t gerr,
  639                                 gpointer user_data)
  640 {
  641     GTask *task = G_TASK (user_data);
  642     g_autoptr(GError) error = NULL;
  643 
  644     if (seahorse_gpgme_propagate_error (gerr, &error)) {
  645         g_task_return_error (task, g_steal_pointer (&error));
  646         return FALSE; /* don't call again */
  647     }
  648 
  649     seahorse_progress_end (g_task_get_cancellable (task), task);
  650     g_task_return_boolean (task, TRUE);
  651     return FALSE; /* don't call again */
  652 }
  653 
  654 /**
  655  * seahorse_gpgme_key_op_change_pass_async:
  656  * @pkey: The key that you want to change the password of
  657  * @cancellable: (nullable): A #GCancellable
  658  * @callback: The callback that will be called when the operation finishes
  659  * @user_data: (closure callback): User data passed on to @callback
  660  *
  661  * Changes the password of @pkey. The actual changing will be done by GPGME, so
  662  * this function doesn't allow to specify the new password.
  663  */
  664 void
  665 seahorse_gpgme_key_op_change_pass_async (SeahorseGpgmeKey *pkey,
  666                                          GCancellable *cancellable,
  667                                          GAsyncReadyCallback callback,
  668                                          gpointer user_data)
  669 {
  670     g_autoptr(GTask) task = NULL;
  671     gpgme_ctx_t gctx;
  672     gpgme_error_t gerr;
  673     g_autoptr(GError) error = NULL;
  674     g_autoptr(GSource) gsource = NULL;
  675     gpgme_key_t key;
  676 
  677     g_return_if_fail (SEAHORSE_GPGME_IS_KEY (pkey));
  678     g_return_if_fail (seahorse_object_get_usage (SEAHORSE_OBJECT (pkey)) == SEAHORSE_USAGE_PRIVATE_KEY);
  679 
  680     gctx = seahorse_gpgme_keyring_new_context (&gerr);
  681 
  682     task = g_task_new (pkey, cancellable, callback, user_data);
  683     gpgme_set_progress_cb (gctx, on_key_op_progress, task);
  684     g_task_set_task_data (task, gctx, (GDestroyNotify) gpgme_release);
  685 
  686     seahorse_progress_prep_and_begin (cancellable, task, NULL);
  687     gsource = seahorse_gpgme_gsource_new (gctx, cancellable);
  688     g_source_set_callback (gsource, (GSourceFunc)on_key_op_change_pass_complete,
  689                            g_object_ref (task), g_object_unref);
  690 
  691     key = seahorse_gpgme_key_get_private (pkey);
  692     if (gerr == 0)
  693         gerr = gpgme_op_passwd_start (gctx, key, 0);
  694 
  695     if (seahorse_gpgme_propagate_error (gerr, &error)) {
  696         g_task_return_error (task, g_steal_pointer (&error));
  697         return;
  698     }
  699 
  700     g_source_attach (gsource, g_main_context_default ());
  701 }
  702 
  703 gboolean
  704 seahorse_gpgme_key_op_change_pass_finish (SeahorseGpgmeKey *pkey,
  705                                           GAsyncResult *result,
  706                                           GError **error)
  707 {
  708     g_return_val_if_fail (g_task_is_valid (result, pkey), FALSE);
  709 
  710     return g_task_propagate_boolean (G_TASK (result), error);
  711 }
  712 
  713 typedef enum
  714 {
  715     TRUST_START,
  716     TRUST_COMMAND,
  717     TRUST_VALUE,
  718     TRUST_CONFIRM,
  719     TRUST_QUIT,
  720     TRUST_ERROR
  721 } TrustState;
  722 
  723 /* action helper for setting trust of a key */
  724 static gpgme_error_t
  725 edit_trust_action (guint state, gpointer data, int fd)
  726 {
  727     gint trust = GPOINTER_TO_INT (data);
  728     
  729     switch (state) {
  730         /* enter command */
  731         case TRUST_COMMAND:
  732             PRINT ((fd, "trust"));
  733             break;
  734         /* enter numeric trust value */
  735         case TRUST_VALUE:
  736             PRINTF ((fd, "%d", trust));
  737             break;
  738         /* confirm ultimate or if save */
  739         case TRUST_CONFIRM:
  740             PRINT ((fd, YES));
  741             break;
  742         /* quit */
  743         case TRUST_QUIT:
  744             PRINT ((fd, QUIT));
  745             break;
  746         default:
  747             return GPG_E (GPG_ERR_GENERAL);
  748     }
  749     
  750     PRINT ((fd, "\n"));
  751     return GPG_OK;
  752 }
  753 
  754 /* transition helper for setting trust of a key */
  755 static guint
  756 edit_trust_transit (guint current_state, gpgme_status_code_t status,
  757             const gchar *args, gpointer data, gpgme_error_t *err)
  758 {
  759     guint next_state;
  760     
  761     switch (current_state) {
  762         /* start state */
  763         case TRUST_START:
  764             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
  765                 next_state = TRUST_COMMAND;
  766             else {
  767                 *err = GPG_E (GPG_ERR_GENERAL);
  768                 g_return_val_if_reached (TRUST_ERROR);
  769             }
  770             break;
  771         /* did command, next is value */
  772         case TRUST_COMMAND:
  773             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "edit_ownertrust.value"))
  774                 next_state = TRUST_VALUE;
  775             else {
  776                 *err = GPG_E (GPG_ERR_GENERAL);
  777                 g_return_val_if_reached (TRUST_ERROR);
  778             }
  779             break;
  780         /* did value, go to quit or confirm ultimate */
  781         case TRUST_VALUE:
  782             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
  783                 next_state = TRUST_QUIT;
  784             else if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, "edit_ownertrust.set_ultimate.okay"))
  785                 next_state = TRUST_CONFIRM;
  786             else {
  787                 *err = GPG_E (GPG_ERR_GENERAL);
  788                 g_return_val_if_reached (TRUST_ERROR);
  789             }
  790             break;
  791         /* did confirm ultimate, go to quit */
  792         case TRUST_CONFIRM:
  793             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
  794                 next_state = TRUST_QUIT;
  795             else {
  796                 *err = GPG_E (GPG_ERR_GENERAL);
  797                 g_return_val_if_reached (TRUST_ERROR);
  798             }
  799             break;
  800         /* did quit, go to confirm to finish op */
  801         case TRUST_QUIT:
  802             if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, SAVE))
  803                 next_state = TRUST_CONFIRM;
  804             else {
  805                 *err = GPG_E (GPG_ERR_GENERAL);
  806                 g_return_val_if_reached (TRUST_ERROR);
  807             }
  808             break;
  809         /* error, go to quit */
  810         case TRUST_ERROR:
  811             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
  812                 next_state = TRUST_QUIT;
  813             else
  814                 next_state = TRUST_ERROR;
  815             break;
  816         default:
  817             *err = GPG_E (GPG_ERR_GENERAL);
  818             g_return_val_if_reached (TRUST_ERROR);
  819             break;
  820     }
  821     
  822     return next_state;
  823 }
  824 
  825 /**
  826  * seahorse_gpgme_key_op_set_trust:
  827  * @pkey: #SeahorseGpgmeKey whose trust will be changed
  828  * @trust: New trust value that must be at least #SEAHORSE_VALIDITY_NEVER.
  829  * If @pkey is a #SeahorseKeyPair, then @trust cannot be #SEAHORSE_VALIDITY_UNKNOWN.
  830  * If @pkey is not a #SeahorseKeyPair, then @trust cannot be #SEAHORSE_VALIDITY_ULTIMATE.
  831  *
  832  * Tries to change the owner trust of @pkey to @trust.
  833  *
  834  * Returns: Error value
  835  **/
  836 gpgme_error_t
  837 seahorse_gpgme_key_op_set_trust (SeahorseGpgmeKey *pkey, SeahorseValidity trust)
  838 {
  839     SeahorseEditParm *parms;
  840     gint menu_choice;
  841 
  842     g_debug ("[GPGME_KEY_OP] set_trust: trust = %i", trust);
  843 
  844     g_return_val_if_fail (SEAHORSE_GPGME_IS_KEY (pkey), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
  845     g_return_val_if_fail (trust >= SEAHORSE_VALIDITY_NEVER, GPG_E (GPG_ERR_INV_VALUE));
  846     g_return_val_if_fail (seahorse_gpgme_key_get_trust (pkey) != trust, GPG_E (GPG_ERR_INV_VALUE));
  847     
  848     if (seahorse_object_get_usage (SEAHORSE_OBJECT (pkey)) == SEAHORSE_USAGE_PRIVATE_KEY)
  849         g_return_val_if_fail (trust != SEAHORSE_VALIDITY_UNKNOWN, GPG_E (GPG_ERR_INV_VALUE));
  850     else
  851         g_return_val_if_fail (trust != SEAHORSE_VALIDITY_ULTIMATE, GPG_E (GPG_ERR_INV_VALUE));
  852     
  853     switch (trust) {
  854         case SEAHORSE_VALIDITY_NEVER:
  855             menu_choice = GPG_NEVER;
  856             break;
  857         case SEAHORSE_VALIDITY_UNKNOWN:
  858             menu_choice = GPG_UNKNOWN;
  859             break;
  860         case SEAHORSE_VALIDITY_MARGINAL:
  861             menu_choice = GPG_MARGINAL;
  862             break;
  863         case SEAHORSE_VALIDITY_FULL:
  864             menu_choice = GPG_FULL;
  865             break;
  866         case SEAHORSE_VALIDITY_ULTIMATE:
  867             menu_choice = GPG_ULTIMATE;
  868             break;
  869         default:
  870             menu_choice = 1;
  871     }
  872     
  873     parms = seahorse_edit_parm_new (TRUST_START, edit_trust_action,
  874         edit_trust_transit, GINT_TO_POINTER (menu_choice));
  875     
  876     return edit_key (pkey, parms);
  877 }
  878 
  879 typedef enum {
  880     DISABLE_START,
  881     DISABLE_COMMAND,
  882     DISABLE_QUIT,
  883     DISABLE_ERROR
  884 } DisableState;
  885 
  886 /* action helper for disable/enable a key */
  887 static gpgme_error_t
  888 edit_disable_action (guint state, gpointer data, int fd)
  889 {
  890     const gchar *command = data;
  891     
  892     switch (state) {
  893         case DISABLE_COMMAND:
  894             PRINT ((fd, command));
  895             break;
  896         case DISABLE_QUIT:
  897             PRINT ((fd, QUIT));
  898             break;
  899         default:
  900             break;
  901     }
  902     
  903     PRINT ((fd, "\n"));
  904     return GPG_OK;
  905 }
  906 
  907 /* transition helper for disable/enable a key */
  908 static guint
  909 edit_disable_transit (guint current_state, gpgme_status_code_t status,
  910               const gchar *args, gpointer data, gpgme_error_t *err)
  911 {
  912     guint next_state;
  913     
  914     switch (current_state) {
  915         /* start, do command */
  916         case DISABLE_START:
  917             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
  918                 next_state = DISABLE_COMMAND;
  919             else {
  920                 *err = GPG_E (GPG_ERR_GENERAL);
  921                 g_return_val_if_reached (DISABLE_ERROR);
  922             }
  923             break;
  924         /* did command, quit */
  925         case DISABLE_COMMAND:
  926             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
  927                 next_state = DISABLE_QUIT;
  928             else {
  929                 *err = GPG_E (GPG_ERR_GENERAL);
  930                 g_return_val_if_reached (DISABLE_ERROR);
  931             }
  932         /* error, quit */
  933         case DISABLE_ERROR:
  934             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
  935                 next_state = DISABLE_QUIT;
  936             else
  937                 next_state = DISABLE_ERROR;
  938             break;
  939         default:
  940             *err = GPG_E (GPG_ERR_GENERAL);
  941             g_return_val_if_reached (DISABLE_ERROR);
  942             break;
  943     }
  944     
  945     return next_state;
  946 }
  947 
  948 /**
  949  * seahorse_gpgme_key_op_set_disabled:
  950  * @skey: #SeahorseKey to change
  951  * @disabled: New disabled state
  952  *
  953  * Tries to change disabled state of @skey to @disabled.
  954  *
  955  * Returns: Error value
  956  **/
  957 gpgme_error_t
  958 seahorse_gpgme_key_op_set_disabled (SeahorseGpgmeKey *pkey, gboolean disabled)
  959 {
  960     gchar *command;
  961     SeahorseEditParm *parms;
  962     
  963     g_return_val_if_fail (SEAHORSE_GPGME_IS_KEY (pkey), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
  964     
  965     /* Get command and op */
  966     if (disabled)
  967         command = "disable";
  968     else
  969         command = "enable";
  970     
  971     parms = seahorse_edit_parm_new (DISABLE_START, edit_disable_action, edit_disable_transit, command);
  972     
  973     return edit_key (pkey, parms);
  974 }
  975 
  976 typedef struct
  977 {
  978     guint   index;
  979     time_t  expires;
  980 } ExpireParm;
  981 
  982 typedef enum
  983 {
  984     EXPIRE_START,
  985     EXPIRE_SELECT,
  986     EXPIRE_COMMAND,
  987     EXPIRE_DATE,
  988     EXPIRE_QUIT,
  989     EXPIRE_SAVE,
  990     EXPIRE_ERROR
  991 } ExpireState;
  992 
  993 /* action helper for changing expiration date of a key */
  994 static gpgme_error_t
  995 edit_expire_action (guint state, gpointer data, int fd)
  996 {
  997     ExpireParm *parm = (ExpireParm*)data;
  998   
  999     switch (state) {
 1000         /* selected key */
 1001         case EXPIRE_SELECT:
 1002             PRINTF ((fd, "key %d", parm->index));
 1003             break;
 1004         case EXPIRE_COMMAND:
 1005             PRINT ((fd, "expire"));
 1006             break;
 1007         /* set date */
 1008         case EXPIRE_DATE:
 1009             PRINT ((fd, (parm->expires) ?
 1010                 seahorse_util_get_date_string (parm->expires) : "0"));
 1011             break;
 1012         case EXPIRE_QUIT:
 1013             PRINT ((fd, QUIT));
 1014             break;
 1015         case EXPIRE_SAVE:
 1016             PRINT ((fd, YES));
 1017             break;
 1018         case EXPIRE_ERROR:
 1019             break;
 1020         default:
 1021             return GPG_E (GPG_ERR_GENERAL);
 1022     }
 1023 
 1024     PRINT ((fd, "\n"));
 1025     return GPG_OK;
 1026 }
 1027 
 1028 /* transition helper for changing expiration date of a key */
 1029 static guint
 1030 edit_expire_transit (guint current_state, gpgme_status_code_t status,
 1031              const gchar *args, gpointer data, gpgme_error_t *err)
 1032 {
 1033     guint next_state;
 1034  
 1035     switch (current_state) {
 1036         /* start state, selected key */
 1037         case EXPIRE_START:
 1038             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1039                 next_state = EXPIRE_SELECT;
 1040             else {
 1041                 *err = GPG_E (GPG_ERR_GENERAL);
 1042                 g_return_val_if_reached (EXPIRE_ERROR);
 1043             }
 1044             break;
 1045         /* selected key, do command */
 1046         case EXPIRE_SELECT:
 1047             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1048                 next_state = EXPIRE_COMMAND;
 1049             else {
 1050                 *err = GPG_E (GPG_ERR_GENERAL);
 1051                 g_return_val_if_reached (EXPIRE_ERROR);
 1052             }
 1053             break;
 1054         /* did command, set expires */
 1055         case EXPIRE_COMMAND:
 1056             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "keygen.valid"))
 1057                 next_state = EXPIRE_DATE;
 1058             else {
 1059                 *err = GPG_E (GPG_ERR_GENERAL);
 1060                 g_return_val_if_reached (EXPIRE_ERROR);
 1061             }
 1062             break;
 1063         /* set expires, quit */
 1064         case EXPIRE_DATE:
 1065             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1066                 next_state = EXPIRE_QUIT;
 1067             else {
 1068                 *err = GPG_E (GPG_ERR_GENERAL);
 1069                 g_return_val_if_reached (EXPIRE_ERROR);
 1070             }
 1071             break;
 1072         /* quit, save */
 1073         case EXPIRE_QUIT:
 1074             if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, SAVE))
 1075                 next_state = EXPIRE_SAVE;
 1076             else {
 1077                 *err = GPG_E (GPG_ERR_GENERAL);
 1078                 g_return_val_if_reached (EXPIRE_ERROR);
 1079             }
 1080             break;
 1081         /* error, quit */
 1082         case EXPIRE_ERROR:
 1083             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1084                 next_state = EXPIRE_QUIT;
 1085             else
 1086                 next_state = EXPIRE_ERROR;
 1087             break;
 1088         default:
 1089             *err = GPG_E (GPG_ERR_GENERAL);
 1090             g_return_val_if_reached (EXPIRE_ERROR);
 1091             break;
 1092     }
 1093     return next_state;
 1094 }
 1095 
 1096 gpgme_error_t
 1097 seahorse_gpgme_key_op_set_expires (SeahorseGpgmeSubkey *subkey, const time_t expires)
 1098 {
 1099     ExpireParm exp_parm;
 1100     SeahorseEditParm *parms;
 1101     gpgme_key_t key;
 1102     
 1103     g_return_val_if_fail (SEAHORSE_GPGME_IS_SUBKEY (subkey), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
 1104     g_return_val_if_fail (expires != (time_t)seahorse_pgp_subkey_get_expires (SEAHORSE_PGP_SUBKEY (subkey)), GPG_E (GPG_ERR_INV_VALUE));
 1105     
 1106     key = seahorse_gpgme_subkey_get_pubkey (subkey);
 1107     g_return_val_if_fail (key, GPG_E (GPG_ERR_INV_VALUE));
 1108     
 1109     exp_parm.index = seahorse_pgp_subkey_get_index (SEAHORSE_PGP_SUBKEY (subkey)); 
 1110     exp_parm.expires = expires;
 1111     
 1112     parms = seahorse_edit_parm_new (EXPIRE_START, edit_expire_action, edit_expire_transit, &exp_parm);
 1113     
 1114     return edit_refresh_gpgme_key (NULL, key, parms);
 1115 }
 1116 
 1117 typedef enum {
 1118     ADD_REVOKER_START,
 1119     ADD_REVOKER_COMMAND,
 1120     ADD_REVOKER_SELECT,
 1121     ADD_REVOKER_CONFIRM,
 1122     ADD_REVOKER_QUIT,
 1123     ADD_REVOKER_ERROR
 1124 } AddRevokerState;
 1125 
 1126 /* action helper for adding a revoker */
 1127 static gpgme_error_t
 1128 add_revoker_action (guint state, gpointer data, int fd)
 1129 {
 1130     gchar *keyid = (gchar*)data;
 1131     
 1132     switch (state) {
 1133         case ADD_REVOKER_COMMAND:
 1134             PRINT ((fd, "addrevoker"));
 1135             break;
 1136         /* select revoker */
 1137         case ADD_REVOKER_SELECT:
 1138             PRINT ((fd, keyid));
 1139             break;
 1140         case ADD_REVOKER_CONFIRM:
 1141             PRINT ((fd, YES));
 1142             break;
 1143         case ADD_REVOKER_QUIT:
 1144             PRINT ((fd, QUIT));
 1145             break;
 1146         default:
 1147             return GPG_E (GPG_ERR_GENERAL);
 1148     }
 1149     
 1150     PRINT ((fd, "\n"));
 1151     return GPG_OK;
 1152 }
 1153 
 1154 /* transition helper for adding a revoker */
 1155 static guint
 1156 add_revoker_transit (guint current_state, gpgme_status_code_t status,
 1157              const gchar *args, gpointer data, gpgme_error_t *err)
 1158 {
 1159     guint next_state;
 1160     
 1161     switch (current_state) {
 1162         /* start, do command */
 1163         case ADD_REVOKER_START:
 1164             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1165                 next_state = ADD_REVOKER_COMMAND;
 1166             else {
 1167                 *err = GPG_E (GPG_ERR_GENERAL);
 1168                 g_return_val_if_reached (ADD_REVOKER_ERROR);
 1169             }
 1170             break;
 1171         /* did command, select revoker */
 1172         case ADD_REVOKER_COMMAND:
 1173             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "keyedit.add_revoker"))
 1174                 next_state = ADD_REVOKER_SELECT;
 1175             else {
 1176                 *err = GPG_E (GPG_ERR_GENERAL);
 1177                 g_return_val_if_reached (ADD_REVOKER_ERROR);
 1178             }
 1179             break;
 1180         /* selected revoker, confirm */
 1181         case ADD_REVOKER_SELECT:
 1182             if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, "keyedit.add_revoker.okay"))
 1183                 next_state = ADD_REVOKER_CONFIRM;
 1184             else {
 1185                 *err = GPG_E (GPG_ERR_GENERAL);
 1186                 g_return_val_if_reached (ADD_REVOKER_ERROR);
 1187             }
 1188             break;
 1189         /* confirmed, quit */
 1190         case ADD_REVOKER_CONFIRM:
 1191             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1192                 next_state = ADD_REVOKER_QUIT;
 1193             else {
 1194                 *err = GPG_E (GPG_ERR_GENERAL);
 1195                 g_return_val_if_reached (ADD_REVOKER_ERROR);
 1196             }
 1197             break;
 1198         /* quit, confirm(=save) */
 1199         case ADD_REVOKER_QUIT:
 1200             if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, SAVE))
 1201                 next_state = ADD_REVOKER_CONFIRM;
 1202             else {
 1203                 *err = GPG_E (GPG_ERR_GENERAL);
 1204                 g_return_val_if_reached (ADD_REVOKER_ERROR);
 1205             }
 1206             break;
 1207         /* error, quit */
 1208         case ADD_REVOKER_ERROR:
 1209             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1210                 next_state = ADD_REVOKER_QUIT;
 1211             else
 1212                 next_state = ADD_REVOKER_ERROR;
 1213             break;
 1214         default:
 1215             *err = GPG_E (GPG_ERR_GENERAL);
 1216             g_return_val_if_reached (ADD_REVOKER_ERROR);
 1217             break;
 1218     }
 1219     
 1220     return next_state;
 1221 }
 1222 
 1223 gpgme_error_t
 1224 seahorse_gpgme_key_op_add_revoker (SeahorseGpgmeKey *pkey, SeahorseGpgmeKey *revoker)
 1225 {
 1226     SeahorseEditParm *parms;
 1227     const gchar *keyid;
 1228 
 1229     g_return_val_if_fail (SEAHORSE_GPGME_IS_KEY (pkey), GPG_E (GPG_ERR_WRONG_KEY_USAGE));    
 1230     g_return_val_if_fail (SEAHORSE_GPGME_IS_KEY (revoker), GPG_E (GPG_ERR_WRONG_KEY_USAGE));    
 1231     g_return_val_if_fail (seahorse_object_get_usage (SEAHORSE_OBJECT (pkey)) == SEAHORSE_USAGE_PRIVATE_KEY, GPG_E (GPG_ERR_WRONG_KEY_USAGE));
 1232     g_return_val_if_fail (seahorse_object_get_usage (SEAHORSE_OBJECT (revoker)) == SEAHORSE_USAGE_PRIVATE_KEY, GPG_E (GPG_ERR_WRONG_KEY_USAGE));
 1233 
 1234     keyid = seahorse_pgp_key_get_keyid (SEAHORSE_PGP_KEY (pkey));
 1235     g_return_val_if_fail (keyid, GPG_E (GPG_ERR_INV_VALUE));
 1236 
 1237     parms = seahorse_edit_parm_new (ADD_REVOKER_START, add_revoker_action,
 1238                                     add_revoker_transit, (gpointer)keyid);
 1239 
 1240     return edit_key (pkey, parms);
 1241 }
 1242 
 1243 static gboolean
 1244 on_key_op_add_uid_complete (gpgme_error_t gerr,
 1245                             gpointer user_data)
 1246 {
 1247     GTask *task = G_TASK (user_data);
 1248     g_autoptr(GError) error = NULL;
 1249 
 1250     if (seahorse_gpgme_propagate_error (gerr, &error)) {
 1251         g_task_return_error (task, g_steal_pointer (&error));
 1252         return FALSE; /* don't call again */
 1253     }
 1254 
 1255     seahorse_progress_end (g_task_get_cancellable (task), task);
 1256     g_task_return_boolean (task, TRUE);
 1257     return FALSE; /* don't call again */
 1258 }
 1259 
 1260 void
 1261 seahorse_gpgme_key_op_add_uid_async (SeahorseGpgmeKey *pkey,
 1262                                      const gchar *name,
 1263                                      const gchar *email,
 1264                                      const gchar *comment,
 1265                                      GCancellable *cancellable,
 1266                                      GAsyncReadyCallback callback,
 1267                                      gpointer user_data)
 1268 {
 1269     g_autoptr(GTask) task = NULL;
 1270     gpgme_ctx_t gctx;
 1271     gpgme_error_t gerr;
 1272     g_autoptr(GError) error = NULL;
 1273     g_autoptr(GSource) gsource = NULL;
 1274     gpgme_key_t key;
 1275     g_autofree gchar* uid = NULL;
 1276 
 1277     g_return_if_fail (SEAHORSE_GPGME_IS_KEY (pkey));
 1278     g_return_if_fail (seahorse_object_get_usage (SEAHORSE_OBJECT (pkey)) == SEAHORSE_USAGE_PRIVATE_KEY);
 1279 
 1280     gctx = seahorse_gpgme_keyring_new_context (&gerr);
 1281 
 1282     task = g_task_new (pkey, cancellable, callback, user_data);
 1283     gpgme_set_progress_cb (gctx, on_key_op_progress, task);
 1284     g_task_set_task_data (task, gctx, (GDestroyNotify) gpgme_release);
 1285 
 1286     seahorse_progress_prep_and_begin (cancellable, task, NULL);
 1287     gsource = seahorse_gpgme_gsource_new (gctx, cancellable);
 1288     g_source_set_callback (gsource, (GSourceFunc)on_key_op_add_uid_complete,
 1289                            g_object_ref (task), g_object_unref);
 1290 
 1291     key = seahorse_gpgme_key_get_private (pkey);
 1292     uid = seahorse_pgp_uid_calc_label (name, email, comment);
 1293     if (gerr == 0)
 1294         gerr = gpgme_op_adduid_start (gctx, key, uid, 0);
 1295 
 1296     if (seahorse_gpgme_propagate_error (gerr, &error)) {
 1297         g_task_return_error (task, g_steal_pointer (&error));
 1298         return;
 1299     }
 1300 
 1301     g_source_attach (gsource, g_main_context_default ());
 1302 }
 1303 
 1304 gboolean
 1305 seahorse_gpgme_key_op_add_uid_finish (SeahorseGpgmeKey *pkey,
 1306                                       GAsyncResult *result,
 1307                                       GError **error)
 1308 {
 1309     g_return_val_if_fail (g_task_is_valid (result, pkey), FALSE);
 1310 
 1311     return g_task_propagate_boolean (G_TASK (result), error);
 1312 }
 1313 
 1314 typedef enum {
 1315     ADD_KEY_START,
 1316     ADD_KEY_COMMAND,
 1317     ADD_KEY_TYPE,
 1318     ADD_KEY_LENGTH,
 1319     ADD_KEY_EXPIRES,
 1320     ADD_KEY_QUIT,
 1321     ADD_KEY_SAVE,
 1322     ADD_KEY_ERROR
 1323 } AddKeyState;
 1324 
 1325 typedef struct {
 1326     guint               type;
 1327     guint               length;
 1328     time_t              expires;
 1329 } SubkeyParm;
 1330 
 1331 /* action helper for adding a subkey */
 1332 static gpgme_error_t
 1333 add_key_action (guint state, gpointer data, int fd)
 1334 {
 1335     SubkeyParm *parm = (SubkeyParm*)data;
 1336     
 1337     switch (state) {
 1338         case ADD_KEY_COMMAND:
 1339             PRINT ((fd, "addkey"));
 1340             break;
 1341         case ADD_KEY_TYPE:
 1342             PRINTF ((fd, "%d", parm->type));
 1343             break;
 1344         case ADD_KEY_LENGTH:
 1345             PRINTF ((fd, "%d", parm->length));
 1346             break;
 1347         /* Get exact date or 0 */
 1348         case ADD_KEY_EXPIRES:
 1349             PRINT ((fd, (parm->expires) ?
 1350                 seahorse_util_get_date_string (parm->expires) : "0"));
 1351             break;
 1352         case ADD_KEY_QUIT:
 1353             PRINT ((fd, QUIT));
 1354             break;
 1355         case ADD_KEY_SAVE:
 1356             PRINT ((fd, YES));
 1357             break;
 1358         default:
 1359             return GPG_E (GPG_ERR_GENERAL);
 1360     }
 1361     
 1362     PRINT ((fd, "\n"));
 1363     return GPG_OK;
 1364 }
 1365 
 1366 /* transition helper for adding a subkey */
 1367 static guint
 1368 add_key_transit (guint current_state, gpgme_status_code_t status,
 1369          const gchar *args, gpointer data, gpgme_error_t *err)
 1370 {
 1371     guint next_state;
 1372 
 1373     switch (current_state) {
 1374         /* start, do command */
 1375         case ADD_KEY_START:
 1376             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1377                 next_state = ADD_KEY_COMMAND;
 1378             else {
 1379                 *err = GPG_E (GPG_ERR_GENERAL);
 1380                 g_return_val_if_reached (ADD_KEY_ERROR);
 1381             }
 1382             break;
 1383         /* did command, do type */
 1384         case ADD_KEY_COMMAND:
 1385         case ADD_KEY_TYPE:
 1386         case ADD_KEY_LENGTH:
 1387         case ADD_KEY_EXPIRES:
 1388             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "keygen.algo"))
 1389                 next_state = ADD_KEY_TYPE;
 1390             else if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "keygen.size"))
 1391                 next_state = ADD_KEY_LENGTH;
 1392             else if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "keygen.valid"))
 1393                 next_state = ADD_KEY_EXPIRES;
 1394             else if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1395                 next_state = ADD_KEY_QUIT;
 1396             else {
 1397                 *err = GPG_E (GPG_ERR_GENERAL);
 1398                 return ADD_KEY_ERROR; /* Legitimate errors error here */
 1399             }
 1400             break;
 1401         /* quit, save */
 1402         case ADD_KEY_QUIT:
 1403             if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, SAVE))
 1404                 next_state = ADD_KEY_SAVE;
 1405             else {
 1406                 *err = GPG_E (GPG_ERR_GENERAL);
 1407                 g_return_val_if_reached (ADD_KEY_ERROR);
 1408             }
 1409             break;
 1410         /* error, quit */
 1411         case ADD_KEY_ERROR:
 1412             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1413                 next_state = ADD_KEY_QUIT;
 1414             else
 1415                 next_state = ADD_KEY_ERROR;
 1416             break;
 1417         default:
 1418             *err = GPG_E (GPG_ERR_GENERAL);
 1419             g_return_val_if_reached (ADD_KEY_ERROR);
 1420             break;
 1421     }
 1422     
 1423     return next_state;
 1424 }
 1425 
 1426 gpgme_error_t
 1427 seahorse_gpgme_key_op_add_subkey (SeahorseGpgmeKey *pkey, const SeahorseKeyEncType type, 
 1428                                   const guint length, const time_t expires)
 1429 {
 1430     SeahorseEditParm *parms;
 1431     SubkeyParm *key_parm;
 1432     guint real_type;
 1433     SeahorseKeyTypeTable table;
 1434     gpgme_error_t gerr;
 1435     
 1436     g_return_val_if_fail (SEAHORSE_GPGME_IS_KEY (pkey), GPG_E (GPG_ERR_WRONG_KEY_USAGE));    
 1437     g_return_val_if_fail (seahorse_object_get_usage (SEAHORSE_OBJECT (pkey)) == SEAHORSE_USAGE_PRIVATE_KEY, GPG_E (GPG_ERR_WRONG_KEY_USAGE));
 1438     
 1439     gerr = seahorse_gpgme_get_keytype_table (&table);
 1440     g_return_val_if_fail (GPG_IS_OK (gerr), gerr);
 1441     
 1442     /* Check length range & type */
 1443     switch (type) {
 1444         case DSA:
 1445             real_type = table->dsa_sign;
 1446             g_return_val_if_fail (length >= DSA_MIN && length <= DSA_MAX, GPG_E (GPG_ERR_INV_VALUE));
 1447             break;
 1448         case ELGAMAL:
 1449             real_type = table->elgamal_enc;
 1450             g_return_val_if_fail (length >= ELGAMAL_MIN && length <= LENGTH_MAX, GPG_E (GPG_ERR_INV_VALUE));
 1451             break;
 1452         case RSA_SIGN: case RSA_ENCRYPT:
 1453             if (type == RSA_SIGN)
 1454                 real_type = table->rsa_sign;
 1455             else
 1456                 real_type = table->rsa_enc;
 1457             g_return_val_if_fail (length >= RSA_MIN && length <= LENGTH_MAX, GPG_E (GPG_ERR_INV_VALUE));
 1458             break;
 1459         default:
 1460             g_return_val_if_reached (GPG_E (GPG_ERR_INV_VALUE));
 1461             break;
 1462     }
 1463     
 1464     key_parm = g_new (SubkeyParm, 1);
 1465     key_parm->type = real_type;
 1466     key_parm->length = length;
 1467     key_parm->expires = expires;
 1468     
 1469     parms = seahorse_edit_parm_new (ADD_KEY_START, add_key_action, add_key_transit, key_parm);
 1470     
 1471     return edit_key (pkey, parms);
 1472 }
 1473 
 1474 typedef enum {
 1475     DEL_KEY_START,
 1476     DEL_KEY_SELECT,
 1477     DEL_KEY_COMMAND,
 1478     DEL_KEY_CONFIRM,
 1479     DEL_KEY_QUIT,
 1480     DEL_KEY_ERROR
 1481 } DelKeyState;
 1482 
 1483 /* action helper for deleting a subkey */
 1484 static gpgme_error_t
 1485 del_key_action (guint state, gpointer data, int fd)
 1486 {
 1487     switch (state) {
 1488         /* select key */
 1489         case DEL_KEY_SELECT:
 1490             PRINTF ((fd, "key %d", GPOINTER_TO_UINT (data)));
 1491             break;
 1492         case DEL_KEY_COMMAND:
 1493             PRINT ((fd, "delkey"));
 1494             break;
 1495         case DEL_KEY_CONFIRM:
 1496             PRINT ((fd, YES));
 1497             break;
 1498         case DEL_KEY_QUIT:
 1499             PRINT ((fd, QUIT));
 1500             break;
 1501         default:
 1502             return GPG_E (GPG_ERR_GENERAL);
 1503     }
 1504     
 1505     PRINT ((fd, "\n"));
 1506     return GPG_OK;
 1507 }
 1508 
 1509 /* transition helper for deleting a subkey */
 1510 static guint
 1511 del_key_transit (guint current_state, gpgme_status_code_t status,
 1512          const gchar *args, gpointer data, gpgme_error_t *err)
 1513 {
 1514     guint next_state;
 1515 
 1516     switch (current_state) {
 1517         /* start, select key */
 1518         case DEL_KEY_START:
 1519             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1520                 next_state = DEL_KEY_SELECT;
 1521             else {
 1522                 *err = GPG_E (GPG_ERR_GENERAL);
 1523                 g_return_val_if_reached (DEL_KEY_ERROR);
 1524             }
 1525             break;
 1526         /* selected key, do command */
 1527         case DEL_KEY_SELECT:
 1528             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1529                 next_state = DEL_KEY_COMMAND;
 1530             else {
 1531                 *err = GPG_E (GPG_ERR_GENERAL);
 1532                 g_return_val_if_reached (DEL_KEY_ERROR);
 1533             }
 1534             break;
 1535         case DEL_KEY_COMMAND:
 1536             /* did command, confirm */
 1537             if (status == GPGME_STATUS_GET_BOOL && g_str_equal
 1538             (args, "keyedit.remove.subkey.okay"))
 1539                 next_state = DEL_KEY_CONFIRM;
 1540             /* did command, quit */
 1541             else if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1542                 next_state = DEL_KEY_QUIT;
 1543             else {
 1544                 *err = GPG_E (GPG_ERR_GENERAL);
 1545                 g_return_val_if_reached (DEL_KEY_ERROR);
 1546             }
 1547             break;
 1548         /* confirmed, quit */
 1549         case DEL_KEY_CONFIRM:
 1550             next_state = DEL_KEY_QUIT;
 1551             break;
 1552         /* quit, confirm(=save) */
 1553         case DEL_KEY_QUIT:
 1554             if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, SAVE))
 1555                 next_state = DEL_KEY_CONFIRM;
 1556             else {
 1557                 *err = GPG_E (GPG_ERR_GENERAL);
 1558                 g_return_val_if_reached (DEL_KEY_ERROR);
 1559             }
 1560             break;
 1561         /* error, quit */
 1562         case DEL_KEY_ERROR:
 1563             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1564                 next_state = DEL_KEY_QUIT;
 1565             else
 1566                 next_state = DEL_KEY_ERROR;
 1567             break;
 1568         default:
 1569             *err = GPG_E (GPG_ERR_GENERAL);
 1570             g_return_val_if_reached (DEL_KEY_ERROR);
 1571             break;
 1572     }
 1573     
 1574     return next_state;
 1575 }
 1576 
 1577 gpgme_error_t
 1578 seahorse_gpgme_key_op_del_subkey (SeahorseGpgmeSubkey *subkey)
 1579 {
 1580     SeahorseEditParm *parms;
 1581     gpgme_key_t key;
 1582     int index;
 1583     
 1584     g_return_val_if_fail (SEAHORSE_GPGME_IS_SUBKEY (subkey), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
 1585     
 1586     key = seahorse_gpgme_subkey_get_pubkey (subkey);
 1587     g_return_val_if_fail (key, GPG_E (GPG_ERR_INV_VALUE));
 1588     
 1589     index = seahorse_pgp_subkey_get_index (SEAHORSE_PGP_SUBKEY (subkey));
 1590     parms = seahorse_edit_parm_new (DEL_KEY_START, del_key_action,
 1591                                     del_key_transit, GUINT_TO_POINTER (index));
 1592     
 1593     return edit_refresh_gpgme_key (NULL, key, parms);
 1594 }
 1595 
 1596 typedef struct
 1597 {
 1598     guint           index;
 1599     SeahorseRevokeReason    reason;
 1600     const gchar     *description;
 1601 } RevSubkeyParm;
 1602 
 1603 typedef enum {
 1604     REV_SUBKEY_START,
 1605     REV_SUBKEY_SELECT,
 1606     REV_SUBKEY_COMMAND,
 1607     REV_SUBKEY_CONFIRM,
 1608     REV_SUBKEY_REASON,
 1609     REV_SUBKEY_DESCRIPTION,
 1610     REV_SUBKEY_ENDDESC,
 1611     REV_SUBKEY_QUIT,
 1612     REV_SUBKEY_ERROR
 1613 } RevSubkeyState;
 1614 
 1615 /* action helper for revoking a subkey */
 1616 static gpgme_error_t
 1617 rev_subkey_action (guint state, gpointer data, int fd)
 1618 {
 1619     RevSubkeyParm *parm = (RevSubkeyParm*)data;
 1620     
 1621     switch (state) {
 1622         case REV_SUBKEY_SELECT:
 1623             PRINTF ((fd, "key %d", parm->index));
 1624             break;
 1625         case REV_SUBKEY_COMMAND:
 1626             PRINT ((fd, "revkey"));
 1627             break;
 1628         case REV_SUBKEY_CONFIRM:
 1629             PRINT ((fd, YES));
 1630             break;
 1631         case REV_SUBKEY_REASON:
 1632             PRINTF ((fd, "%d", parm->reason));
 1633             break;
 1634         case REV_SUBKEY_DESCRIPTION:
 1635             PRINTF ((fd, "%s", parm->description));
 1636             break;
 1637         case REV_SUBKEY_ENDDESC:
 1638             /* Need empty line, which is written at the end */
 1639             break;
 1640         case REV_SUBKEY_QUIT:
 1641             PRINT ((fd, QUIT));
 1642             break;
 1643         default:
 1644             g_return_val_if_reached (GPG_E (GPG_ERR_GENERAL));
 1645     }
 1646     
 1647     PRINT ((fd, "\n"));
 1648     return GPG_OK;
 1649 }
 1650 
 1651 /* transition helper for revoking a subkey */
 1652 static guint
 1653 rev_subkey_transit (guint current_state, gpgme_status_code_t status,
 1654             const gchar *args, gpointer data, gpgme_error_t *err)
 1655 {
 1656     guint next_state;
 1657 
 1658     switch (current_state) {
 1659         /* start, select key */
 1660         case REV_SUBKEY_START:
 1661             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1662                 next_state = REV_SUBKEY_SELECT;
 1663             else {
 1664                 *err = GPG_E (GPG_ERR_GENERAL);
 1665                 g_return_val_if_reached (REV_SUBKEY_ERROR);
 1666             }
 1667             break;
 1668         /* selected key, do command */
 1669         case REV_SUBKEY_SELECT:
 1670             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1671                 next_state = REV_SUBKEY_COMMAND;
 1672             else {
 1673                 *err = GPG_E (GPG_ERR_GENERAL);
 1674                 g_return_val_if_reached (REV_SUBKEY_ERROR);
 1675             }
 1676             break;
 1677         /* did command, confirm */
 1678         case REV_SUBKEY_COMMAND:
 1679             if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, "keyedit.revoke.subkey.okay"))
 1680                 next_state = REV_SUBKEY_CONFIRM;
 1681             else {
 1682                 *err = GPG_E (GPG_ERR_GENERAL);
 1683                 g_return_val_if_reached (REV_SUBKEY_ERROR);
 1684             }
 1685             break;
 1686         case REV_SUBKEY_CONFIRM:
 1687             /* did confirm, do reason */
 1688             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "ask_revocation_reason.code"))
 1689                 next_state = REV_SUBKEY_REASON;
 1690             /* did confirm, quit */
 1691             else if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1692                 next_state = REV_SUBKEY_QUIT;
 1693             else {
 1694                 *err = GPG_E (GPG_ERR_GENERAL);
 1695                 g_return_val_if_reached (REV_SUBKEY_ERROR);
 1696             }
 1697             break;
 1698         /* did reason, do description */
 1699         case REV_SUBKEY_REASON:
 1700             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "ask_revocation_reason.text"))
 1701                 next_state = REV_SUBKEY_DESCRIPTION;
 1702             else {
 1703                 *err = GPG_E (GPG_ERR_GENERAL);
 1704                 g_return_val_if_reached (REV_SUBKEY_ERROR);
 1705             }
 1706             break;
 1707         case REV_SUBKEY_DESCRIPTION:
 1708             /* did description, end it */
 1709             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "ask_revocation_reason.text"))
 1710                 next_state = REV_SUBKEY_ENDDESC;
 1711             /* did description, confirm */
 1712             else if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, "ask_revocation_reason.okay"))
 1713                 next_state = REV_SUBKEY_CONFIRM;
 1714             else {
 1715                 *err = GPG_E (GPG_ERR_GENERAL);
 1716                 g_return_val_if_reached (REV_SUBKEY_ERROR);
 1717             }
 1718             break;
 1719         /* ended description, confirm */
 1720         case REV_SUBKEY_ENDDESC:
 1721             if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, "ask_revocation_reason.okay"))
 1722                 next_state = REV_SUBKEY_CONFIRM;
 1723             else {
 1724                 *err = GPG_E (GPG_ERR_GENERAL);
 1725                 g_return_val_if_reached (REV_SUBKEY_ERROR);
 1726             }
 1727             break;
 1728         /* quit, confirm(=save) */
 1729         case REV_SUBKEY_QUIT:
 1730             if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, SAVE))
 1731                 next_state = REV_SUBKEY_CONFIRM;
 1732             else {
 1733                 *err = GPG_E (GPG_ERR_GENERAL);
 1734                 g_return_val_if_reached (REV_SUBKEY_ERROR);
 1735             }
 1736             break;
 1737         /* error, quit */
 1738         case REV_SUBKEY_ERROR:
 1739             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1740                 next_state = REV_SUBKEY_QUIT;
 1741             else
 1742                 next_state = REV_SUBKEY_ERROR;
 1743             break;
 1744         default:
 1745             *err = GPG_E (GPG_ERR_GENERAL);
 1746             g_return_val_if_reached (REV_SUBKEY_ERROR);
 1747             break;
 1748     }
 1749     
 1750     return next_state;
 1751 }
 1752 
 1753 gpgme_error_t
 1754 seahorse_gpgme_key_op_revoke_subkey (SeahorseGpgmeSubkey *subkey, SeahorseRevokeReason reason, 
 1755                                      const gchar *description)
 1756 {
 1757     RevSubkeyParm rev_parm;
 1758     SeahorseEditParm *parms;
 1759     gpgme_subkey_t gsubkey;
 1760     gpgme_key_t key;
 1761     
 1762     g_return_val_if_fail (SEAHORSE_GPGME_IS_SUBKEY (subkey), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
 1763     
 1764     gsubkey = seahorse_gpgme_subkey_get_subkey (subkey);
 1765     g_return_val_if_fail (!gsubkey->revoked, GPG_E (GPG_ERR_INV_VALUE));
 1766     
 1767     key = seahorse_gpgme_subkey_get_pubkey (subkey);
 1768     g_return_val_if_fail (key, GPG_E (GPG_ERR_INV_VALUE));
 1769     
 1770     rev_parm.index = seahorse_pgp_subkey_get_index (SEAHORSE_PGP_SUBKEY (subkey));
 1771     rev_parm.reason = reason;
 1772     rev_parm.description = description;
 1773     
 1774     parms = seahorse_edit_parm_new (REV_SUBKEY_START, rev_subkey_action,
 1775                                     rev_subkey_transit, &rev_parm);
 1776     
 1777     return edit_refresh_gpgme_key (NULL, key, parms);
 1778 }
 1779 
 1780 typedef struct {
 1781     guint           index;
 1782 } PrimaryParm;
 1783 
 1784 typedef enum {
 1785     PRIMARY_START,
 1786     PRIMARY_SELECT,
 1787     PRIMARY_COMMAND,
 1788     PRIMARY_QUIT,
 1789     PRIMARY_SAVE,
 1790     PRIMARY_ERROR
 1791 } PrimaryState;
 1792 
 1793 /* action helper for setting primary uid */
 1794 static gpgme_error_t
 1795 primary_action (guint state, gpointer data, int fd)
 1796 {
 1797     PrimaryParm *parm = (PrimaryParm*)data;
 1798     
 1799     switch (state) {
 1800     case PRIMARY_SELECT:
 1801         /* Note that the GPG id is not 0 based */
 1802         PRINTF ((fd, "uid %d", parm->index));
 1803         break;
 1804     case PRIMARY_COMMAND:
 1805         PRINT ((fd, "primary"));
 1806         break;
 1807     case PRIMARY_QUIT:
 1808         PRINT ((fd, QUIT));
 1809         break;
 1810     case PRIMARY_SAVE:
 1811         PRINT ((fd, YES));
 1812         break;
 1813     default:
 1814         g_return_val_if_reached (GPG_E (GPG_ERR_GENERAL));
 1815         break;
 1816     }
 1817   
 1818     PRINT ((fd, "\n"));
 1819     return GPG_OK;
 1820 }
 1821 
 1822 /* transition helper for setting primary key */
 1823 static guint
 1824 primary_transit (guint current_state, gpgme_status_code_t status,
 1825             const gchar *args, gpointer data, gpgme_error_t *err)
 1826 {
 1827     guint next_state;
 1828   
 1829     switch (current_state) {
 1830     
 1831     /* start, select key */
 1832     case PRIMARY_START:
 1833         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1834             next_state = PRIMARY_SELECT;
 1835         else {
 1836             *err = GPG_E (GPG_ERR_GENERAL);
 1837             g_return_val_if_reached (PRIMARY_ERROR);
 1838         }
 1839         break;
 1840 
 1841     /* selected key, do command */
 1842     case PRIMARY_SELECT:
 1843         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1844             next_state = PRIMARY_COMMAND;
 1845         else {
 1846             *err = GPG_E (GPG_ERR_GENERAL);
 1847             g_return_val_if_reached (PRIMARY_ERROR);
 1848         }
 1849         break;
 1850 
 1851     /* did command, quit */
 1852     case PRIMARY_COMMAND:
 1853         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1854             next_state = PRIMARY_QUIT;
 1855         else {
 1856             *err = GPG_E (GPG_ERR_GENERAL);
 1857             g_return_val_if_reached (PRIMARY_ERROR);
 1858         }
 1859         break;
 1860         
 1861     /* quitting so save */
 1862     case PRIMARY_QUIT:        
 1863         if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, SAVE))
 1864             next_state = PRIMARY_SAVE;
 1865         else {
 1866             *err = GPG_E (GPG_ERR_GENERAL);
 1867             g_return_val_if_reached (PRIMARY_ERROR);
 1868         }
 1869         break;
 1870 
 1871     /* error, quit */
 1872     case PRIMARY_ERROR:
 1873         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 1874             next_state = PRIMARY_QUIT;
 1875         else
 1876             next_state = PRIMARY_ERROR;
 1877         break;
 1878         
 1879     default:
 1880         *err = GPG_E (GPG_ERR_GENERAL);
 1881         g_return_val_if_reached (PRIMARY_ERROR);
 1882         break;
 1883     }
 1884     
 1885     return next_state;
 1886 }
 1887 
 1888 static gboolean
 1889 on_key_op_make_primary_complete (gpgme_error_t gerr,
 1890                                  gpointer user_data)
 1891 {
 1892     GTask *task = G_TASK (user_data);
 1893     SeahorseGpgmeUid *uid = SEAHORSE_GPGME_UID (g_task_get_source_object (task));
 1894     SeahorsePgpKey *parent_key = NULL;
 1895     g_autoptr(GError) error = NULL;
 1896 
 1897     if (seahorse_gpgme_propagate_error (gerr, &error)) {
 1898         g_task_return_error (task, g_steal_pointer (&error));
 1899         return FALSE; /* don't call again */
 1900     }
 1901 
 1902     parent_key = seahorse_pgp_uid_get_parent (SEAHORSE_PGP_UID (uid));
 1903     seahorse_gpgme_key_refresh (SEAHORSE_GPGME_KEY (parent_key));
 1904 
 1905     g_task_return_boolean (task, TRUE);
 1906     return FALSE; /* don't call again */
 1907 }
 1908 
 1909 void
 1910 seahorse_gpgme_key_op_make_primary_async (SeahorseGpgmeUid *uid,
 1911                                           GCancellable *cancellable,
 1912                                           GAsyncReadyCallback callback,
 1913                                           gpointer user_data)
 1914 {
 1915     g_autoptr(GTask) task = NULL;
 1916     gpgme_ctx_t gctx;
 1917     gpgme_error_t gerr;
 1918     g_autoptr(GError) error = NULL;
 1919     g_autoptr(GSource) gsource = NULL;
 1920     gpgme_key_t key = NULL;
 1921     gpgme_user_id_t gpg_uid = NULL;
 1922 
 1923     g_return_if_fail (SEAHORSE_GPGME_IS_UID (uid));
 1924 
 1925     gpg_uid = seahorse_gpgme_uid_get_userid (uid);
 1926     g_return_if_fail (!gpg_uid->revoked && !gpg_uid->invalid);
 1927 
 1928     key = seahorse_gpgme_uid_get_pubkey (uid);
 1929     g_return_if_fail (key);
 1930 
 1931     gctx = seahorse_gpgme_keyring_new_context (&gerr);
 1932 
 1933     task = g_task_new (uid, cancellable, callback, user_data);
 1934     gpgme_set_progress_cb (gctx, on_key_op_progress, task);
 1935     g_task_set_task_data (task, gctx, (GDestroyNotify) gpgme_release);
 1936 
 1937     seahorse_progress_prep_and_begin (cancellable, task, NULL);
 1938     gsource = seahorse_gpgme_gsource_new (gctx, cancellable);
 1939     g_source_set_callback (gsource, G_SOURCE_FUNC (on_key_op_make_primary_complete),
 1940                            g_object_ref (task), g_object_unref);
 1941 
 1942     if (gerr == 0)
 1943         gerr = gpgme_op_set_uid_flag_start (gctx, key, gpg_uid->uid, "primary", NULL);
 1944 
 1945     if (seahorse_gpgme_propagate_error (gerr, &error)) {
 1946         g_task_return_error (task, g_steal_pointer (&error));
 1947         return;
 1948     }
 1949 
 1950     g_source_attach (gsource, g_main_context_default ());
 1951 }
 1952 
 1953 gboolean
 1954 seahorse_gpgme_key_op_make_primary_finish (SeahorseGpgmeUid *uid,
 1955                                            GAsyncResult *result,
 1956                                            GError **error)
 1957 {
 1958     g_return_val_if_fail (SEAHORSE_GPGME_IS_UID (uid), FALSE);
 1959     g_return_val_if_fail (g_task_is_valid (result, uid), FALSE);
 1960 
 1961     return g_task_propagate_boolean (G_TASK (result), error);
 1962 }
 1963 
 1964 
 1965 typedef struct {
 1966     guint           index;
 1967 } DelUidParm;
 1968 
 1969 typedef enum {
 1970     DEL_UID_START,
 1971     DEL_UID_SELECT,
 1972     DEL_UID_COMMAND,
 1973     DEL_UID_CONFIRM,
 1974     DEL_UID_QUIT,
 1975     DEL_UID_SAVE,
 1976     DEL_UID_ERROR
 1977 } DelUidState;
 1978 
 1979 /* action helper for removing a uid */
 1980 static gpgme_error_t
 1981 del_uid_action (guint state, gpointer data, int fd)
 1982 {
 1983     DelUidParm *parm = (DelUidParm*)data;
 1984     
 1985     switch (state) {
 1986     case DEL_UID_SELECT:
 1987         PRINTF ((fd, "uid %d", parm->index));
 1988         break;
 1989     case DEL_UID_COMMAND:
 1990         PRINT ((fd, "deluid"));
 1991         break;
 1992     case DEL_UID_CONFIRM:
 1993         PRINT ((fd, YES));
 1994         break;
 1995     case DEL_UID_QUIT:
 1996         PRINT ((fd, QUIT));
 1997         break;
 1998     case DEL_UID_SAVE:
 1999         PRINT ((fd, YES));
 2000         break;
 2001     default:
 2002         g_return_val_if_reached (GPG_E (GPG_ERR_GENERAL));
 2003         break;
 2004     }
 2005   
 2006     PRINT ((fd, "\n"));
 2007     return GPG_OK;
 2008 }
 2009 
 2010 /* transition helper for setting deleting a uid */
 2011 static guint
 2012 del_uid_transit (guint current_state, gpgme_status_code_t status,
 2013             const gchar *args, gpointer data, gpgme_error_t *err)
 2014 {
 2015     guint next_state;
 2016   
 2017     switch (current_state) {
 2018     
 2019     /* start, select key */
 2020     case DEL_UID_START:
 2021         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 2022             next_state = DEL_UID_SELECT;
 2023         else {
 2024             *err = GPG_E (GPG_ERR_GENERAL);
 2025             g_return_val_if_reached (DEL_UID_ERROR);
 2026         }
 2027         break;
 2028 
 2029     /* selected key, do command */
 2030     case DEL_UID_SELECT:
 2031         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 2032             next_state = DEL_UID_COMMAND;
 2033         else {
 2034             *err = GPG_E (GPG_ERR_GENERAL);
 2035             g_return_val_if_reached (DEL_UID_ERROR);
 2036         }
 2037         break;
 2038 
 2039     /* did command, confirm */
 2040     case DEL_UID_COMMAND:
 2041         if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, "keyedit.remove.uid.okay"))
 2042             next_state = DEL_UID_CONFIRM;
 2043         else if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 2044             next_state = DEL_UID_QUIT;
 2045         else {
 2046             *err = GPG_E (GPG_ERR_GENERAL);
 2047             g_return_val_if_reached (REV_SUBKEY_ERROR);
 2048         }
 2049         break;
 2050 
 2051     /* confirmed, quit */
 2052     case DEL_UID_CONFIRM:
 2053         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 2054             next_state = DEL_UID_QUIT;
 2055         else {
 2056             *err = GPG_E (GPG_ERR_GENERAL);
 2057             g_return_val_if_reached (DEL_UID_ERROR);
 2058         }
 2059         break;
 2060                 
 2061     /* quitted so save */
 2062     case DEL_UID_QUIT:        
 2063         if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, SAVE))
 2064             next_state = DEL_UID_SAVE;
 2065         else {
 2066             *err = GPG_E (GPG_ERR_GENERAL);
 2067             g_return_val_if_reached (REV_SUBKEY_ERROR);
 2068         }
 2069         break;
 2070 
 2071     /* error, quit */
 2072     case DEL_UID_ERROR:
 2073         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 2074             next_state = DEL_UID_QUIT;
 2075         else
 2076             next_state = DEL_UID_ERROR;
 2077         break;
 2078         
 2079     default:
 2080         *err = GPG_E (GPG_ERR_GENERAL);
 2081         g_return_val_if_reached (DEL_UID_ERROR);
 2082         break;
 2083     }
 2084     
 2085     return next_state;
 2086 }
 2087                 
 2088 gpgme_error_t   
 2089 seahorse_gpgme_key_op_del_uid (SeahorseGpgmeUid *uid)
 2090 {
 2091     DelUidParm del_uid_parm;
 2092     SeahorseEditParm *parms;
 2093     gpgme_key_t key;
 2094     
 2095     g_return_val_if_fail (SEAHORSE_GPGME_IS_UID (uid), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
 2096     
 2097     key = seahorse_gpgme_uid_get_pubkey (uid);
 2098     g_return_val_if_fail (key, GPG_E (GPG_ERR_INV_VALUE));
 2099   
 2100     del_uid_parm.index = seahorse_gpgme_uid_get_actual_index (uid);
 2101     
 2102     parms = seahorse_edit_parm_new (DEL_UID_START, del_uid_action,
 2103                                     del_uid_transit, &del_uid_parm);
 2104  
 2105     return edit_refresh_gpgme_key (NULL, key, parms);
 2106 }
 2107 
 2108 typedef struct {
 2109     const gchar *filename;
 2110 } PhotoIdAddParm;
 2111 
 2112 typedef enum {
 2113     PHOTO_ID_ADD_START,
 2114     PHOTO_ID_ADD_COMMAND,
 2115     PHOTO_ID_ADD_URI,
 2116     PHOTO_ID_ADD_BIG,
 2117     PHOTO_ID_ADD_QUIT,
 2118     PHOTO_ID_ADD_SAVE,
 2119     PHOTO_ID_ADD_ERROR
 2120 } PhotoIdAddState;
 2121 
 2122 /* action helper for adding a photoid to a #SeahorseKey */
 2123 static gpgme_error_t
 2124 photoid_add_action (guint state, gpointer data, int fd)
 2125 {
 2126     PhotoIdAddParm *parm = (PhotoIdAddParm*)data;
 2127     
 2128     switch (state) {
 2129     case PHOTO_ID_ADD_COMMAND:
 2130         PRINT ((fd, "addphoto"));
 2131         break;
 2132     case PHOTO_ID_ADD_URI:
 2133         PRINT ((fd, parm->filename));
 2134         break;
 2135     case PHOTO_ID_ADD_BIG:
 2136         PRINT ((fd, YES));
 2137         break;
 2138     case PHOTO_ID_ADD_QUIT:
 2139         PRINT ((fd, QUIT));
 2140         break;
 2141     case PHOTO_ID_ADD_SAVE:
 2142         PRINT ((fd, YES));
 2143         break;
 2144     default:
 2145         g_return_val_if_reached (GPG_E (GPG_ERR_GENERAL));
 2146         break;
 2147     }
 2148   
 2149     seahorse_util_print_fd (fd, "\n");
 2150     return GPG_OK;
 2151 }
 2152 
 2153 static guint
 2154 photoid_add_transit (guint current_state, gpgme_status_code_t status,
 2155                      const gchar *args, gpointer data, gpgme_error_t *err)
 2156 {
 2157     guint next_state;
 2158     
 2159     switch (current_state) {
 2160     
 2161     case PHOTO_ID_ADD_START:
 2162         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 2163             next_state = PHOTO_ID_ADD_COMMAND;
 2164         else {
 2165             *err = GPG_E (GPG_ERR_GENERAL);
 2166             g_return_val_if_reached (PHOTO_ID_ADD_ERROR);
 2167         }
 2168         break;
 2169     case PHOTO_ID_ADD_COMMAND:
 2170         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "photoid.jpeg.add")) {
 2171             next_state = PHOTO_ID_ADD_URI;
 2172         } else {
 2173             *err = GPG_E (GPG_ERR_GENERAL);
 2174             g_return_val_if_reached (PHOTO_ID_ADD_ERROR);
 2175         }
 2176         break;
 2177    case PHOTO_ID_ADD_URI:
 2178         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT)) {
 2179             next_state = PHOTO_ID_ADD_QUIT;
 2180         } else if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, "photoid.jpeg.size")) {
 2181             next_state = PHOTO_ID_ADD_BIG;
 2182         } else {
 2183             *err = GPG_E (GPG_ERR_GENERAL);
 2184             g_return_val_if_reached (PHOTO_ID_ADD_ERROR);
 2185         }
 2186         break;
 2187     case PHOTO_ID_ADD_BIG:
 2188         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT)) {
 2189             next_state = PHOTO_ID_ADD_QUIT;
 2190         /* This happens when the file is invalid or can't be accessed */
 2191         } else if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, "photoid.jpeg.add")) {
 2192             *err = GPG_E (GPG_ERR_USER_1);
 2193             return PHOTO_ID_ADD_ERROR;
 2194         } else {
 2195             *err = GPG_E (GPG_ERR_GENERAL);
 2196             g_return_val_if_reached (PHOTO_ID_ADD_ERROR);
 2197         }
 2198         break;
 2199     case PHOTO_ID_ADD_QUIT:
 2200         if (status == GPGME_STATUS_GET_BOOL && g_str_equal (args, SAVE)) {
 2201             next_state = PHOTO_ID_ADD_SAVE;
 2202         } else {
 2203             *err = GPG_E (GPG_ERR_GENERAL);
 2204             g_return_val_if_reached (PHOTO_ID_ADD_ERROR);
 2205         }
 2206         break;
 2207     default:
 2208         *err = GPG_E (GPG_ERR_GENERAL);
 2209         g_return_val_if_reached (PHOTO_ID_ADD_ERROR);
 2210         break;
 2211     }
 2212     
 2213     return next_state;
 2214 }
 2215 
 2216 gpgme_error_t 
 2217 seahorse_gpgme_key_op_photo_add (SeahorseGpgmeKey *pkey, const gchar *filename)
 2218 {
 2219     SeahorseEditParm *parms;
 2220     PhotoIdAddParm photoid_add_parm;
 2221     gpgme_key_t key;
 2222     
 2223     g_return_val_if_fail (SEAHORSE_GPGME_IS_KEY (pkey), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
 2224     g_return_val_if_fail (filename, GPG_E (GPG_ERR_INV_VALUE));
 2225       
 2226     key = seahorse_gpgme_key_get_public (pkey);
 2227     g_return_val_if_fail (key, GPG_E (GPG_ERR_INV_VALUE));
 2228     
 2229     photoid_add_parm.filename = filename;
 2230     
 2231     parms = seahorse_edit_parm_new (PHOTO_ID_ADD_START, photoid_add_action,
 2232                                     photoid_add_transit, &photoid_add_parm);
 2233     
 2234     return edit_refresh_gpgme_key (NULL, key, parms);
 2235 }
 2236 
 2237 gpgme_error_t 
 2238 seahorse_gpgme_key_op_photo_delete (SeahorseGpgmePhoto *photo)
 2239 {
 2240     DelUidParm del_uid_parm;
 2241     SeahorseEditParm *parms;
 2242     gpgme_key_t key;
 2243  
 2244     g_return_val_if_fail (SEAHORSE_IS_GPGME_PHOTO (photo), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
 2245       
 2246     key = seahorse_gpgme_photo_get_pubkey (photo);
 2247     g_return_val_if_fail (key, GPG_E (GPG_ERR_INV_VALUE));
 2248     
 2249     del_uid_parm.index = seahorse_gpgme_photo_get_index (photo);
 2250    
 2251     parms = seahorse_edit_parm_new (DEL_UID_START, del_uid_action,
 2252                                     del_uid_transit, &del_uid_parm);
 2253  
 2254     return edit_refresh_gpgme_key (NULL, key, parms);
 2255 }
 2256 
 2257 typedef struct {
 2258     GList *photos;
 2259     guint uid;
 2260     guint num_uids;
 2261     char *output_file;
 2262     gpgme_key_t key;
 2263 } PhotoIdLoadParm;
 2264 
 2265 typedef enum {
 2266     PHOTO_ID_LOAD_START,
 2267     PHOTO_ID_LOAD_SELECT,
 2268     PHOTO_ID_LOAD_OUTPUT_IMAGE,
 2269     PHOTO_ID_LOAD_DESELECT,
 2270     PHOTO_ID_LOAD_QUIT,
 2271     PHOTO_ID_LOAD_ERROR
 2272 } PhotoIdLoadState;
 2273 
 2274 /* action helper for getting a list of photoids attached to a #SeahorseKey */
 2275 static gpgme_error_t
 2276 photoid_load_action (guint state, gpointer data, int fd)
 2277 {
 2278     PhotoIdLoadParm *parm = (PhotoIdLoadParm*)data;
 2279     
 2280     switch (state) {
 2281         case PHOTO_ID_LOAD_SELECT:
 2282             PRINTF ((fd, "uid %d", parm->uid));            
 2283             break;
 2284         case PHOTO_ID_LOAD_OUTPUT_IMAGE:
 2285             PRINT ((fd, "showphoto"));
 2286             break;
 2287         case PHOTO_ID_LOAD_DESELECT:
 2288             PRINTF ((fd, "uid %d", parm->uid));            
 2289             break;
 2290         case PHOTO_ID_LOAD_QUIT:
 2291             PRINT ((fd, QUIT));
 2292             break;
 2293         default:
 2294             g_return_val_if_reached (GPG_E (GPG_ERR_GENERAL));
 2295             break;
 2296     }
 2297   
 2298     seahorse_util_print_fd (fd, "\n");
 2299     return GPG_OK;
 2300 }
 2301 
 2302 static guint
 2303 photoid_load_transit (guint current_state, gpgme_status_code_t status,
 2304                       const gchar *args, gpointer data, gpgme_error_t *err)
 2305 {
 2306     PhotoIdLoadParm *parm = (PhotoIdLoadParm*)data;
 2307     SeahorseGpgmePhoto *photo;
 2308     GdkPixbuf *pixbuf = NULL;
 2309     guint next_state = 0;
 2310     struct stat st;
 2311     GError *error = NULL;
 2312     
 2313     switch (current_state) {
 2314     
 2315     /* start, get photoid list */
 2316     case PHOTO_ID_LOAD_START:
 2317         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT))
 2318             next_state = PHOTO_ID_LOAD_SELECT;
 2319         else {
 2320             *err = GPG_E (GPG_ERR_GENERAL);
 2321             g_return_val_if_reached (PHOTO_ID_LOAD_ERROR);
 2322         }
 2323         break;
 2324         
 2325     case PHOTO_ID_LOAD_SELECT:
 2326         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT)) {
 2327             next_state = PHOTO_ID_LOAD_OUTPUT_IMAGE;
 2328         } else {
 2329             *err = GPG_E (GPG_ERR_GENERAL);
 2330             g_return_val_if_reached (PHOTO_ID_LOAD_ERROR);
 2331         }
 2332         break;
 2333         
 2334     case PHOTO_ID_LOAD_OUTPUT_IMAGE:
 2335         
 2336         if (g_file_test (parm->output_file, G_FILE_TEST_EXISTS)) {
 2337 
 2338             photo = seahorse_gpgme_photo_new (parm->key, NULL, parm->uid);
 2339             parm->photos = g_list_append (parm->photos, photo);
 2340             
 2341             if (g_stat (parm->output_file, &st) == -1) {
 2342                 g_warning ("couldn't stat output image file '%s': %s", parm->output_file,
 2343                            g_strerror (errno));
 2344                 
 2345             } else if (st.st_size > 0) {
 2346                 pixbuf = gdk_pixbuf_new_from_file (parm->output_file, &error);
 2347                 if (pixbuf == NULL) {
 2348                     g_warning ("Loading image %s failed: %s", parm->output_file,
 2349                                error && error->message ? error->message : "unknown");
 2350                     g_error_free (error);
 2351                 } 
 2352             }
 2353             
 2354             g_unlink (parm->output_file);
 2355             
 2356             /* Load a 'missing' icon */
 2357             if (!pixbuf) {
 2358                 pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), 
 2359                                                    "gnome-unknown", 48, 0, NULL);
 2360             }
 2361                 
 2362             seahorse_pgp_photo_set_pixbuf (SEAHORSE_PGP_PHOTO (photo), pixbuf);
 2363             g_object_unref (pixbuf);
 2364         }
 2365     
 2366         if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT)) {
 2367             next_state = PHOTO_ID_LOAD_DESELECT;
 2368         } else {
 2369             *err = GPG_E (GPG_ERR_GENERAL);
 2370             g_return_val_if_reached (PHOTO_ID_LOAD_ERROR);
 2371         }
 2372         break;
 2373         
 2374     case PHOTO_ID_LOAD_DESELECT:
 2375         if (parm->uid < parm->num_uids) {
 2376             parm->uid = parm->uid + 1;
 2377             g_debug ("PhotoIDLoad Next UID %i", parm->uid);
 2378             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT)) {
 2379                 next_state = PHOTO_ID_LOAD_SELECT;
 2380             } else {
 2381                 *err = GPG_E (GPG_ERR_GENERAL);
 2382                 g_return_val_if_reached (PHOTO_ID_LOAD_ERROR);
 2383             }
 2384         } else {
 2385             if (status == GPGME_STATUS_GET_LINE && g_str_equal (args, PROMPT)) {
 2386                 next_state = PHOTO_ID_LOAD_QUIT;
 2387                 g_debug ("PhotoIDLoad Quiting Load");
 2388             } else {
 2389                 *err = GPG_E (GPG_ERR_GENERAL);
 2390                 g_return_val_if_reached (PHOTO_ID_LOAD_ERROR);
 2391             }
 2392         }
 2393         break;
 2394         
 2395     case PHOTO_ID_LOAD_QUIT:
 2396         /* Shouldn't be reached */
 2397         *err = GPG_E (GPG_ERR_GENERAL);
 2398         g_debug ("PhotoIDLoad Reached Quit");
 2399         g_return_val_if_reached (PHOTO_ID_LOAD_ERROR);
 2400         break;
 2401         
 2402     default:
 2403         *err = GPG_E (GPG_ERR_GENERAL);
 2404         g_return_val_if_reached (PHOTO_ID_LOAD_ERROR);
 2405         break;
 2406     }
 2407     
 2408     return next_state;
 2409 }
 2410 
 2411 gpgme_error_t 
 2412 seahorse_gpgme_key_op_photos_load (SeahorseGpgmeKey *pkey)
 2413 {
 2414     /* Make sure there's enough room for the .jpg extension */
 2415     gchar image_path[]    = "/tmp/seahorse-photoid-XXXXXX\0\0\0\0";
 2416     
 2417     SeahorseEditParm *parms;
 2418     PhotoIdLoadParm photoid_load_parm;
 2419     gpgme_error_t gerr;
 2420     gpgme_key_t key;
 2421     const gchar *oldpath;
 2422     const gchar *keyid;
 2423     gchar *path;
 2424     gint fd;
 2425 
 2426     g_return_val_if_fail (SEAHORSE_GPGME_IS_KEY (pkey), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
 2427     
 2428     key = seahorse_gpgme_key_get_public (pkey);
 2429     g_return_val_if_fail (key, GPG_E (GPG_ERR_INV_VALUE));
 2430     g_return_val_if_fail (key->subkeys && key->subkeys->keyid, GPG_E (GPG_ERR_INV_VALUE));
 2431     keyid = key->subkeys->keyid;
 2432 
 2433     g_debug ("PhotoIDLoad Start");
 2434 
 2435     fd = g_mkstemp (image_path);
 2436     if(fd == -1) { 
 2437         gerr = GPG_E(GPG_ERR_GENERAL);
 2438     
 2439     } else {
 2440 
 2441         g_unlink(image_path);
 2442         close(fd);
 2443         strcat (image_path, ".jpg");
 2444         
 2445         photoid_load_parm.uid = 1;
 2446         photoid_load_parm.num_uids = 0;
 2447         photoid_load_parm.photos = NULL;
 2448         photoid_load_parm.output_file = image_path;
 2449         photoid_load_parm.key = key;
 2450 
 2451         g_debug ("PhotoIdLoad KeyID %s", keyid);
 2452         gerr = seahorse_gpg_op_num_uids (NULL, keyid, &(photoid_load_parm.num_uids));
 2453         g_debug ("PhotoIDLoad Number of UIDs %i", photoid_load_parm.num_uids);
 2454 
 2455         if (GPG_IS_OK (gerr)) {
 2456             
 2457             setenv ("SEAHORSE_IMAGE_FILE", image_path, 1);
 2458             oldpath = getenv("PATH");
 2459             
 2460             path = g_strdup_printf ("%s:%s", EXECDIR, getenv ("PATH"));
 2461             setenv ("PATH", path, 1);
 2462             g_free (path);
 2463             
 2464             parms = seahorse_edit_parm_new (PHOTO_ID_LOAD_START, photoid_load_action,
 2465                                             photoid_load_transit, &photoid_load_parm);
 2466             
 2467             /* generate list */
 2468             gerr = edit_gpgme_key (NULL, key, parms);
 2469             setenv ("PATH", oldpath, 1);
 2470 
 2471             if (GPG_IS_OK (gerr))
 2472                 seahorse_pgp_key_set_photos (SEAHORSE_PGP_KEY (pkey), photoid_load_parm.photos);
 2473         }
 2474         
 2475         seahorse_object_list_free (photoid_load_parm.photos);
 2476     }
 2477 
 2478     g_debug ("PhotoIDLoad Done");
 2479 
 2480     return gerr;
 2481 }
 2482 
 2483 gpgme_error_t   
 2484 seahorse_gpgme_key_op_photo_primary (SeahorseGpgmePhoto *photo)
 2485 {
 2486     PrimaryParm pri_parm;
 2487     SeahorseEditParm *parms;
 2488     gpgme_key_t key;
 2489     
 2490     g_return_val_if_fail (SEAHORSE_IS_GPGME_PHOTO (photo), GPG_E (GPG_ERR_WRONG_KEY_USAGE));
 2491       
 2492     key = seahorse_gpgme_photo_get_pubkey (photo);
 2493     g_return_val_if_fail (key, GPG_E (GPG_ERR_INV_VALUE));
 2494     
 2495     pri_parm.index = seahorse_gpgme_photo_get_index (photo);
 2496    
 2497     parms = seahorse_edit_parm_new (PRIMARY_START, primary_action,
 2498                                     primary_transit, &pri_parm);
 2499 
 2500     return edit_refresh_gpgme_key (NULL, key, parms);
 2501 }