diff --git a/NEWS b/NEWS index 409181c7..8aa40413 100644 --- a/NEWS +++ b/NEWS @@ -73,6 +73,11 @@ Noteworthy changes in version 0.4.1 (unreleased) printf ("%s\n", result->fpr); } + * The new gpgme_op_import_result function provides detailed + information about the result of an import operation in + GpgmeImportResult and GpgmeImportStatus objects. Thus, the + gpgme_op_import_ext variant is deprecated. + * Interface changes relative to the 0.4.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GpgmeIOCb CHANGED: Return type from void to GpgmeError. @@ -99,6 +104,10 @@ GPGME_Bad_Passphrase NEW gpgme_op_genkey CHANGED: FPR argument dropped. gpgme_op_genkey_result NEW GpgmeGenKeyResult NEW +gpgme_op_import_ext DEPRECATED: Use gpgme_op_import_result. +gpgme_op_import_result NEW +GpgmeImportStatus NEW +GPgmeImportResult NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.4.0 (2002-12-23) diff --git a/doc/ChangeLog b/doc/ChangeLog index 9886a0b7..5f320008 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,6 +1,9 @@ 2003-04-25 Marcus Brinkmann - * gpgme.texi (Generating Keys): Fix documentation of ppublic and + * gpgme.texi (Importing Keys): Add documentation for + GpgmeImportStatus, GpgmeImportResult and gpgme_op_import_result. + + * gpgme.texi (Generating Keys): Fix documentation of public and secret arguments. 2003-04-24 Marcus Brinkmann diff --git a/doc/gpgme.texi b/doc/gpgme.texi index c318f97e..ff519299 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -2221,6 +2221,7 @@ successfully. The returned pointer is only valid until the next operation is started on the context. @end deftypefun + @node Exporting Keys @subsection Exporting Keys @cindex key, export @@ -2282,11 +2283,125 @@ started successfully, @code{GPGME_Invalid_Value} if @var{keydata} if @code{GPGME_No_Data} if @var{keydata} is an empty data buffer. @end deftypefun +@deftp {Data type} {GpgmeImportStatus} +This is a pointer to a structure used to store a part of the result of +a @code{gpgme_op_genkey} operation. For each considered key one +status is added that contains information about the result of the +import. The structure contains the following members: + +@table @code +@item GpgmeImportStatus next +This is a pointer to the next status object in the list. + +@item char *fpr +This is the fingerprint of the key that was considered. + +@item GpgmeError result +If the import was not successful, this is the error value that caused +the import to fail. Otherwise it is @code{GPGME_No_Error}. + +@item unsigned int status +This is a bit-wise OR of the following flags that give more +information about what part of the key was imported. If the key was +already known, this might be 0. + +@table @code +@item GPGME_IMPORT_NEW +The key was new. + +@item GPGME_IMPORT_UID +The key contained new user IDs. + +@item GPGME_IMPORT_SIG +The key contained new signatures. + +@item GPGME_IMPORT_SUBKEY +The key contained new sub keys. + +@item GPGME_IMPORT_PRIVATE +The key contained a private key. +@end table +@end table +@end deftp + +@deftp {Data type} {GpgmeImportResult} +This is a pointer to a structure used to store the result of a +@code{gpgme_op_genkey} operation. After a successful import +operation, you can retrieve the pointer to the result with +@code{gpgme_op_import_result}. The structure contains the following +members: + +@table @code +@item int considered +The total number of considered keys. + +@item int no_user_id +The number of keys without user ID. + +@item int imported +The total number of imported keys. + +@item imported_rsa +The number of imported RSA keys. + +@item unchanged +The number of unchanged keys. + +@item new_user_ids +The number of new user IDs. + +@item new_sub_keys +The number of new sub keys. + +@item new_signatures +The number of new signatures. + +@item new_revocations +The number of new revocations. + +@item secret_read +The total number of secret keys read. + +@item secret_imported +The number of imported secret keys. + +@item secret_unchanged +The number of unchanged secret keys. + +@item not_imported +The number of keys not imported. + +@item GpgmeImportStatus imports +A list of GpgmeImportStatus objects which contain more information +about the keys for which an import was attempted. +@end table +@end deftp + +@deftypefun GpgmeImportResult gpgme_op_import_result (@w{GpgmeCtx @var{ctx}}) +The function @code{gpgme_op_import_result} returns a +@code{GpgmeImportResult} pointer to a structure holding the result of +a @code{gpgme_op_import} operation. The pointer is only valid if the +last operation on the context was a @code{gpgme_op_import} or +@code{gpgme_op_import_start} operation, and if this operation finished +successfully. The returned pointer is only valid until the next +operation is started on the context. +@end deftypefun + +The following interface is deprecated and only provided for backward +compatibility. Don't use it. It will be removed in a future version +of @acronym{GPGME}. + @deftypefun GpgmeError gpgme_op_import_ext (@w{GpgmeCtx @var{ctx}}, @w{GpgmeData @var{keydata}}, @w{int *@var{nr}}) -The function @code{gpgme_op_import_ext} is like -@code{gpgme_op_import}, but also returns the number of processed keys -in @var{nr}. This is the same as the @code{count} information in the -detailed results available with @code{gpgme_get_op_info}. +The function @code{gpgme_op_import_ext} is equivalent to: + +@example + GpgmeError err = gpgme_op_import (ctx, keydata); + if (!err) + @{ + GpgmeImportResult result = gpgme_op_import_result (ctx); + *nr = result->considered; + @} +@end example @end deftypefun diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index 4b118d5a..1202ea1b 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,5 +1,31 @@ 2003-04-25 Marcus Brinkmann + * gpgme.h: New enum for GPGME_IMPORT_NEW, GPGME_IMPORT_UID, + GPGME_IMPORT_SIG, GPGME_IMPORT_SUBKEY, GPGME_IMPORT_PRIVATE. + (GpgmeError): GPGME_Unknown_Reason, GPGME_Not_Found, + GPGME_Ambiguous_Specification, GPGME_Wrong_Key_Usage, + GPGME_Key_Revoked, GPGME_Key_Expired, GPGME_No_CRL_Known, + GPGME_CRL_Too_Old, GPGME_Policy_Mismatch, GPGME_No_Secret_Key, + GPGME_Key_Not_Trusted, GPGME_Issuer_Missing, GPGME_Chain_Too_Long, + GPGME_Unsupported_Algorithm, GPGME_Sig_Expired, + GPGME_Bad_Signature, GPGME_No_Public_Key): New error codes. + (struct _gpgme_import_status): New structure. + (GpgmeImportStatus): New type. + (struct _gpgme_op_import_result): New structure. + (GpgmeImportResult): New type. + (gpgme_op_import_result): New function. + * import.c: Include and "gpgme.h", but not "util.h". + (struct import_result): Change to type op_data_t. + (release_import_result): Rename to ... + (release_op_data): ... this. + (append_xml_impinfo): Function removed. + (gpgme_op_import_result): New function. + (parse_import): New function. + (parse_import_res): Likewise. + (import_status_handler): Change first argument to void *. Rewrite + to use new functions. + (_gpgme_op_import_start): Rework error handling. + * edit.c: Do not include , "util.h", but "gpgme.h". (edit_resut): Change to typedef for op_data_t. (edit_status_handler): Change first argument to void *. diff --git a/gpgme/gpgme.h b/gpgme/gpgme.h index f7c8b54a..416ae90b 100644 --- a/gpgme/gpgme.h +++ b/gpgme/gpgme.h @@ -96,8 +96,33 @@ typedef enum GPGME_Invalid_Engine = 0x0013, GPGME_No_UserID = 0x0014, GPGME_Invalid_UserID = 0x0015, + + /* Reasons for invalid user id. */ + GPGME_Unknown_Reason = 0x0100, + GPGME_Not_Found = 0x0101, + GPGME_Ambiguous_Specification = 0x0102, + GPGME_Wrong_Key_Usage = 0x0103, + GPGME_Key_Revoked = 0x0104, + GPGME_Key_Expired = 0x0105, + GPGME_No_CRL_Known = 0x0106, + GPGME_CRL_Too_Old = 0x0107, + GPGME_Policy_Mismatch = 0x0108, + GPGME_No_Secret_Key = 0x0109, + GPGME_Key_Not_Trusted = 0x010a, + + /* Import problems. */ + GPGME_Issuer_Missing = 0x0200, + GPGME_Chain_Too_Long = 0x0201, + + /* Verification problems. */ + GPGME_Unsupported_Algorithm = 0x0300, + GPGME_Sig_Expired = 0x0301, + GPGME_Bad_Signature = 0x0302, + GPGME_No_Public_Key = 0x0303, + + /* Deprecated. */ GPGME_Busy = -2, - GPGME_No_Request = -3, + GPGME_No_Request = -3 } GpgmeError; @@ -750,11 +775,99 @@ GpgmeError gpgme_op_verify_start (GpgmeCtx ctx, GpgmeData sig, GpgmeError gpgme_op_verify (GpgmeCtx ctx, GpgmeData sig, GpgmeData signed_text, GpgmeData plaintext); + +enum + { + /* The key was new. */ + GPGME_IMPORT_NEW = 1, + + /* The key contained new user IDs. */ + GPGME_IMPORT_UID = 2, + + /* The key contained new signatures. */ + GPGME_IMPORT_SIG = 4, + + /* The key contained new sub keys. */ + GPGME_IMPORT_SUBKEY = 8, + + /* The key contained a private key. */ + GPGME_IMPORT_PRIVATE = 16 + }; + +struct _gpgme_import_status +{ + struct _gpgme_import_status *next; + + /* Fingerprint. */ + char *fpr; + + /* If a problem occured, the reason why the key could not be + imported. Otherwise GPGME_No_Error. */ + GpgmeError result; + + /* The result of the import, the GPGME_IMPORT_* values bit-wise + ORed. 0 means the key was already known and no new components + have been added. */ + unsigned int status; +}; +typedef struct _gpgme_import_status *GpgmeImportStatus; + +/* Import. */ +struct _gpgme_op_import_result +{ + /* Number of considered keys. */ + int considered; + + /* Keys without user ID. */ + int no_user_id; + + /* Imported keys. */ + int imported; + + /* Imported RSA keys. */ + int imported_rsa; + + /* Unchanged keys. */ + int unchanged; + + /* Number of new user ids. */ + int new_user_ids; + + /* Number of new sub keys. */ + int new_sub_keys; + + /* Number of new signatures. */ + int new_signatures; + + /* Number of new revocations. */ + int new_revocations; + + /* Number of secret keys read. */ + int secret_read; + + /* Number of secret keys imported. */ + int secret_imported; + + /* Number of secret keys unchanged. */ + int secret_unchanged; + + /* Number of keys not imported. */ + int not_imported; + + /* List of keys for which an import was attempted. */ + GpgmeImportStatus imports; +}; +typedef struct _gpgme_op_import_result *GpgmeImportResult; + +/* Retrieve a pointer to the result of the import operation. */ +GpgmeImportResult gpgme_op_import_result (GpgmeCtx ctx); + /* Import the key in KEYDATA into the keyring. */ GpgmeError gpgme_op_import_start (GpgmeCtx ctx, GpgmeData keydata); GpgmeError gpgme_op_import (GpgmeCtx ctx, GpgmeData keydata); GpgmeError gpgme_op_import_ext (GpgmeCtx ctx, GpgmeData keydata, int *nr); + /* Export the keys listed in RECP into KEYDATA. */ GpgmeError gpgme_op_export_start (GpgmeCtx ctx, GpgmeRecipients recp, GpgmeData keydata); diff --git a/gpgme/import.c b/gpgme/import.c index 3c050b19..c9fff5b5 100644 --- a/gpgme/import.c +++ b/gpgme/import.c @@ -1,4 +1,4 @@ -/* import.c - Import functions. +/* import.c - Import a key. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003 g10 Code GmbH @@ -22,155 +22,183 @@ #include #endif #include +#include #include -#include "util.h" +#include "gpgme.h" #include "context.h" #include "ops.h" -struct import_result +typedef struct { - int nr_imported; - int nr_considered; - GpgmeData xmlinfo; -}; -typedef struct import_result *ImportResult; + struct _gpgme_op_import_result result; + + /* A pointer to the next pointer of the last import status in the + list. This makes appending new imports painless while preserving + the order. */ + GpgmeImportStatus *lastp; +} *op_data_t; + static void -release_import_result (void *hook) +release_op_data (void *hook) { - ImportResult result = (ImportResult) hook; + op_data_t opd = (op_data_t) hook; + GpgmeImportStatus import = opd->result.imports; - if (result->xmlinfo) - gpgme_data_release (result->xmlinfo); + while (import) + { + GpgmeImportStatus next = import->next; + free (import->fpr); + free (import); + import = next; + } } -/* Parse the args and append the information to the XML structure in - the data buffer. With args of NULL the xml structure is - closed. */ -static void -append_xml_impinfo (GpgmeData *rdh, GpgmeStatusCode code, char *args) +GpgmeImportResult +gpgme_op_import_result (GpgmeCtx ctx) { -#define MAX_IMPORTED_FIELDS 14 - static const char *const imported_fields[MAX_IMPORTED_FIELDS] - = { "keyid", "username", 0 }; - static const char *const imported_fields_x509[MAX_IMPORTED_FIELDS] - = { "fpr", 0 }; - static const char *const import_res_fields[MAX_IMPORTED_FIELDS] - = { "count", "no_user_id", "imported", "imported_rsa", - "unchanged", "n_uids", "n_subk", "n_sigs", "s_sigsn_revoc", - "sec_read", "sec_imported", "sec_dups", "skipped_new", 0 }; - const char *field[MAX_IMPORTED_FIELDS]; - const char *const *field_name = 0; - GpgmeData dh; - int i; + op_data_t opd; + GpgmeError err; - /* Verify that we can use the args. */ - if (code != GPGME_STATUS_EOF) + err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, (void **) &opd, -1, NULL); + if (err || !opd) + return NULL; + + return &opd->result; +} + + +static GpgmeError +parse_import (char *args, GpgmeImportStatus *import_status, int problem) +{ + GpgmeImportStatus import; + char *tail; + long int nr; + + import = malloc (sizeof (*import)); + if (!import) + return GPGME_Out_Of_Core; + import->next = NULL; + + errno = 0; + nr = strtol (args, &tail, 0); + if (errno || args == tail || *tail != ' ') { - if (!args) - return; + /* The crypto backend does not behave. */ + free (import); + return GPGME_General_Error; + } + args = tail; - if (code == GPGME_STATUS_IMPORTED) - field_name = imported_fields; - else if (code == GPGME_STATUS_IMPORT_RES) - field_name = import_res_fields; - else - return; - - for (i = 0; field_name[i]; i++) + if (problem) + { + switch (nr) { - field[i] = args; - if (field_name[i + 1]) - { - args = strchr (args, ' '); - if (!args) - return; /* Invalid line. */ - *args++ = '\0'; - } - } - - /* gpgsm does not print a useful user ID and uses a fingerprint - instead of the key ID. */ - if (code == GPGME_STATUS_IMPORTED && field[0] && strlen (field[0]) > 16) - field_name = imported_fields_x509; - } + case 0: + case 4: + default: + import->result = GPGME_Unknown_Reason; + break; - /* Initialize the data buffer if necessary. */ - if (!*rdh) - { - if (gpgme_data_new (rdh)) - return; /* FIXME: We are ignoring out-of-core. */ - dh = *rdh; - _gpgme_data_append_string (dh, "\n"); - } - else - dh = *rdh; - - if (code == GPGME_STATUS_EOF) - { - /* Just close the XML containter. */ - _gpgme_data_append_string (dh, "\n"); + case 1: + import->result = GPGME_Invalid_Key; + break; + + case 2: + import->result = GPGME_Issuer_Missing; + break; + + case 3: + import->result = GPGME_Chain_Too_Long; + break; + } + import->status = 0; } else { - if (code == GPGME_STATUS_IMPORTED) - _gpgme_data_append_string (dh, " \n"); - else if (code == GPGME_STATUS_IMPORT_RES) - _gpgme_data_append_string (dh, " \n"); - - for (i = 0; field_name[i]; i++) - { - _gpgme_data_append_string (dh, " <"); - _gpgme_data_append_string (dh, field_name[i]); - _gpgme_data_append_string (dh, ">"); - _gpgme_data_append_string_for_xml (dh, field[i]); - _gpgme_data_append_string (dh, "\n"); - } - - if (code == GPGME_STATUS_IMPORTED) - _gpgme_data_append_string (dh, " \n"); - else if (code == GPGME_STATUS_IMPORT_RES) - _gpgme_data_append_string (dh, " \n"); + import->result = GPGME_No_Error; + import->status = nr; } + + while (*args == ' ') + args++; + tail = strchr (args, ' '); + if (tail) + *tail = '\0'; + + import->fpr = strdup (args); + if (!import->fpr) + { + free (import); + return GPGME_Out_Of_Core; + } + + *import_status = import; + return 0; +} + + + +GpgmeError +parse_import_res (char *args, GpgmeImportResult result) +{ + char *tail; + + errno = 0; + +#define PARSE_NEXT(x) \ + (x) = strtol (args, &tail, 0); \ + if (errno || args == tail || *tail != ' ') \ + /* The crypto backend does not behave. */ \ + return GPGME_General_Error; \ + args = tail; + + PARSE_NEXT (result->considered); + PARSE_NEXT (result->no_user_id); + PARSE_NEXT (result->imported); + PARSE_NEXT (result->imported_rsa); + PARSE_NEXT (result->new_user_ids); + PARSE_NEXT (result->new_sub_keys); + PARSE_NEXT (result->new_signatures); + PARSE_NEXT (result->new_revocations); + PARSE_NEXT (result->secret_read); + PARSE_NEXT (result->secret_imported); + PARSE_NEXT (result->secret_unchanged); + PARSE_NEXT (result->not_imported); + + return 0; } static GpgmeError -import_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args) +import_status_handler (void *priv, GpgmeStatusCode code, char *args) { + GpgmeCtx ctx = (GpgmeCtx) priv; GpgmeError err; - ImportResult result; + op_data_t opd; - err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, (void **) &result, - sizeof (*result), release_import_result); + err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, (void **) &opd, + -1, NULL); if (err) return err; switch (code) { - case GPGME_STATUS_EOF: - if (result->xmlinfo) - { - append_xml_impinfo (&result->xmlinfo, code, NULL); - _gpgme_set_op_info (ctx, result->xmlinfo); - result->xmlinfo = NULL; - } - /* XXX Calculate error value. */ - break; + case GPGME_STATUS_IMPORT_OK: + case GPGME_STATUS_IMPORT_PROBLEM: + err = parse_import (args, opd->lastp, + code == GPGME_STATUS_IMPORT_OK ? 0 : 1); + if (err) + return err; - case GPGME_STATUS_IMPORTED: - result->nr_imported++; - append_xml_impinfo (&result->xmlinfo, code, args); + opd->lastp = &(*opd->lastp)->next; break; case GPGME_STATUS_IMPORT_RES: - result->nr_considered = strtol (args, 0, 0); - append_xml_impinfo (&result->xmlinfo, code, args); + err = parse_import_res (args, &opd->result); break; default: @@ -183,30 +211,25 @@ import_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args) static GpgmeError _gpgme_op_import_start (GpgmeCtx ctx, int synchronous, GpgmeData keydata) { - int err = 0; + GpgmeError err; + op_data_t opd; err = _gpgme_op_reset (ctx, synchronous); if (err) - goto leave; + return err; + + err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, (void **) &opd, + sizeof (*opd), release_op_data); + if (err) + return err; + opd->lastp = &opd->result.imports; - /* Check the supplied data */ if (!keydata) - { - err = GPGME_No_Data; - goto leave; - } + return GPGME_No_Data; _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx); - err = _gpgme_engine_op_import (ctx->engine, keydata); - - leave: - if (err) - { - _gpgme_engine_release (ctx->engine); - ctx->engine = NULL; - } - return err; + return _gpgme_engine_op_import (ctx->engine, keydata); } @@ -216,38 +239,26 @@ gpgme_op_import_start (GpgmeCtx ctx, GpgmeData keydata) return _gpgme_op_import_start (ctx, 0, keydata); } -/** - * gpgme_op_import: - * @c: Context - * @keydata: Data object - * @nr: Will contain number of considered keys. - * - * Import all key material from @keydata into the key database. - * - * Return value: 0 on success or an error code. - **/ + +/* Import the key in KEYDATA into the keyring. */ GpgmeError -gpgme_op_import_ext (GpgmeCtx ctx, GpgmeData keydata, int *nr) +gpgme_op_import (GpgmeCtx ctx, GpgmeData keydata) { GpgmeError err = _gpgme_op_import_start (ctx, 1, keydata); if (!err) err = _gpgme_wait_one (ctx); - if (!err && nr) - { - ImportResult result; - - err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, (void **) &result, - -1, NULL); - if (result) - *nr = result->nr_considered; - else - *nr = 0; - } return err; } + GpgmeError -gpgme_op_import (GpgmeCtx ctx, GpgmeData keydata) +gpgme_op_import_ext (GpgmeCtx ctx, GpgmeData keydata, int *nr) { - return gpgme_op_import_ext (ctx, keydata, 0); + GpgmeError err = gpgme_op_import (ctx, keydata); + if (!err && nr) + { + GpgmeImportResult result = gpgme_op_import_result (ctx); + *nr = result->considered; + } + return err; }