gpgconf: Add access to --list-dirs for non-default engine.
[gpgme.git] / src / engine.c
index 09f379c..2c7e625 100644 (file)
@@ -15,7 +15,7 @@
    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, see <http://www.gnu.org/licenses/>.
+   License along with this program; if not, see <https://www.gnu.org/licenses/>.
 */
 
 #ifdef HAVE_CONFIG_H
@@ -46,31 +46,16 @@ struct engine
 static struct engine_ops *engine_ops[] =
   {
     &_gpgme_engine_ops_gpg,            /* OpenPGP.  */
-#ifdef ENABLE_GPGSM
     &_gpgme_engine_ops_gpgsm,          /* CMS.  */
-#else
-    NULL,
-#endif
-#ifdef ENABLE_GPGCONF
     &_gpgme_engine_ops_gpgconf,                /* gpg-conf.  */
-#else
-    NULL,
-#endif
-#ifdef ENABLE_ASSUAN
     &_gpgme_engine_ops_assuan,         /* Low-Level Assuan.  */
-#else
-    NULL,
-#endif
-#ifdef ENABLE_G13
     &_gpgme_engine_ops_g13,            /* Crypto VFS.  */
-#else
-    NULL,
-#endif
 #ifdef ENABLE_UISERVER
-    &_gpgme_engine_ops_uiserver                /* UI-Server.  */
+    &_gpgme_engine_ops_uiserver,       /* UI-Server.  */
 #else
-    NULL
+    NULL,
 #endif
+    &_gpgme_engine_ops_spawn
   };
 
 
@@ -78,6 +63,10 @@ static struct engine_ops *engine_ops[] =
 static gpgme_engine_info_t engine_info;
 DEFINE_STATIC_LOCK (engine_info_lock);
 
+/* If non-NULL, the minimal version required for all engines.  */
+static char *engine_minimal_version;
+
+
 \f
 /* Get the file name of the engine for PROTOCOL.  */
 static const char *
@@ -108,7 +97,8 @@ engine_get_home_dir (gpgme_protocol_t proto)
 
 
 /* Get a malloced string containing the version number of the engine
-   for PROTOCOL.  */
+ * for PROTOCOL.  If this function returns NULL for a valid protocol,
+ * it should be assumed that the engine is a pseudo engine. */
 static char *
 engine_get_version (gpgme_protocol_t proto, const char *file_name)
 {
@@ -122,7 +112,8 @@ engine_get_version (gpgme_protocol_t proto, const char *file_name)
 }
 
 
-/* Get the required version number of the engine for PROTOCOL.  */
+/* Get the required version number of the engine for PROTOCOL.  This
+ * may be NULL. */
 static const char *
 engine_get_req_version (gpgme_protocol_t proto)
 {
@@ -179,8 +170,8 @@ _gpgme_engine_info_release (gpgme_engine_info_t info)
     {
       gpgme_engine_info_t next_info = info->next;
 
-      assert (info->file_name);
-      free (info->file_name);
+      if (info->file_name)
+        free (info->file_name);
       if (info->home_dir)
        free (info->home_dir);
       if (info->version)
@@ -191,6 +182,26 @@ _gpgme_engine_info_release (gpgme_engine_info_t info)
 }
 
 
+/* This is an internal function to set a mimimal required version.
+ * This function must only be called by gpgme_set_global_flag.
+ * Returns 0 on success.  */
+int
+_gpgme_set_engine_minimal_version (const char *value)
+{
+  free (engine_minimal_version);
+  if (value)
+    {
+      engine_minimal_version = strdup (value);
+      return !engine_minimal_version;
+    }
+  else
+    {
+      engine_minimal_version = NULL;
+      return 0;
+    }
+}
+
+
 /* 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
@@ -198,6 +209,8 @@ _gpgme_engine_info_release (gpgme_engine_info_t info)
 gpgme_error_t
 gpgme_get_engine_info (gpgme_engine_info_t *info)
 {
+  gpgme_error_t err;
+
   LOCK (engine_info_lock);
   if (!engine_info)
     {
@@ -207,13 +220,16 @@ gpgme_get_engine_info (gpgme_engine_info_t *info)
                                        GPGME_PROTOCOL_GPGCONF,
                                        GPGME_PROTOCOL_ASSUAN,
                                        GPGME_PROTOCOL_G13,
-                                       GPGME_PROTOCOL_UISERVER };
+                                       GPGME_PROTOCOL_UISERVER,
+                                        GPGME_PROTOCOL_SPAWN    };
       unsigned int proto;
 
+      err = 0;
       for (proto = 0; proto < DIM (proto_list); proto++)
        {
          const char *ofile_name = engine_get_file_name (proto_list[proto]);
          const char *ohome_dir  = engine_get_home_dir (proto_list[proto]);
+          char *version = engine_get_version (proto_list[proto], NULL);
          char *file_name;
          char *home_dir;
 
@@ -221,13 +237,43 @@ gpgme_get_engine_info (gpgme_engine_info_t *info)
            continue;
 
          file_name = strdup (ofile_name);
-          home_dir = ohome_dir? strdup (ohome_dir): NULL;
+          if (!file_name)
+            err = gpg_error_from_syserror ();
 
-         *lastp = malloc (sizeof (*engine_info));
-         if (!*lastp || !file_name)
-           {
-             int saved_err = gpg_error_from_syserror ();
+          if (ohome_dir)
+            {
+              home_dir = strdup (ohome_dir);
+              if (!home_dir && !err)
+                err = gpg_error_from_syserror ();
+            }
+          else
+            home_dir = NULL;
+
+         *lastp = calloc (1, sizeof (*engine_info));
+          if (!*lastp && !err)
+            err = gpg_error_from_syserror ();
+
+          /* Check against the optional minimal engine version.  */
+          if (!err && version && engine_minimal_version
+              && !_gpgme_compare_versions (version, engine_minimal_version))
+            {
+#if GPG_ERROR_VERSION_NUMBER < 0x011900 /* 1.25 */
+              err = gpg_error (GPG_ERR_NO_ENGINE);
+#else
+              err = gpg_error (GPG_ERR_ENGINE_TOO_OLD);
+#endif
+            }
 
+          /* Now set the dummy version for pseudo engines.  */
+          if (!err && !version)
+            {
+              version = strdup ("1.0.0");
+              if (!version)
+                err = gpg_error_from_syserror ();
+            }
+
+         if (err)
+           {
              _gpgme_engine_info_release (engine_info);
              engine_info = NULL;
 
@@ -235,16 +281,20 @@ gpgme_get_engine_info (gpgme_engine_info_t *info)
                free (file_name);
              if (home_dir)
                free (home_dir);
+             if (version)
+               free (version);
 
              UNLOCK (engine_info_lock);
-             return saved_err;
+             return err;
            }
 
          (*lastp)->protocol = proto_list[proto];
          (*lastp)->file_name = file_name;
          (*lastp)->home_dir = home_dir;
-         (*lastp)->version = engine_get_version (proto_list[proto], NULL);
+         (*lastp)->version = version;
          (*lastp)->req_version = engine_get_req_version (proto_list[proto]);
+         if (!(*lastp)->req_version)
+            (*lastp)->req_version = "1.0.0"; /* Dummy for pseudo engines. */
          (*lastp)->next = NULL;
          lastp = &(*lastp)->next;
        }
@@ -289,11 +339,13 @@ _gpgme_engine_info_copy (gpgme_engine_info_t *r_info)
 
       assert (info->file_name);
       file_name = strdup (info->file_name);
+      if (!file_name)
+        err = gpg_error_from_syserror ();
 
       if (info->home_dir)
        {
          home_dir = strdup (info->home_dir);
-         if (!home_dir)
+         if (!home_dir && !err)
            err = gpg_error_from_syserror ();
        }
       else
@@ -302,19 +354,19 @@ _gpgme_engine_info_copy (gpgme_engine_info_t *r_info)
       if (info->version)
        {
          version = strdup (info->version);
-         if (!version)
+         if (!version && !err)
            err = gpg_error_from_syserror ();
        }
       else
        version = NULL;
 
       *lastp = malloc (sizeof (*engine_info));
-      if (!*lastp || !file_name || err)
-       {
-         int saved_err = gpg_error_from_syserror ();
+      if (!*lastp && !err)
+        err = gpg_error_from_syserror ();
 
+      if (err)
+       {
          _gpgme_engine_info_release (new_info);
-
          if (file_name)
            free (file_name);
          if (home_dir)
@@ -323,7 +375,7 @@ _gpgme_engine_info_copy (gpgme_engine_info_t *r_info)
            free (version);
 
          UNLOCK (engine_info_lock);
-         return saved_err;
+         return err;
        }
 
       (*lastp)->protocol = info->protocol;
@@ -351,6 +403,7 @@ _gpgme_set_engine_info (gpgme_engine_info_t info, gpgme_protocol_t proto,
 {
   char *new_file_name;
   char *new_home_dir;
+  char *new_version;
 
   /* FIXME: Use some PROTO_MAX definition.  */
   if (proto > DIM (engine_ops))
@@ -399,6 +452,17 @@ _gpgme_set_engine_info (gpgme_engine_info_t info, gpgme_protocol_t proto,
         new_home_dir = NULL;
     }
 
+  new_version = engine_get_version (proto, new_file_name);
+  if (!new_version)
+    {
+      new_version = strdup ("1.0.0"); /* Fake one for dummy entries.  */
+      if (!new_version)
+        {
+          free (new_file_name);
+          free (new_home_dir);
+        }
+    }
+
   /* Remove the old members.  */
   assert (info->file_name);
   free (info->file_name);
@@ -410,7 +474,7 @@ _gpgme_set_engine_info (gpgme_engine_info_t info, gpgme_protocol_t proto,
   /* Install the new members.  */
   info->file_name = new_file_name;
   info->home_dir = new_home_dir;
-  info->version = engine_get_version (proto, new_file_name);
+  info->version = new_version;
 
   return 0;
 }
@@ -461,7 +525,8 @@ _gpgme_engine_new (gpgme_engine_info_t info, engine_t *r_engine)
     {
       gpgme_error_t err;
       err = (*engine->ops->new) (&engine->engine,
-                                info->file_name, info->home_dir);
+                                info->file_name, info->home_dir,
+                                 info->version);
       if (err)
        {
          free (engine);
@@ -501,6 +566,21 @@ _gpgme_engine_release (engine_t engine)
 }
 
 
+/* Set a status callback which is used to monitor the status values
+ * before they are passed to a handler set with
+ * _gpgme_engine_set_status_handler.  */
+void
+_gpgme_engine_set_status_cb (engine_t engine,
+                             gpgme_status_cb_t cb, void *cb_value)
+{
+  if (!engine)
+    return;
+
+  if (engine->ops->set_status_cb)
+    (*engine->ops->set_status_cb) (engine->engine, cb, cb_value);
+}
+
+
 void
 _gpgme_engine_set_status_handler (engine_t engine,
                                  engine_status_handler_t fnc, void *fnc_value)
@@ -572,8 +652,11 @@ _gpgme_engine_set_protocol (engine_t engine, gpgme_protocol_t protocol)
 
 
 gpgme_error_t
-_gpgme_engine_op_decrypt (engine_t engine, gpgme_data_t ciph,
-                         gpgme_data_t plain)
+_gpgme_engine_op_decrypt (engine_t engine,
+                          gpgme_decrypt_flags_t flags,
+                          gpgme_data_t ciph,
+                         gpgme_data_t plain, int export_session_key,
+                          const char *override_session_key)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -581,27 +664,14 @@ _gpgme_engine_op_decrypt (engine_t engine, gpgme_data_t ciph,
   if (!engine->ops->decrypt)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
-  return (*engine->ops->decrypt) (engine->engine, ciph, plain);
-}
-
-
-gpgme_error_t
-_gpgme_engine_op_decrypt_verify (engine_t engine, gpgme_data_t ciph,
-                                gpgme_data_t plain)
-{
-  if (!engine)
-    return gpg_error (GPG_ERR_INV_VALUE);
-
-  if (!engine->ops->decrypt_verify)
-    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-
-  return (*engine->ops->decrypt_verify) (engine->engine, ciph, plain);
+  return (*engine->ops->decrypt) (engine->engine, flags, ciph, plain,
+                                  export_session_key, override_session_key);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_delete (engine_t engine, gpgme_key_t key,
-                        int allow_secret)
+                        unsigned int flags)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -609,7 +679,7 @@ _gpgme_engine_op_delete (engine_t engine, gpgme_key_t key,
   if (!engine->ops->delete)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
-  return (*engine->ops->delete) (engine->engine, key, allow_secret);
+  return (*engine->ops->delete) (engine->engine, key, flags);
 }
 
 
@@ -693,9 +763,13 @@ _gpgme_engine_op_export_ext (engine_t engine, const char *pattern[],
 
 
 gpgme_error_t
-_gpgme_engine_op_genkey (engine_t engine, gpgme_data_t help_data,
-                        int use_armor, gpgme_data_t pubkey,
-                        gpgme_data_t seckey)
+_gpgme_engine_op_genkey (engine_t engine,
+                         const char *userid, const char *algo,
+                         unsigned long reserved, unsigned long expires,
+                         gpgme_key_t key, unsigned int flags,
+                         gpgme_data_t help_data,
+                        unsigned int extraflags,
+                         gpgme_data_t pubkey, gpgme_data_t seckey)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -703,12 +777,44 @@ _gpgme_engine_op_genkey (engine_t engine, gpgme_data_t help_data,
   if (!engine->ops->genkey)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
-  return (*engine->ops->genkey) (engine->engine, help_data, use_armor,
+  return (*engine->ops->genkey) (engine->engine,
+                                 userid, algo, reserved, expires, key, flags,
+                                 help_data, extraflags,
                                 pubkey, seckey);
 }
 
 
 gpgme_error_t
+_gpgme_engine_op_keysign (engine_t engine, gpgme_key_t key, const char *userid,
+                          unsigned long expires, unsigned int flags,
+                          gpgme_ctx_t ctx)
+{
+  if (!engine)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (!engine->ops->keysign)
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  return (*engine->ops->keysign) (engine->engine,
+                                  key, userid, expires, flags, ctx);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_tofu_policy (engine_t engine,
+                              gpgme_key_t key,  gpgme_tofu_policy_t policy)
+{
+  if (!engine)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (!engine->ops->tofu_policy)
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  return (*engine->ops->tofu_policy) (engine->engine, key, policy);
+}
+
+
+gpgme_error_t
 _gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata,
                          gpgme_key_t *keyarray)
 {
@@ -724,7 +830,8 @@ _gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata,
 
 gpgme_error_t
 _gpgme_engine_op_keylist (engine_t engine, const char *pattern,
-                         int secret_only, gpgme_keylist_mode_t mode)
+                         int secret_only, gpgme_keylist_mode_t mode,
+                         int engine_flags)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -732,14 +839,15 @@ _gpgme_engine_op_keylist (engine_t engine, const char *pattern,
   if (!engine->ops->keylist)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
-  return (*engine->ops->keylist) (engine->engine, pattern, secret_only, mode);
+  return (*engine->ops->keylist) (engine->engine, pattern, secret_only, mode,
+                                  engine_flags);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_keylist_ext (engine_t engine, const char *pattern[],
                              int secret_only, int reserved,
-                             gpgme_keylist_mode_t mode)
+                             gpgme_keylist_mode_t mode, int engine_flags)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -748,7 +856,20 @@ _gpgme_engine_op_keylist_ext (engine_t engine, const char *pattern[],
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
   return (*engine->ops->keylist_ext) (engine->engine, pattern, secret_only,
-                                     reserved, mode);
+                                     reserved, mode, engine_flags);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_keylist_data (engine_t engine, gpgme_data_t data)
+{
+  if (!engine)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (!engine->ops->keylist_data)
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  return (*engine->ops->keylist_data) (engine->engine, data);
 }
 
 
@@ -784,7 +905,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);
@@ -792,7 +914,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);
 }
 
 
@@ -860,6 +983,34 @@ _gpgme_engine_op_conf_save (engine_t engine, gpgme_conf_comp_t conf)
 }
 
 
+gpgme_error_t
+_gpgme_engine_op_conf_dir (engine_t engine, const char *what, char **result)
+{
+  if (!engine)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (!engine->ops->conf_dir)
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  return (*engine->ops->conf_dir) (engine->engine, what, result);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_query_swdb (engine_t engine,
+                             const char *name, const char *iversion,
+                             gpgme_query_swdb_result_t result)
+{
+  if (!engine)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (!engine->ops->query_swdb)
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  return (*engine->ops->query_swdb) (engine->engine, name, iversion, result);
+}
+
+
 void
 _gpgme_engine_set_io_cbs (engine_t engine, gpgme_io_cbs_t io_cbs)
 {
@@ -936,3 +1087,21 @@ _gpgme_engine_set_pinentry_mode (engine_t engine, gpgme_pinentry_mode_t mode)
 
   return (*engine->ops->set_pinentry_mode) (engine->engine, mode);
 }
+
+
+gpgme_error_t
+_gpgme_engine_op_spawn (engine_t engine,
+                        const char *file, const char *argv[],
+                        gpgme_data_t datain,
+                        gpgme_data_t dataout, gpgme_data_t dataerr,
+                        unsigned int flags)
+{
+  if (!engine)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (!engine->ops->opspawn)
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  return (*engine->ops->opspawn) (engine->engine, file, argv,
+                                  datain, dataout, dataerr, flags);
+}