json: Fix uninitialized key unref in op_delete
[gpgme.git] / src / gpgme.c
index 281ba9c..2d829d9 100644 (file)
@@ -1,6 +1,7 @@
 /* gpgme.c - GnuPG Made Easy.
    Copyright (C) 2000 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2012 g10 Code GmbH
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2012,
+                 2014, 2015 g10 Code GmbH
 
    This file is part of GPGME.
 
@@ -15,9 +16,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.  */
+   License along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
 
 #if HAVE_CONFIG_H
 #include <config.h>
@@ -37,6 +37,8 @@
 #include "wait.h"
 #include "debug.h"
 #include "priv-io.h"
+#include "sys-util.h"
+#include "mbox-util.h"
 
 \f
 /* The default locale.  */
@@ -65,6 +67,19 @@ gpgme_set_global_flag (const char *name, const char *value)
     return -1;
   else if (!strcmp (name, "debug"))
     return _gpgme_debug_set_debug_envvar (value);
+  else if (!strcmp (name, "disable-gpgconf"))
+    {
+      _gpgme_dirinfo_disable_gpgconf ();
+      return 0;
+    }
+  else if (!strcmp (name, "require-gnupg"))
+    return _gpgme_set_engine_minimal_version (value);
+  else if (!strcmp (name, "gpgconf-name"))
+    return _gpgme_set_default_gpgconf_name (value);
+  else if (!strcmp (name, "gpg-name"))
+    return _gpgme_set_default_gpg_name (value);
+  else if (!strcmp (name, "w32-inst-dir"))
+    return _gpgme_set_override_inst_dir (value);
   else
     return -1;
 }
@@ -76,11 +91,12 @@ gpgme_set_global_flag (const char *name, const char *value)
 gpgme_error_t
 gpgme_new (gpgme_ctx_t *r_ctx)
 {
+  gpgme_error_t err;
   gpgme_ctx_t ctx;
   TRACE_BEG (DEBUG_CTX, "gpgme_new", r_ctx);
 
   if (_gpgme_selftest)
-    return TRACE_ERR (gpgme_error (_gpgme_selftest));
+    return TRACE_ERR (_gpgme_selftest);
 
   if (!r_ctx)
     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
@@ -91,11 +107,13 @@ gpgme_new (gpgme_ctx_t *r_ctx)
 
   INIT_LOCK (ctx->lock);
 
-  _gpgme_engine_info_copy (&ctx->engine_info);
-  if (!ctx->engine_info)
+  err = _gpgme_engine_info_copy (&ctx->engine_info);
+  if (!err && !ctx->engine_info)
+    err = gpg_error (GPG_ERR_NO_ENGINE);
+  if (err)
     {
       free (ctx);
-      return TRACE_ERR (gpg_error_from_syserror ());
+      return TRACE_ERR (err);
     }
 
   ctx->keylist_mode = GPGME_KEYLIST_MODE_LOCAL;
@@ -220,17 +238,20 @@ gpgme_release (gpgme_ctx_t ctx)
     return;
 
   _gpgme_engine_release (ctx->engine);
+  ctx->engine = NULL;
   _gpgme_fd_table_deinit (&ctx->fdt);
   _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);
+  free (ctx->override_session_key);
+  free (ctx->request_origin);
+  free (ctx->auto_key_locate);
   _gpgme_engine_info_release (ctx->engine_info);
+  ctx->engine_info = NULL;
   DESTROY_LOCK (ctx->lock);
   free (ctx);
 }
@@ -308,7 +329,8 @@ gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
       && protocol != GPGME_PROTOCOL_GPGCONF
       && protocol != GPGME_PROTOCOL_ASSUAN
       && protocol != GPGME_PROTOCOL_G13
-      && protocol != GPGME_PROTOCOL_UISERVER)
+      && protocol != GPGME_PROTOCOL_UISERVER
+      && protocol != GPGME_PROTOCOL_SPAWN)
     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
 
   if (!ctx)
@@ -357,7 +379,7 @@ gpgme_set_sub_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
 }
 
 
-gpgme_error_t
+gpgme_protocol_t
 gpgme_get_sub_protocol (gpgme_ctx_t ctx)
 {
   TRACE2 (DEBUG_CTX, "gpgme_get_sub_protocol", ctx,
@@ -392,6 +414,9 @@ gpgme_get_protocol_name (gpgme_protocol_t protocol)
     case GPGME_PROTOCOL_UISERVER:
       return "UIServer";
 
+    case GPGME_PROTOCOL_SPAWN:
+      return "Spawn";
+
     case GPGME_PROTOCOL_DEFAULT:
       return "default";
 
@@ -403,6 +428,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)
@@ -413,7 +474,7 @@ gpgme_set_armor (gpgme_ctx_t ctx, int use_armor)
   if (!ctx)
     return;
 
-  ctx->use_armor = use_armor;
+  ctx->use_armor = !!use_armor;
 }
 
 
@@ -427,6 +488,134 @@ gpgme_get_armor (gpgme_ctx_t ctx)
 }
 
 
+/* Set the flag NAME for CTX to VALUE.  Please consult the manual for
+ * a description of the flags.
+ */
+gpgme_error_t
+gpgme_set_ctx_flag (gpgme_ctx_t ctx, const char *name, const char *value)
+{
+  gpgme_error_t err = 0;
+  int abool;
+
+  TRACE2 (DEBUG_CTX, "gpgme_set_ctx_flag", ctx,
+          "name='%s' value='%s'",
+         name? name:"(null)", value?value:"(null)");
+
+  abool = (value && *value)? !!atoi (value) : 0;
+
+  if (!ctx || !name || !value)
+    err = gpg_error (GPG_ERR_INV_VALUE);
+  else if (!strcmp (name, "redraw"))
+    {
+      ctx->redraw_suggested = abool;
+    }
+  else if (!strcmp (name, "full-status"))
+    {
+      ctx->full_status = abool;
+    }
+  else if (!strcmp (name, "raw-description"))
+    {
+      ctx->raw_description = abool;
+    }
+  else if (!strcmp (name, "export-session-key"))
+    {
+      ctx->export_session_keys = abool;
+    }
+  else if (!strcmp (name, "override-session-key"))
+    {
+      free (ctx->override_session_key);
+      ctx->override_session_key = strdup (value);
+      if (!ctx->override_session_key)
+        err = gpg_error_from_syserror ();
+    }
+  else if (!strcmp (name, "auto-key-retrieve"))
+    {
+      ctx->auto_key_retrieve = abool;
+    }
+  else if (!strcmp (name, "request-origin"))
+    {
+      free (ctx->request_origin);
+      ctx->request_origin = strdup (value);
+      if (!ctx->request_origin)
+        err = gpg_error_from_syserror ();
+    }
+  else if (!strcmp (name, "no-symkey-cache"))
+    {
+      ctx->no_symkey_cache = abool;
+    }
+  else if (!strcmp (name, "ignore-mdc-error"))
+    {
+      ctx->ignore_mdc_error = abool;
+    }
+  else if (!strcmp (name, "auto-key-locate"))
+    {
+      free (ctx->auto_key_locate);
+      ctx->auto_key_locate = strdup (value);
+      if (!ctx->auto_key_locate)
+        err = gpg_error_from_syserror ();
+    }
+  else
+    err = gpg_error (GPG_ERR_UNKNOWN_NAME);
+
+  return err;
+}
+
+
+/* Get the context flag named NAME.  See gpgme_set_ctx_flag for a list
+ * of valid names.  If the NAME is unknown NULL is returned.  For a
+ * boolean flag an empty string is returned for False and the string
+ * "1" for True; thus either atoi or a simple string test can be
+ * used.  */
+const char *
+gpgme_get_ctx_flag (gpgme_ctx_t ctx, const char *name)
+{
+  if (!ctx || !name)
+    return NULL;
+  else if (!strcmp (name, "redraw"))
+    {
+      return ctx->redraw_suggested? "1":"";
+    }
+  else if (!strcmp (name, "full-status"))
+    {
+      return ctx->full_status? "1":"";
+    }
+  else if (!strcmp (name, "raw-description"))
+    {
+      return ctx->raw_description? "1":"";
+    }
+  else if (!strcmp (name, "export-session-key"))
+    {
+      return ctx->export_session_keys? "1":"";
+    }
+  else if (!strcmp (name, "override-session-key"))
+    {
+      return ctx->override_session_key? ctx->override_session_key : "";
+    }
+  else if (!strcmp (name, "auto-key-retrieve"))
+    {
+      return ctx->auto_key_retrieve? "1":"";
+    }
+  else if (!strcmp (name, "request-origin"))
+    {
+      return ctx->request_origin? ctx->request_origin : "";
+    }
+  else if (!strcmp (name, "no-symkey-cache"))
+    {
+      return ctx->no_symkey_cache? "1":"";
+    }
+  else if (!strcmp (name, "ignore-mdc-error"))
+    {
+      return ctx->ignore_mdc_error? "1":"";
+    }
+  else if (!strcmp (name, "auto-key-locate"))
+    {
+      return ctx->auto_key_locate? ctx->auto_key_locate : "";
+    }
+  else
+    return NULL;
+}
+
+
 /* Enable or disable the use of the special textmode.  Textmode is for
   example used for the RFC2015 signatures; note that the updated RFC
   3156 mandates that the MUA does some preparations so that textmode
@@ -440,7 +629,7 @@ gpgme_set_textmode (gpgme_ctx_t ctx, int use_textmode)
   if (!ctx)
     return;
 
-  ctx->use_textmode = use_textmode;
+  ctx->use_textmode = !!use_textmode;
 }
 
 /* Return the state of the textmode flag.  */
@@ -453,6 +642,30 @@ gpgme_get_textmode (gpgme_ctx_t ctx)
 }
 
 
+/* Enable offline mode for this context. In offline mode dirmngr
+  will be disabled. */
+void
+gpgme_set_offline (gpgme_ctx_t ctx, int offline)
+{
+  TRACE2 (DEBUG_CTX, "gpgme_set_offline", ctx, "offline=%i (%s)",
+          offline, offline ? "yes" : "no");
+
+  if (!ctx)
+    return;
+
+  ctx->offline = !!offline;
+}
+
+/* Return the state of the offline flag.  */
+int
+gpgme_get_offline (gpgme_ctx_t ctx)
+{
+  TRACE2 (DEBUG_CTX, "gpgme_get_offline", ctx, "ctx->offline=%i (%s)",
+          ctx->offline, ctx->offline ? "yes" : "no");
+  return ctx->offline;
+}
+
+
 /* Set the number of certifications to include in an S/MIME message.
    The default is GPGME_INCLUDE_CERTS_DEFAULT.  -1 means all certs,
    and -2 means all certs except the root cert.  */
@@ -514,7 +727,7 @@ gpgme_get_keylist_mode (gpgme_ctx_t ctx)
 
 /* Set the pinentry mode for CTX to MODE. */
 gpgme_error_t
-gpgme_set_pinentry_mode (gpgme_ctx_t ctx, gpgme_keylist_mode_t mode)
+gpgme_set_pinentry_mode (gpgme_ctx_t ctx, gpgme_pinentry_mode_t mode)
 {
   TRACE1 (DEBUG_CTX, "gpgme_set_pinentry_mode", ctx, "pinentry_mode=%u",
          (unsigned int)mode);
@@ -613,6 +826,47 @@ gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t *r_cb,
 }
 
 
+/* This function sets a callback function to be used as a status
+   message forwarder.  */
+void
+gpgme_set_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t cb, void *cb_value)
+{
+  TRACE2 (DEBUG_CTX, "gpgme_set_status_cb", ctx, "status_cb=%p/%p",
+         cb, cb_value);
+
+  if (!ctx)
+    return;
+
+  ctx->status_cb = cb;
+  ctx->status_cb_value = cb_value;
+}
+
+
+/* This function returns the callback function to be used as a
+   status message forwarder.  */
+void
+gpgme_get_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t *r_cb,
+                      void **r_cb_value)
+{
+  TRACE2 (DEBUG_CTX, "gpgme_get_status_cb", ctx, "ctx->status_cb=%p/%p",
+         ctx ? ctx->status_cb : NULL, ctx ? ctx->status_cb_value : NULL);
+
+  if (r_cb)
+    *r_cb = NULL;
+
+  if (r_cb_value)
+    *r_cb_value = NULL;
+
+  if (!ctx || !ctx->status_cb)
+    return;
+
+  if (r_cb)
+    *r_cb = ctx->status_cb;
+  if (r_cb_value)
+    *r_cb_value = ctx->status_cb_value;
+}
+
+
 /* Set the I/O callback functions for CTX to IO_CBS.  */
 void
 gpgme_set_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs)
@@ -643,7 +897,7 @@ gpgme_set_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs)
 
 /* This function provides access to the internal read function; it is
    normally not used.  */
-ssize_t
+gpgme_ssize_t
 gpgme_io_read (int fd, void *buffer, size_t count)
 {
   int ret;
@@ -659,7 +913,7 @@ gpgme_io_read (int fd, void *buffer, size_t count)
 /* This function provides access to the internal write function.  It
    is to be used by user callbacks to return data to gpgme.  See
    gpgme_passphrase_cb_t and gpgme_edit_cb_t.  */
-ssize_t
+gpgme_ssize_t
 gpgme_io_write (int fd, const void *buffer, size_t count)
 {
   int ret;
@@ -678,8 +932,9 @@ gpgme_io_write (int fd, const void *buffer, size_t count)
    written or an error is return.  Returns: 0 on success or -1 on
    error and the sets errno. */
 int
-gpgme_io_writen (int fd, const void *buffer, size_t count)
+gpgme_io_writen (int fd, const void *buffer_arg, size_t count)
 {
+  const char *buffer = buffer_arg;
   int ret = 0;
   TRACE_BEG2 (DEBUG_GLOBAL, "gpgme_io_writen", fd,
              "buffer=%p, count=%u", buffer, count);
@@ -909,38 +1164,70 @@ gpgme_sig_notation_get (gpgme_ctx_t ctx)
   return ctx->sig_notations;
 }
 
+
 \f
-const char *
-gpgme_pubkey_algo_name (gpgme_pubkey_algo_t algo)
+/* Return a public key algorithm string made of the algorithm and size
+   or the curve name.  May return NULL on error.  Caller must free the
+   result using gpgme_free.  */
+char *
+gpgme_pubkey_algo_string (gpgme_subkey_t subkey)
 {
-  switch (algo)
+  const char *prefix = NULL;
+  char *result;
+
+  if (!subkey)
     {
-    case GPGME_PK_RSA:
-      return "RSA";
+      gpg_err_set_errno (EINVAL);
+      return NULL;
+    }
 
+  switch (subkey->pubkey_algo)
+    {
+    case GPGME_PK_RSA:
     case GPGME_PK_RSA_E:
-      return "RSA-E";
-
-    case GPGME_PK_RSA_S:
-      return "RSA-S";
-
-    case GPGME_PK_ELG_E:
-      return "ELG-E";
-
-    case GPGME_PK_DSA:
-      return "DSA";
+    case GPGME_PK_RSA_S: prefix = "rsa"; break;
+    case GPGME_PK_ELG_E: prefix = "elg"; break;
+    case GPGME_PK_DSA:  prefix = "dsa"; break;
+    case GPGME_PK_ELG:   prefix = "xxx"; break;
+    case GPGME_PK_ECC:
+    case GPGME_PK_ECDH:
+    case GPGME_PK_ECDSA:
+    case GPGME_PK_EDDSA: prefix = "";    break;
+    }
 
-    case GPGME_PK_ELG:
-      return "ELG";
+  if (prefix && *prefix)
+    {
+      char buffer[40];
+      snprintf (buffer, sizeof buffer, "%s%u", prefix, subkey->length);
+      result = strdup (buffer);
+    }
+  else if (prefix && subkey->curve && *subkey->curve)
+    result = strdup (subkey->curve);
+  else if (prefix)
+    result =  strdup ("E_error");
+  else
+    result = strdup  ("unknown");
 
-    case GPGME_PK_ECDSA:
-      return "ECDSA";
+  return result;
+}
 
-    case GPGME_PK_ECDH:
-      return "ECDH";
 
-    default:
-      return NULL;
+const char *
+gpgme_pubkey_algo_name (gpgme_pubkey_algo_t algo)
+{
+  switch (algo)
+    {
+    case GPGME_PK_RSA:   return "RSA";
+    case GPGME_PK_RSA_E: return "RSA-E";
+    case GPGME_PK_RSA_S: return "RSA-S";
+    case GPGME_PK_ELG_E: return "ELG-E";
+    case GPGME_PK_DSA:   return "DSA";
+    case GPGME_PK_ECC:   return "ECC";
+    case GPGME_PK_ELG:   return "ELG";
+    case GPGME_PK_ECDSA: return "ECDSA";
+    case GPGME_PK_ECDH:  return "ECDH";
+    case GPGME_PK_EDDSA: return "EdDSA";
+    default:             return NULL;
     }
 }
 
@@ -977,6 +1264,9 @@ gpgme_hash_algo_name (gpgme_hash_algo_t algo)
     case GPGME_MD_SHA512:
       return "SHA512";
 
+    case GPGME_MD_SHA224:
+      return "SHA224";
+
     case GPGME_MD_MD4:
       return "MD4";