/* Use the assuan machinery to read the ATR. */ static void reload_data (GpaCMUnknown *card) { gpg_error_t err, operr; char command[100]; gpgme_ctx_t gpgagent; membuf_t mb; char *buf; gpgagent = GPA_CM_OBJECT (card)->agent_ctx; g_return_if_fail (gpgagent); card->reloading++; init_membuf (&mb, 512); err = gpgme_op_assuan_transact_ext (gpgagent, "SCD APDU --dump-atr", scd_atr_data_cb, &mb, NULL, NULL, NULL, NULL, &operr); if (!err) err = operr; if (!err) { put_membuf (&mb, "", 1); buf = get_membuf (&mb, NULL); if (buf) { buf = g_strdup_printf ("\n%s\n%s", _("The ATR of the card is:"), buf); gtk_label_set_text (GTK_LABEL (card->label), buf); g_free (buf); } else gtk_label_set_text (GTK_LABEL (card->label), ""); } else { g_free (get_membuf (&mb, NULL)); if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) ; /* Lost the card. */ else g_debug ("assuan command `%s' failed: %s <%s>\n", command, gpg_strerror (err), gpg_strsource (err)); gtk_label_set_text (GTK_LABEL (card->label), ""); } card->reloading--; }
/* Use the assuan machinery to load the bulk of the OpenPGP card data. */ static void reload_data (GpaCMDinsig *card) { static struct { const char *name; int entry_id; void (*updfnc) (GpaCMDinsig *card, int entry_id, char *string); } attrtbl[] = { { "SERIALNO", ENTRY_SERIALNO }, { NULL } }; int attridx; gpg_error_t err, operr; char command[100]; struct scd_getattr_parm parm; gpgme_ctx_t gpgagent; gpgagent = GPA_CM_OBJECT (card)->agent_ctx; g_return_if_fail (gpgagent); card->reloading++; parm.card = card; for (attridx=0; attrtbl[attridx].name; attridx++) { parm.name = attrtbl[attridx].name; parm.entry_id = attrtbl[attridx].entry_id; parm.updfnc = attrtbl[attridx].updfnc; snprintf (command, sizeof command, "SCD GETATTR %s", parm.name); err = gpgme_op_assuan_transact_ext (gpgagent, command, NULL, NULL, NULL, NULL, scd_getattr_cb, &parm, &operr); if (!err) err = operr; if (err) { if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) ; /* Lost the card. */ else { g_debug ("assuan command `%s' failed: %s <%s>\n", command, gpg_strerror (err), gpg_strsource (err)); } clear_card_data (card); break; } } card->reloading--; }
/* Fill the app_selection box with the available applications. */ static void setup_app_selector (GpaCardManager *cardman) { gpg_error_t err, operr; membuf_t mb; char *string; char *p, *p0, *p1; if (!cardman->gpgagent || !cardman->app_selector) return; init_membuf (&mb, 256); err = gpgme_op_assuan_transact_ext (cardman->gpgagent, "SCD GETINFO app_list", setup_app_selector_data_cb, &mb, NULL, NULL, NULL, NULL, &operr); if (err || operr) { g_free (get_membuf (&mb, NULL)); return; } /* Make sure the data is a string and get it. */ put_membuf (&mb, "", 1); string = get_membuf (&mb, NULL); if (!string) return; /* Out of core. */ for (p=p0=string; *p; p++) { if (*p == '\n') { *p = 0; p1 = strchr (p0, ':'); if (p1) *p1 = 0; gtk_combo_box_append_text (GTK_COMBO_BOX (cardman->app_selector), p0); if (p[1]) p0 = p+1; } } g_free (string); }
/* This function is called by the timeout ticker started by start_ticker. It is used to poll scdaemon to detect a card status change. */ static gboolean ticker_cb (gpointer user_data) { GpaCardManager *cardman = user_data; if (!cardman || !cardman->ticker_timeout_id || !cardman->gpgagent || cardman->in_card_reload) return TRUE; /* Keep on ticking. */ /* Note that we are single threaded and thus there is no need to lock the assuan context. */ gpgme_op_assuan_transact_ext (cardman->gpgagent, "GETEVENTCOUNTER", NULL, NULL, NULL, NULL, geteventcounter_status_cb, cardman, NULL); return TRUE; /* Keep on ticking. */ }
/* This function is called to triggers a key-generation. */ static void card_genkey (GpaCardManager *cardman) { gpg_error_t err, operr; GpaGenKeyCardOperation *op; char *keyattr; if (cardman->cardtype != GPA_CM_OPENPGP_TYPE) return; /* Not possible. */ if (!cardman->gpgagent) { g_debug ("Ooops: no assuan context"); return; } /* Note: This test works only with GnuPG > 2.0.10 but that version is anyway required for the card manager to work correctly. */ err = gpgme_op_assuan_transact_ext (cardman->gpgagent, "SCD GETINFO deny_admin", NULL, NULL, NULL, NULL, NULL, NULL, &operr); if (!err) err = operr; if (!err) { gpa_window_error ("Admin commands are disabled in scdamon.\n" "Key generation is not possible.", NULL); return; } keyattr = (cardman->card_widget ? gpa_cm_openpgp_get_key_attributes (cardman->card_widget) : NULL); op = gpa_gen_key_card_operation_new (GTK_WIDGET (cardman), keyattr); g_signal_connect_swapped (G_OBJECT (op), "completed", G_CALLBACK (card_genkey_completed), cardman); g_signal_connect (G_OBJECT (op), "completed", G_CALLBACK (g_object_unref), NULL); }
int main (int argc, char **argv) { gpgme_error_t err; gpgme_error_t op_err; gpgme_ctx_t ctx; const char *command; gpgme_check_version (NULL); #ifndef HAVE_W32_SYSTEM setlocale (LC_ALL, ""); gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL)); gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL)); #endif if (argc) { argc--; argv++; } command = argc? *argv : "NOP"; err = gpgme_new (&ctx); fail_if_err (err); err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_ASSUAN); fail_if_err (err); err = gpgme_op_assuan_transact_ext (ctx, command, data_cb, NULL, inq_cb, NULL, status_cb, NULL, &op_err); fail_if_err (err || op_err); gpgme_release (ctx); return 0; }
gpgme_error_t gpgme_op_assuan_transact (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { gpgme_error_t err; TRACE (DEBUG_CTX, "gpgme_op_assuan_transact", ctx); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); /* Users of the old-style session based interfaces need to look at the result structure. */ err = gpgme_op_assuan_transact_ext (ctx, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value, NULL); return err; }
/* This function is called to trigger a card-reload. */ static void card_reload (GpaCardManager *cardman) { gpg_error_t err, operr; const char *application; char *command_buf = NULL; const char *command; const char *err_desc = NULL; char *err_desc_buffer = NULL; int auto_app; if (!cardman->gpgagent) return; /* No support for GPGME_PROTOCOL_ASSUAN. */ /* Start the ticker if not yet done. */ start_ticker (cardman); if (!cardman->in_card_reload) { cardman->in_card_reload++; update_info_visibility (cardman); cardman->cardtype = G_TYPE_NONE; cardman->cardtypename = "Unknown"; /* The first thing we need to do is to issue the SERIALNO command; this makes sure that scdaemon initalizes the card if that has not yet been done. */ command = "SCD SERIALNO"; if (cardman->app_selector && (gtk_combo_box_get_active (GTK_COMBO_BOX (cardman->app_selector)) > 0) && (application = gtk_combo_box_get_active_text (GTK_COMBO_BOX (cardman->app_selector)))) { command_buf = g_strdup_printf ("%s %s", command, application); command = command_buf; auto_app = 0; } else auto_app = 1; err = gpgme_op_assuan_transact_ext (cardman->gpgagent, command, scd_data_cb, NULL, scd_inq_cb, NULL, scd_status_cb, cardman, &operr); if (!err) { err = operr; if (!auto_app && gpg_err_source (err) == GPG_ERR_SOURCE_SCD && gpg_err_code (err) == GPG_ERR_CONFLICT) { /* Not in auto select mode and the scdaemon told us about a conflicting use. We now do a restart and try again to display an application selection conflict error only if it is not due to our own connection to the scdaemon. */ if (!gpgme_op_assuan_transact_ext (cardman->gpgagent, "SCD RESTART", NULL, NULL, NULL, NULL, NULL, NULL, &operr) && !operr) { err = gpgme_op_assuan_transact_ext (cardman->gpgagent, command, scd_data_cb, NULL, scd_inq_cb, NULL, scd_status_cb, cardman, &operr); if (!err) err = operr; } } } if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT || gpg_err_code (err) == GPG_ERR_CARD_REMOVED) { err_desc = _("No card found."); } else if (gpg_err_source (err) == GPG_ERR_SOURCE_SCD && gpg_err_code (err) == GPG_ERR_CONFLICT) { err_desc = auto_app ? _("The selected card application is currently not available.") : _("Another process is using a different card application " "than the selected one.\n\n" "You may change the application selection mode to " "\"Auto\" to select the active application."); } else if (!auto_app && gpg_err_source (err) == GPG_ERR_SOURCE_SCD && gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED) { err_desc = _("The selected card application is not available."); } else if (err) { g_debug ("assuan command `%s' failed: %s <%s>\n", command, gpg_strerror (err), gpg_strsource (err)); if (!gpgme_op_assuan_transact_ext (cardman->gpgagent, "SCD SERIALNO undefined", NULL, NULL, NULL, NULL, NULL, NULL, &operr) && !operr) err = 0; else { err_desc = _("Error accessing the card."); statusbar_update (cardman, _("Error accessing card")); } } g_free (command_buf); if (!err) { /* Get the event counter to avoid a duplicate reload due to the ticker. */ gpgme_op_assuan_transact_ext (cardman->gpgagent, "GETEVENTCOUNTER", NULL, NULL, NULL, NULL, scd_status_cb, cardman, NULL); /* Now we need to get the APPTYPE of the card so that the correct GpaCM* object can can act on the data. */ command = "SCD GETATTR APPTYPE"; err = gpgme_op_assuan_transact_ext (cardman->gpgagent, command, scd_data_cb, NULL, scd_inq_cb, NULL, scd_status_cb, cardman, &operr); if (!err) err = operr; if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT || gpg_err_code (err) == GPG_ERR_CARD_REMOVED) statusbar_update (cardman, _("No card")); else if (err) { g_debug ("assuan command `%s' failed: %s <%s>\n", command, gpg_strerror (err), gpg_strsource (err)); statusbar_update (cardman, _("Error accessing card")); } } update_card_widget (cardman, err_desc); g_free (err_desc_buffer); err_desc_buffer = NULL; err_desc = NULL; update_title (cardman); update_info_visibility (cardman); /* We decrement our lock using a idle handler with lo priority. This gives us a better chance not to do a reload a second time on behalf of the file watcher or ticker. */ g_object_ref (cardman); g_idle_add_full (G_PRIORITY_LOW, card_reload_finish_idle_cb, cardman, NULL); } }