core: New API functions gpgme_set_sender, gpgme_get_sender.
authorWerner Koch <wk@gnupg.org>
Tue, 25 Oct 2016 15:27:49 +0000 (17:27 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 25 Oct 2016 15:28:01 +0000 (17:28 +0200)
* src/context.h (struct gpgme_context): Add field 'sender'.
* src/gpgme.c: Include mbox-util.h.
(gpgme_release): Free SENDER.
(gpgme_set_sender): New.
(gpgme_get_sender): New.
* src/gpgme.def, src/libgpgme.vers: Add new functions.

* src/engine-gpg.c (append_args_from_sender): New.
(gpg_encrypt_sign, gpg_sign): Call append_args_from_sender.
(gpg_verify): Add arg CTX.  Call append_args_from_sender/
* src/engine-gpgsm.c (gpgsm_verify): Add dummy arg CTX.
* src/engine-uiserver.c (uiserver_verify): Ditto.
* src/engine.c (_gpgme_engine_op_verify): Add arg CTX.
* src/verify.c (verify_start): Pass CTX to engine function.

* tests/gpg/t-verify.c (main): Add some checks for new functions.
* tests/run-sign.c (main): Add option --sender.
* tests/run-verify.c (main): Ditto.

Signed-off-by: Werner Koch <wk@gnupg.org>
17 files changed:
NEWS
doc/gpgme.texi
src/context.h
src/engine-backend.h
src/engine-gpg.c
src/engine-gpgsm.c
src/engine-uiserver.c
src/engine.c
src/engine.h
src/gpgme.c
src/gpgme.def
src/gpgme.h.in
src/libgpgme.vers
src/verify.c
tests/gpg/t-verify.c
tests/run-sign.c
tests/run-verify.c

diff --git a/NEWS b/NEWS
index f5d7ce0..38f38d6 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,12 @@ Noteworthy changes in version 1.7.2 (unreleased)
 ------------------------------------------------
 
 
+ * Interface changes relative to the 1.7.1 release:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ gpgme_set_sender                NEW.
+ gpgme_get_sender                NEW.
+
+
 Noteworthy changes in version 1.7.1 (2016-10-18)
 ------------------------------------------------
 
index cc59888..9fae9aa 100644 (file)
@@ -186,6 +186,7 @@ Context Attributes
 
 * Protocol Selection::            Selecting the protocol used by a context.
 * Crypto Engine::                 Configuring the crypto engine.
+* Setting the Sender::            How to tell the engine the sender.
 * ASCII Armor::                   Requesting @acronym{ASCII} armored output.
 * Text Mode::                     Choosing canonical text mode.
 * Offline Mode::                  Choosing offline mode.
@@ -2366,6 +2367,7 @@ started.  In fact, these references are accessed through the
 @menu
 * Protocol Selection::            Selecting the protocol used by a context.
 * Crypto Engine::                 Configuring the crypto engine.
+* Setting the Sender::            How to tell the engine the sender.
 * ASCII Armor::                   Requesting @acronym{ASCII} armored output.
 * Text Mode::                     Choosing canonical text mode.
 * Offline Mode::                  Choosing offline mode.
@@ -2448,6 +2450,47 @@ successful, or an eror code on failure.
 @end deftypefun
 
 
+@node Setting the Sender
+@subsection How to tell the engine the sender.
+@cindex context, sender
+@cindex sender
+@cindex From:
+
+Some engines can make use of the sender’s address, for example to
+figure out the best user id in certain trust models.  For verification
+and signing of mails, it is thus suggested to let the engine know the
+sender ("From:") address.  @acronym{GPGME} provides two functions to
+accomplish that.  Note that the esoteric use of multiple "From:"
+addresses is not supported.
+
+@deftypefun gpgme_error_t gpgme_set_sender @
+      (@w{gpgme_ctx_t @var{ctx}}, @
+       @w{int @var{address}})
+
+The function @code{gpgme_set_sender} specifies the sender address for
+use in sign and verify operations.  @var{address} is expected to be
+the ``addr-spec'' part of an address but my also be a complete mailbox
+address, in which case this function extracts the ``addr-spec'' from
+it.  Using @code{NULL} for @var{address} clears the sender address.
+
+The function returns 0 on success or an error code on failure.  The
+most likely failure is that no valid ``addr-spec'' was found in
+@var{address}.
+
+@end deftypefun
+
+@deftypefun @w{const char *} gpgme_get_sender @
+      (@w{gpgme_ctx_t @var{ctx}})
+
+The function @code{gpgme_get_sender} returns the current sender
+address from the context, or NULL if none was set.  The returned
+value is valid as long as the @var{ctx} is valid and
+@code{gpgme_set_sender} has not been called again.
+
+@end deftypefun
+
+
+
 @c FIXME: Unfortunately, using @acronym here breaks texi2dvi.
 @node ASCII Armor
 @subsection @acronym{ASCII} Armor
index 4b12c3b..f6c1ad1 100644 (file)
@@ -119,16 +119,18 @@ struct gpgme_context
   /* Number of certs to be included.  */
   unsigned int include_certs;
 
-  /* The number of keys in signers.  */
+  /* The actual number of keys in SIGNERS, the allocated size of the
+   * array, and the array with the signing keys.  */
   unsigned int signers_len;
-
-  /* Size of the following array.  */
   unsigned int signers_size;
   gpgme_key_t *signers;
 
   /* The signature notations for this context.  */
   gpgme_sig_notation_t sig_notations;
 
+  /* The sender's addr-spec or NULL.  */
+  char *sender;
+
   /* The locale for the pinentry.  */
   char *lc_ctype;
   char *lc_messages;
index ccab0e3..e02c715 100644 (file)
@@ -111,7 +111,8 @@ struct engine_ops
                         gpgme_ctx_t ctx /* FIXME */);
   gpgme_error_t (*trustlist) (void *engine, const char *pattern);
   gpgme_error_t (*verify) (void *engine, gpgme_data_t sig,
-                          gpgme_data_t signed_text, gpgme_data_t plaintext);
+                          gpgme_data_t signed_text, gpgme_data_t plaintext,
+                           gpgme_ctx_t ctx);
   gpgme_error_t  (*getauditlog) (void *engine, gpgme_data_t output,
                                  unsigned int flags);
   gpgme_error_t  (*opassuan_transact) (void *engine,
index 4415c94..cb52dea 100644 (file)
@@ -1646,6 +1646,23 @@ append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
 
 
 static gpgme_error_t
+append_args_from_sender (engine_gpg_t gpg, gpgme_ctx_t ctx)
+{
+  gpgme_error_t err;
+
+  if (ctx->sender && have_gpg_version (gpg, "2.1.15"))
+    {
+      err = add_arg (gpg, "--sender");
+      if (!err)
+        err = add_arg (gpg, ctx->sender);
+    }
+  else
+    err = 0;
+  return err;
+}
+
+
+static gpgme_error_t
 append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
 {
   gpgme_error_t err = 0;
@@ -1893,6 +1910,9 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
     err = append_args_from_signers (gpg, ctx);
 
   if (!err)
+    err = append_args_from_sender (gpg, ctx);
+
+  if (!err)
     err = append_args_from_sig_notations (gpg, ctx);
 
   /* Tell the gpg object about the data.  */
@@ -2794,6 +2814,8 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
   if (!err)
     err = append_args_from_signers (gpg, ctx);
   if (!err)
+    err = append_args_from_sender (gpg, ctx);
+  if (!err)
     err = append_args_from_sig_notations (gpg, ctx);
 
   if (gpgme_data_get_file_name (in))
@@ -2845,12 +2867,15 @@ gpg_trustlist (void *engine, const char *pattern)
 
 static gpgme_error_t
 gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
-           gpgme_data_t plaintext)
+           gpgme_data_t plaintext, gpgme_ctx_t ctx)
 {
   engine_gpg_t gpg = engine;
-  gpgme_error_t err = 0;
+  gpgme_error_t err;
 
-  if (plaintext)
+  err = append_args_from_sender (gpg, ctx);
+  if (err)
+    ;
+  else if (plaintext)
     {
       /* Normal or cleartext signature.  */
       err = add_arg (gpg, "--output");
index e7e2a20..0ce4a6d 100644 (file)
@@ -1901,11 +1901,13 @@ gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
 
 static gpgme_error_t
 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
-             gpgme_data_t plaintext)
+             gpgme_data_t plaintext, gpgme_ctx_t ctx)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
 
+  (void)ctx;
+
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
 
index 63e77de..76fa4d7 100644 (file)
@@ -1243,13 +1243,16 @@ uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
 /* FIXME: Missing a way to specify --silent.  */
 static gpgme_error_t
 uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
-             gpgme_data_t plaintext)
+                 gpgme_data_t plaintext, gpgme_ctx_t ctx)
 {
   engine_uiserver_t uiserver = engine;
   gpgme_error_t err;
   const char *protocol;
   char *cmd;
 
+  (void)ctx; /* FIXME: We should to add a --sender option to the
+              * UISever protocol.  */
+
   if (!uiserver)
     return gpg_error (GPG_ERR_INV_VALUE);
   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
@@ -1395,6 +1398,6 @@ struct engine_ops _gpgme_engine_ops_uiserver =
     uiserver_cancel,
     NULL,              /* cancel_op */
     NULL,               /* passwd */
-    NULL,                /* set_pinentry_mode */
+    NULL,               /* set_pinentry_mode */
     NULL                /* opspawn */
   };
index a1173a0..f5dfe51 100644 (file)
@@ -902,7 +902,8 @@ _gpgme_engine_op_trustlist (engine_t engine, const char *pattern)
 
 gpgme_error_t
 _gpgme_engine_op_verify (engine_t engine, gpgme_data_t sig,
-                        gpgme_data_t signed_text, gpgme_data_t plaintext)
+                        gpgme_data_t signed_text, gpgme_data_t plaintext,
+                         gpgme_ctx_t ctx)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -910,7 +911,8 @@ _gpgme_engine_op_verify (engine_t engine, gpgme_data_t sig,
   if (!engine->ops->verify)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
-  return (*engine->ops->verify) (engine->engine, sig, signed_text, plaintext);
+  return (*engine->ops->verify) (engine->engine, sig, signed_text, plaintext,
+                                 ctx);
 }
 
 
index 4ce2bed..2999ab6 100644 (file)
@@ -152,7 +152,8 @@ gpgme_error_t _gpgme_engine_op_trustlist (engine_t engine,
                                          const char *pattern);
 gpgme_error_t _gpgme_engine_op_verify (engine_t engine, gpgme_data_t sig,
                                       gpgme_data_t signed_text,
-                                      gpgme_data_t plaintext);
+                                      gpgme_data_t plaintext,
+                                       gpgme_ctx_t ctx);
 
 gpgme_error_t _gpgme_engine_op_getauditlog (engine_t engine,
                                             gpgme_data_t output,
index d59f808..6d0dbff 100644 (file)
@@ -38,6 +38,7 @@
 #include "debug.h"
 #include "priv-io.h"
 #include "sys-util.h"
+#include "mbox-util.h"
 
 \f
 /* The default locale.  */
@@ -275,12 +276,10 @@ gpgme_release (gpgme_ctx_t ctx)
   _gpgme_release_result (ctx);
   _gpgme_signers_clear (ctx);
   _gpgme_sig_notation_clear (ctx);
-  if (ctx->signers)
-    free (ctx->signers);
-  if (ctx->lc_ctype)
-    free (ctx->lc_ctype);
-  if (ctx->lc_messages)
-    free (ctx->lc_messages);
+  free (ctx->sender);
+  free (ctx->signers);
+  free (ctx->lc_ctype);
+  free (ctx->lc_messages);
   _gpgme_engine_info_release (ctx->engine_info);
   ctx->engine_info = NULL;
   DESTROY_LOCK (ctx->lock);
@@ -459,6 +458,42 @@ gpgme_get_protocol_name (gpgme_protocol_t protocol)
     }
 }
 
+
+/* Store the sender's address in the context.  ADDRESS is addr-spec of
+ * mailbox but my also be a complete mailbox, in which case this
+ * function extracts the addr-spec from it.  Returns 0 on success or
+ * an error code if no valid addr-spec could be extracted from
+ * ADDRESS.  */
+gpgme_error_t
+gpgme_set_sender (gpgme_ctx_t ctx, const char *address)
+{
+  char *p = NULL;
+
+  TRACE_BEG1 (DEBUG_CTX, "gpgme_set_sender", ctx, "sender='%s'",
+              address?address:"(null)");
+
+  if (!ctx || (address && !(p = _gpgme_mailbox_from_userid (address))))
+    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+  free (ctx->sender);
+  ctx->sender = p;
+  return TRACE_ERR (0);
+}
+
+
+/* Return the sender's address (addr-spec part) from the context or
+ * NULL if none was set.  The returned value is valid as long as the
+ * CTX is valid and gpgme_set_sender has not been used.  */
+const char *
+gpgme_get_sender (gpgme_ctx_t ctx)
+{
+  TRACE1 (DEBUG_CTX, "gpgme_get_sender", ctx, "sender='%s'",
+          ctx?ctx->sender:"");
+
+  return ctx->sender;
+}
+
+
 /* Enable or disable the use of an ascii armor for all output.  */
 void
 gpgme_set_armor (gpgme_ctx_t ctx, int use_armor)
index c94c960..d633df5 100644 (file)
@@ -246,5 +246,8 @@ EXPORTS
 
     gpgme_addrspec_from_uid               @186
 
+    gpgme_set_sender                      @187
+    gpgme_get_sender                      @188
+
 ; END
 
index 5c914ae..94ef51d 100644 (file)
@@ -1161,6 +1161,12 @@ gpgme_error_t gpgme_sig_notation_add (gpgme_ctx_t ctx, const char *name,
 /* Get the sig notations for this context.  */
 gpgme_sig_notation_t gpgme_sig_notation_get (gpgme_ctx_t ctx);
 
+/* Store a sender address in the context.  */
+gpgme_error_t gpgme_set_sender (gpgme_ctx_t ctx, const char *address);
+
+/* Get the sender address from the context.  */
+const char *gpgme_get_sender (gpgme_ctx_t ctx);
+
 
 \f
 /*
index d3962db..42f00d5 100644 (file)
@@ -119,6 +119,9 @@ GPGME_1.1 {
     gpgme_op_interact;
 
     gpgme_addrspec_from_uid;
+
+    gpgme_set_sender;
+    gpgme_get_sender;
 };
 
 
index eb1cc10..faa8deb 100644 (file)
@@ -1104,7 +1104,8 @@ verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig,
   if (!sig)
     return gpg_error (GPG_ERR_NO_DATA);
 
-  return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
+  return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext,
+                                  ctx);
 }
 
 
index 9842d3a..f955cc9 100644 (file)
@@ -209,6 +209,7 @@ main (int argc, char *argv[])
   gpgme_error_t err;
   gpgme_data_t sig, text;
   gpgme_verify_result_t result;
+  const char *s;
 
   (void)argc;
   (void)argv;
@@ -270,6 +271,54 @@ main (int argc, char *argv[])
       exit (1);
     }
 
+  /* Checking that set/get_sernder works.  */
+  err = gpgme_set_sender (ctx, "foo@example.org");
+  fail_if_err (err);
+  s = gpgme_get_sender (ctx);
+  if (!s || strcmp (s, "foo@example.org"))
+    {
+      fprintf (stderr, "%s:%i: gpgme_{set,get}_sender mismatch\n",
+               __FILE__, __LINE__);
+      exit (1);
+    }
+
+  err = gpgme_set_sender (ctx, "<bar@example.org>");
+  fail_if_err (err);
+  s = gpgme_get_sender (ctx);
+  if (!s || strcmp (s, "bar@example.org"))
+    {
+      fprintf (stderr, "%s:%i: gpgme_{set,get}_sender mismatch\n",
+               __FILE__, __LINE__);
+      exit (1);
+    }
+
+  err = gpgme_set_sender (ctx, "Foo bar (comment) <foo@example.org>");
+  fail_if_err (err);
+  s = gpgme_get_sender (ctx);
+  if (!s || strcmp (s, "foo@example.org"))
+    {
+      fprintf (stderr, "%s:%i: gpgme_{set,get}_sender mismatch\n",
+               __FILE__, __LINE__);
+      exit (1);
+    }
+
+  err = gpgme_set_sender (ctx, "foo");
+  if (gpgme_err_code (err) != GPG_ERR_INV_VALUE)
+    {
+      fprintf (stderr, "%s:%i: gpgme_set_sender didn't detect bogus address\n",
+               __FILE__, __LINE__);
+      exit (1);
+    }
+  /* (the former address should still be there.)  */
+  s = gpgme_get_sender (ctx);
+  if (!s || strcmp (s, "foo@example.org"))
+    {
+      fprintf (stderr, "%s:%i: gpgme_{set,get}_sender mismatch\n",
+               __FILE__, __LINE__);
+      exit (1);
+    }
+
+
   gpgme_data_release (sig);
   gpgme_data_release (text);
   gpgme_release (ctx);
index 70853ed..f790cb6 100644 (file)
@@ -83,6 +83,7 @@ show_usage (int ex)
          "  --uiserver       use the UI server\n"
          "  --loopback       use a loopback pinentry\n"
          "  --key NAME       use key NAME for signing\n"
+         "  --sender MBOX    use MBOX as sender address\n"
          , stderr);
   exit (ex);
 }
@@ -101,6 +102,7 @@ main (int argc, char **argv)
   gpgme_sign_result_t result;
   int print_status = 0;
   int use_loopback = 0;
+  const char *sender = NULL;
 
   if (argc)
     { argc--; argv++; }
@@ -148,6 +150,14 @@ main (int argc, char **argv)
           key_string = *argv;
           argc--; argv++;
         }
+      else if (!strcmp (*argv, "--sender"))
+        {
+          argc--; argv++;
+          if (!argc)
+            show_usage (1);
+          sender = *argv;
+          argc--; argv++;
+        }
       else if (!strcmp (*argv, "--loopback"))
         {
           use_loopback = 1;
@@ -192,6 +202,12 @@ main (int argc, char **argv)
       gpgme_key_unref (akey);
     }
 
+  if (sender)
+    {
+      err = gpgme_set_sender (ctx, sender);
+      fail_if_err (err);
+    }
+
   err = gpgme_data_new_from_file (&in, *argv, 1);
   if (err)
     {
index ebc20d9..22242c0 100644 (file)
@@ -221,6 +221,7 @@ show_usage (int ex)
          "  --status         print status lines from the backend\n"
          "  --openpgp        use the OpenPGP protocol (default)\n"
          "  --cms            use the CMS protocol\n"
+         "  --sender MBOX    use MBOX as sender address\n"
          , stderr);
   exit (ex);
 }
@@ -239,6 +240,7 @@ main (int argc, char **argv)
   gpgme_data_t msg = NULL;
   gpgme_verify_result_t result;
   int print_status = 0;
+  const char *sender = NULL;
 
   if (argc)
     { argc--; argv++; }
@@ -273,6 +275,14 @@ main (int argc, char **argv)
           protocol = GPGME_PROTOCOL_CMS;
           argc--; argv++;
         }
+      else if (!strcmp (*argv, "--sender"))
+        {
+          argc--; argv++;
+          if (!argc)
+            show_usage (1);
+          sender = *argv;
+          argc--; argv++;
+        }
       else if (!strncmp (*argv, "--", 2))
         show_usage (1);
 
@@ -313,6 +323,12 @@ main (int argc, char **argv)
     }
   /* gpgme_set_ctx_flag (ctx, "raw-description", "1"); */
 
+  if (sender)
+    {
+      err = gpgme_set_sender (ctx, sender);
+      fail_if_err (err);
+    }
+
   err = gpgme_data_new_from_stream (&sig, fp_sig);
   if (err)
     {