diff --git a/NEWS b/NEWS index f4c39098..55288b4e 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,10 @@ Noteworthy changes in version 1.1.9 * New encryption flag GPGME_ENCRYPT_NO_ENCRYPT_TO to disable default recipients. + * gpgme_new will fail if gpgme_check_version was not called, or a + selftest failed (for example, if -mms-bitfields was not used on + MingW32 targets). + * Interface changes relative to the 1.1.7 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GPGME_KEYLIST_MODE_EPHEMERAL NEW. @@ -16,6 +20,8 @@ Noteworthy changes in version 1.1.9 gpgme_op_assuan_result NEW. gpgme_subkey_t EXTENDED: New fields is_cardkey, card_number. GPGME_ENCRYPT_NO_ENCRYPT_TO NEW. + gpgme_check_version CHANGED: Is now a macro. + gpgme_new EXTENDED: More failure codes. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/ChangeLog b/doc/ChangeLog index e1f36225..e1bb4a59 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,8 @@ +2009-05-28 Marcus Brinkmann + + * gpgme.texi (Library Version Check): Document selftest error. + (Creating Contexts): Likewise. + 2009-05-18 Marcus Brinkmann * gpgme.texi (Encrypting a Plaintext): Document diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 0d5435f9..871575a2 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -577,13 +577,13 @@ automatically by Libtool. @cindex version check, of the library @deftypefun {const char *} gpgme_check_version (@w{const char *@var{required_version}}) -The function @code{gpgme_check_version} has three purposes. It can be +The function @code{gpgme_check_version} has four purposes. It can be used to retrieve the version number of the library. In addition it can verify that the version number is higher than a certain required version number. In either case, the function initializes some sub-systems, and for this reason alone it must be invoked early in your program, before you make use of the other functions in -@acronym{GPGME}. +@acronym{GPGME}. The last purpose is to run selftests. As a side effect for W32 based systems, the socket layer will get initialized. @@ -606,6 +606,11 @@ If you use a version of a library that is backwards compatible with older releases, but contains additional interfaces which your program uses, this function provides a run-time check if the necessary features are provided by the installed version of the library. + +If a selftest fails, the function may still succeed. Selftest errors +are returned later when invoking @code{gpgme_new}, so that a detailed +error code can be returned (historically, @code{gpgme_check_version} +does not return a detailed error code). @end deftypefun @@ -1985,7 +1990,11 @@ and returns a handle for it in @var{ctx}. The function returns the error code @code{GPG_ERR_NO_ERROR} if the context was successfully created, @code{GPG_ERR_INV_VALUE} if @var{ctx} is not a valid pointer, and @code{GPG_ERR_ENOMEM} if not -enough memory is available. +enough memory is available. Also, it returns +@code{GPG_ERR_NOT_OPERATIONAL} if @code{gpgme_check_version} was not +called to initialize GPGME, and @code{GPG_ERR_SELFTEST_FAILED} if a +selftest failed. Currently, the only selftest is for Windows MingW32 +targets to see if @code{-mms-bitfields} was used (as required). @end deftypefun diff --git a/src/ChangeLog b/src/ChangeLog index 434c6802..7ee34395 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,16 @@ +2009-05-28 Marcus Brinkmann + + * gpgme.h.in (gpgme_check_version_internal): New prototype. + (gpgme_check_version): New macro, overriding function of the same + name. + * libgpgme.vers, gpgme.def: Add gpgme_check_version_internal.o + * context.h (_gpgme_selftest): New variable declaration. + * version.c: Include "context.h". + (gpgme_check_version): Set _gpgme_selftest on success. + (gpgme_check_version_internal): New function. + * gpgme.c (_gpgme_selftest): Define it. + (gpgme_new): Check the selftest result. + 2009-05-18 Marcus Brinkmann * gpgme.h.in (gpgme_encrypt_flags_t): Add diff --git a/src/context.h b/src/context.h index 76aeb337..472b8beb 100644 --- a/src/context.h +++ b/src/context.h @@ -28,6 +28,8 @@ #include "sema.h" +extern gpgme_error_t _gpgme_selftest; + /* Operations might require to remember arbitrary information and data objects during invocations of the status handler. The ctx_op_data structure provides a generic framework to hook in diff --git a/src/gpgme.c b/src/gpgme.c index 99d27ce6..b76b3991 100644 --- a/src/gpgme.c +++ b/src/gpgme.c @@ -41,6 +41,9 @@ DEFINE_STATIC_LOCK (def_lc_lock); static char *def_lc_ctype; static char *def_lc_messages; + +gpgme_error_t _gpgme_selftest = GPG_ERR_NOT_OPERATIONAL; + /* Create a new context as an environment for GPGME crypto operations. */ @@ -50,6 +53,9 @@ gpgme_new (gpgme_ctx_t *r_ctx) gpgme_ctx_t ctx; TRACE_BEG (DEBUG_CTX, "gpgme_new", r_ctx); + if (_gpgme_selftest) + return TRACE_ERR (gpgme_error (_gpgme_selftest)); + ctx = calloc (1, sizeof *ctx); if (!ctx) return TRACE_ERR (gpg_error_from_errno (errno)); diff --git a/src/gpgme.def b/src/gpgme.def index 835177ef..14636d64 100644 --- a/src/gpgme.def +++ b/src/gpgme.def @@ -171,5 +171,8 @@ EXPORTS gpgme_op_assuan_result @132 gpgme_op_assuan_transact_start @133 gpgme_op_assuan_transact @134 + + gpgme_check_version_internal @135 + ; END diff --git a/src/gpgme.h.in b/src/gpgme.h.in index fb2b7363..a9a4a3f7 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -1894,9 +1894,21 @@ gpgme_error_t gpgme_op_conf_save (gpgme_ctx_t ctx, gpgme_conf_comp_t comp); /* Various functions. */ -/* Check that the library fulfills the version requirement. */ +/* Check that the library fulfills the version requirement. Note: + This is here only for the case where a user takes a pointer from + the old version of this function. The new version and macro for + run-time checks are below. */ const char *gpgme_check_version (const char *req_version); +/* Check that the library fulfills the version requirement and check + for struct layout mismatch involving bitfields. */ +const char *gpgme_check_version_internal (const char *req_version, + size_t offset_sig_validity); + +#define gpgme_check_version(req_version) \ + gpgme_check_version_internal (req_version, \ + offsetof (struct _gpgme_signature, validity)) + /* Get the information about the configured and installed engines. A pointer to the first engine in the statically allocated linked list is returned in *INFO. If an error occurs, it is returned. The diff --git a/src/libgpgme.vers b/src/libgpgme.vers index 1653a63c..bc6eb7cd 100644 --- a/src/libgpgme.vers +++ b/src/libgpgme.vers @@ -51,6 +51,8 @@ GPGME_1.1 { gpgme_op_assuan_result; gpgme_op_assuan_transact; gpgme_op_assuan_transact_start; + + gpgme_check_version_internal; }; diff --git a/src/version.c b/src/version.c index c6fb52b9..213df6de 100644 --- a/src/version.c +++ b/src/version.c @@ -32,6 +32,7 @@ #include "gpgme.h" #include "priv-io.h" #include "debug.h" +#include "context.h" /* For _gpgme_sema_subsystem_init (). */ #include "sema.h" @@ -44,6 +45,10 @@ #include "windows.h" #endif +/* We implement this function, so we have to disable the overriding + macro. */ +#undef gpgme_check_version + /* Bootstrap the subsystems needed for concurrent operation. This must be done once at startup. We can not guarantee this using a @@ -183,6 +188,7 @@ _gpgme_compare_versions (const char *my_version, const char * gpgme_check_version (const char *req_version) { + char *result; do_subsystem_inits (); /* Catch-22: We need to get at least the debug subsystem ready @@ -193,7 +199,39 @@ gpgme_check_version (const char *req_version) "req_version=%s, VERSION=%s", req_version? req_version:"(null)", VERSION); - return _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL; + result = _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL; + if (result != NULL) + _gpgme_selftest = 0; + + return result; +} + +/* Check the version and also at runtime if the struct layout of the + library matches the one of the user. This is particular useful for + Windows targets (-mms-bitfields). */ +const char * +gpgme_check_version_internal (const char *req_version, + size_t offset_sig_validity) +{ + char *result; + + TRACE2 (DEBUG_INIT, "gpgme_check_version_internal: ", 0, + "req_version=%s, offset_sig_validity=%i", + req_version ? req_version : "(null)", offset_sig_validity); + + result = gpgme_check_version (req_version); + if (result == NULL) + return result; + + if (offset_sig_validity != offsetof (struct _gpgme_signature, validity)) + { + TRACE1 (DEBUG_INIT, "gpgme_check_version_internal: ", 0, + "offset_sig_validity mismatch: expected %i", + offsetof (struct _gpgme_signature, validity)); + _gpgme_selftest = GPG_ERR_SELFTEST_FAILED; + } + + return result; }