core: New function gpgme_op_tofu_policy
[gpgme.git] / src / engine-gpgconf.c
index cfa04ce..90f32c7 100644 (file)
@@ -1,19 +1,20 @@
 /* engine-gpgconf.c - gpg-conf engine.
    Copyright (C) 2000 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008 g10 Code GmbH
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008,
+                 2013 g10 Code GmbH
+
    This file is part of GPGME.
 
    GPGME is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; either version 2.1 of
    the License, or (at your option) any later version.
-   
+
    GPGME is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    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/>.
  */
 
 #include <stdlib.h>
 #include <string.h>
-#include <sys/types.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
 #include <assert.h>
-#include <unistd.h>
-#include <locale.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
 #include <fcntl.h> /* FIXME */
 #include <errno.h>
 
@@ -57,14 +61,14 @@ static char *
 gpgconf_get_version (const char *file_name)
 {
   return _gpgme_get_program_version (file_name ? file_name
-                                    : _gpgme_get_gpgconf_path ());
+                                    : _gpgme_get_default_gpgconf_name ());
 }
 
 
 static const char *
 gpgconf_get_req_version (void)
 {
-  return NEED_GPGCONF_VERSION;
+  return "2.0.4";
 }
 
 \f
@@ -86,17 +90,20 @@ gpgconf_release (void *engine)
 
 
 static gpgme_error_t
-gpgconf_new (void **engine, const char *file_name, const char *home_dir)
+gpgconf_new (void **engine, const char *file_name, const char *home_dir,
+             const char *version)
 {
   gpgme_error_t err = 0;
   engine_gpgconf_t gpgconf;
 
+  (void)version; /* Not yet used.  */
+
   gpgconf = calloc (1, sizeof *gpgconf);
   if (!gpgconf)
-    return gpg_error_from_errno (errno);
+    return gpg_error_from_syserror ();
 
   gpgconf->file_name = strdup (file_name ? file_name
-                              : _gpgme_get_gpgconf_path ());
+                              : _gpgme_get_default_gpgconf_name ());
   if (!gpgconf->file_name)
     err = gpg_error_from_syserror ();
 
@@ -144,7 +151,7 @@ release_opt (gpgme_conf_opt_t opt)
   release_arg (opt->default_value, opt->alt_type);
   if (opt->default_description)
     free (opt->default_description);
-  
+
   release_arg (opt->no_arg_value, opt->alt_type);
   release_arg (opt->value, opt->alt_type);
   release_arg (opt->new_value, opt->alt_type);
@@ -188,17 +195,20 @@ gpgconf_config_release (gpgme_conf_comp_t conf)
     }
 }
 
-
+/* Read from gpgconf and pass line after line to the hook function.
+   We put a limit of 64 k on the maximum size for a line.  This should
+   allow for quite a long "group" line, which is usually the longest
+   line (mine is currently ~3k).  */
 static gpgme_error_t
-gpgconf_read (void *engine, char *arg1, char *arg2,
+gpgconf_read (void *engine, const char *arg1, char *arg2,
              gpgme_error_t (*cb) (void *hook, char *line),
              void *hook)
 {
   struct engine_gpgconf *gpgconf = engine;
   gpgme_error_t err = 0;
-#define LINELENGTH 1024
-  char linebuf[LINELENGTH] = "";
-  int linelen = 0;
+  char *linebuf;
+  size_t linebufsize;
+  int linelen;
   char *argv[4] = { NULL /* file_name */, NULL, NULL, NULL };
   int rp[2];
   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
@@ -207,7 +217,7 @@ gpgconf_read (void *engine, char *arg1, char *arg2,
   int nread;
   char *mark = NULL;
 
-  argv[1] = arg1;
+  argv[1] = (char*)arg1;
   argv[2] = arg2;
 
 
@@ -215,13 +225,14 @@ gpgconf_read (void *engine, char *arg1, char *arg2,
 
   /* _gpgme_engine_new guarantees that this is not NULL.  */
   argv[0] = gpgconf->file_name;
-  
+
   if (_gpgme_io_pipe (rp, 1) < 0)
     return gpg_error_from_syserror ();
 
   cfd[0].fd = rp[1];
 
-  status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL);
+  status = _gpgme_io_spawn (gpgconf->file_name, argv,
+                            IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
   if (status < 0)
     {
       _gpgme_io_close (rp[0]);
@@ -229,51 +240,80 @@ gpgconf_read (void *engine, char *arg1, char *arg2,
       return gpg_error_from_syserror ();
     }
 
-  do
+  linebufsize = 1024; /* Usually enough for conf lines.  */
+  linebuf = malloc (linebufsize);
+  if (!linebuf)
     {
-      nread = _gpgme_io_read (rp[0], 
-                              linebuf + linelen, LINELENGTH - linelen - 1);
-      if (nread > 0)
-       {
-          char *line;
-          const char *lastmark = NULL;
-          size_t nused;
-
-         linelen += nread;
-         linebuf[linelen] = '\0';
-
-         for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
-           {
-              lastmark = mark;
-             if (mark > line && mark[-1] == '\r')
-               mark[-1] = '\0';
-              else
-                mark[0] = '\0';
-
-             /* Got a full line.  Due to the CR removal code (which
-                 occurs only on Windows) we might be one-off and thus
-                 would see empty lines.  Don't pass them to the
-                 callback. */
-             err = *line? (*cb) (hook, line) : 0;
-             if (err)
-               goto leave;
-           }
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  linelen = 0;
 
-          nused = lastmark? (lastmark + 1 - linebuf) : 0;
-          memmove (linebuf, linebuf + nused, linelen - nused);
-          linelen -= nused;
-       }
+  while ((nread = _gpgme_io_read (rp[0], linebuf + linelen,
+                                  linebufsize - linelen - 1)))
+    {
+      char *line;
+      const char *lastmark = NULL;
+      size_t nused;
+
+      if (nread < 0)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+
+      linelen += nread;
+      linebuf[linelen] = '\0';
+
+      for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
+        {
+          lastmark = mark;
+          if (mark > line && mark[-1] == '\r')
+            mark[-1] = '\0';
+          else
+            mark[0] = '\0';
+
+          /* Got a full line.  Due to the CR removal code (which
+             occurs only on Windows) we might be one-off and thus
+             would see empty lines.  Don't pass them to the
+             callback. */
+          err = *line? (*cb) (hook, line) : 0;
+          if (err)
+            goto leave;
+        }
+
+      nused = lastmark? (lastmark + 1 - linebuf) : 0;
+      memmove (linebuf, linebuf + nused, linelen - nused);
+      linelen -= nused;
+
+      if (!(linelen < linebufsize - 1))
+        {
+          char *newlinebuf;
+
+          if (linelen <  8 * 1024 - 1)
+            linebufsize = 8 * 1024;
+          else if (linelen < 64 * 1024 - 1)
+            linebufsize = 64 * 1024;
+          else
+            {
+              /* We reached our limit - give up.  */
+              err = gpg_error (GPG_ERR_LINE_TOO_LONG);
+              goto leave;
+            }
+
+          newlinebuf = realloc (linebuf, linebufsize);
+          if (!newlinebuf)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
+          linebuf = newlinebuf;
+        }
     }
-  while (nread > 0 && linelen < LINELENGTH - 1);
-  
-  if (!err && nread < 0)
-    err = gpg_error_from_syserror ();
-  if (!err && nread > 0)
-    err = gpg_error (GPG_ERR_LINE_TOO_LONG);
 
  leave:
+  free (linebuf);
   _gpgme_io_close (rp[0]);
-
   return err;
 }
 
@@ -297,7 +337,7 @@ gpgconf_config_load_cb (void *hook, char *line)
 
   /* We require at least the first 3 fields.  */
   if (fields < 2)
-    return gpg_error (GPG_ERR_INV_ENGINE);
+    return trace_gpg_error (GPG_ERR_INV_ENGINE);
 
   /* Find the pointer to the new component in the list.  */
   while (comp && comp->next)
@@ -366,15 +406,15 @@ gpgconf_parse_option (gpgme_conf_opt_t opt,
            case GPGME_CONF_UINT32:
              arg->value.uint32 = strtoul (line, NULL, 0);
              break;
-             
+
            case GPGME_CONF_INT32:
              arg->value.uint32 = strtol (line, NULL, 0);
              break;
-             
+
            case GPGME_CONF_STRING:
               /* The complex types below are only here to silent the
                  compiler warning. */
-            case GPGME_CONF_FILENAME: 
+            case GPGME_CONF_FILENAME:
             case GPGME_CONF_LDAP_SERVER:
             case GPGME_CONF_KEY_FPR:
             case GPGME_CONF_PUB_KEY:
@@ -382,7 +422,7 @@ gpgconf_parse_option (gpgme_conf_opt_t opt,
             case GPGME_CONF_ALIAS_LIST:
              /* Skip quote character.  */
              line++;
-             
+
              err = _gpgme_decode_percent_string (line, &arg->value.string,
                                                  0, 0);
              if (err)
@@ -423,7 +463,7 @@ gpgconf_config_load_cb2 (void *hook, char *line)
 
   /* We require at least the first 10 fields.  */
   if (fields < 10)
-    return gpg_error (GPG_ERR_INV_ENGINE);
+    return trace_gpg_error (GPG_ERR_INV_ENGINE);
 
   opt = calloc (1, sizeof (*opt));
   if (!opt)
@@ -534,7 +574,7 @@ gpgconf_conf_load (void *engine, gpgme_conf_comp_t *comp_p)
 \f
 gpgme_error_t
 _gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
-                    gpgme_conf_type_t type, void *value)
+                    gpgme_conf_type_t type, const void *value)
 {
   gpgme_conf_arg_t arg;
 
@@ -554,11 +594,11 @@ _gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
        case GPGME_CONF_UINT32:
          arg->value.uint32 = *((unsigned int *) value);
          break;
-         
+
        case GPGME_CONF_INT32:
          arg->value.int32 = *((int *) value);
          break;
-         
+
        case GPGME_CONF_STRING:
        case GPGME_CONF_FILENAME:
        case GPGME_CONF_LDAP_SERVER:
@@ -573,7 +613,7 @@ _gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
              return gpg_error_from_syserror ();
            }
          break;
-         
+
        default:
          free (arg);
          return gpg_error (GPG_ERR_INV_VALUE);
@@ -597,7 +637,7 @@ _gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
     case GPGME_CONF_STRING:
     default:
       break;
-       
+
     case GPGME_CONF_FILENAME:
     case GPGME_CONF_LDAP_SERVER:
     case GPGME_CONF_KEY_FPR:
@@ -615,16 +655,19 @@ _gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
 gpgme_error_t
 _gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg)
 {
-  if (opt->new_value)
-    release_arg (opt->new_value, opt->alt_type);
-
   if (reset)
     {
+      if (opt->new_value)
+       release_arg (opt->new_value, opt->alt_type);
       opt->new_value = NULL;
       opt->change_value = 0;
     }
   else
     {
+      /* Support self-assignment, for example for adding an item to an
+        existing list.  */
+      if (opt->new_value && arg != opt->new_value)
+       release_arg (opt->new_value, opt->alt_type);
       opt->new_value = arg;
       opt->change_value = 1;
     }
@@ -635,14 +678,14 @@ _gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg)
 /* FIXME: Major problem: We don't get errors from gpgconf.  */
 
 static gpgme_error_t
-gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf)
+gpgconf_write (void *engine, const char *arg1, char *arg2, gpgme_data_t conf)
 {
   struct engine_gpgconf *gpgconf = engine;
   gpgme_error_t err = 0;
 #define BUFLEN 1024
   char buf[BUFLEN];
   int buflen = 0;
-  char *argv[] = { NULL /* file_name */, arg1, arg2, 0 };
+  char *argv[] = { NULL /* file_name */, (char*)arg1, arg2, 0 };
   int rp[2];
   struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */}, {-1, -1} };
   int status;
@@ -652,14 +695,14 @@ gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf)
 
   /* _gpgme_engine_new guarantees that this is not NULL.  */
   argv[0] = gpgconf->file_name;
-  argv[0] = "/nowhere/path-needs-to-be-fixed/gpgconf";
 
   if (_gpgme_io_pipe (rp, 0) < 0)
     return gpg_error_from_syserror ();
 
   cfd[0].fd = rp[0];
 
-  status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL);
+  status = _gpgme_io_spawn (gpgconf->file_name, argv,
+                            IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
   if (status < 0)
     {
       _gpgme_io_close (rp[0]);
@@ -732,53 +775,54 @@ arg_to_data (gpgme_data_t conf, gpgme_conf_opt_t option, gpgme_conf_arg_t arg)
          buf[sizeof (buf) - 1] = '\0';
          amt = gpgme_data_write (conf, buf, strlen (buf));
          break;
-         
+
        case GPGME_CONF_INT32:
          snprintf (buf, sizeof (buf), "%i", arg->value.uint32);
          buf[sizeof (buf) - 1] = '\0';
          amt = gpgme_data_write (conf, buf, strlen (buf));
          break;
-       
-          
+
+
        case GPGME_CONF_STRING:
           /* The complex types below are only here to silent the
              compiler warning. */
-        case GPGME_CONF_FILENAME: 
+        case GPGME_CONF_FILENAME:
         case GPGME_CONF_LDAP_SERVER:
         case GPGME_CONF_KEY_FPR:
         case GPGME_CONF_PUB_KEY:
         case GPGME_CONF_SEC_KEY:
         case GPGME_CONF_ALIAS_LIST:
-         /* One quote character, and three times to allow
-            for percent escaping.  */
-         {
-           char *ptr = arg->value.string;
-           amt = gpgme_data_write (conf, "\"", 1);
-           if (amt < 0)
-             break;
-
-           while (!err && *ptr)
-             {
-               switch (*ptr)
-                 {
-                 case '%':
-                   amt = gpgme_data_write (conf, "%25", 3);
-                   break;
-
-                 case ':':
-                   amt = gpgme_data_write (conf, "%3a", 3);
-                   break;
-
-                 case ',':
-                   amt = gpgme_data_write (conf, "%2c", 3);
-                   break;
-
-                 default:
-                   amt = gpgme_data_write (conf, ptr, 1);
-                 }
-               ptr++;
-             }
-         }
+          if (arg->value.string)
+            {
+              /* One quote character, and three times to allow for
+                 percent escaping.  */
+              char *ptr = arg->value.string;
+              amt = gpgme_data_write (conf, "\"", 1);
+              if (amt < 0)
+                break;
+
+              while (!err && *ptr)
+                {
+                  switch (*ptr)
+                    {
+                    case '%':
+                      amt = gpgme_data_write (conf, "%25", 3);
+                      break;
+
+                    case ':':
+                      amt = gpgme_data_write (conf, "%3a", 3);
+                      break;
+
+                    case ',':
+                      amt = gpgme_data_write (conf, "%2c", 3);
+                      break;
+
+                    default:
+                      amt = gpgme_data_write (conf, ptr, 1);
+                    }
+                  ptr++;
+                }
+            }
          break;
        }
 
@@ -793,7 +837,7 @@ arg_to_data (gpgme_data_t conf, gpgme_conf_opt_t option, gpgme_conf_arg_t arg)
 
   if (amt < 0)
     return gpg_error_from_syserror ();
-  
+
   return 0;
 }
 
@@ -868,6 +912,8 @@ gpgconf_conf_save (void *engine, gpgme_conf_comp_t comp)
 static void
 gpgconf_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
 {
+  (void)engine;
+  (void)io_cbs;
   /* Nothing to do.  */
 }
 
@@ -884,7 +930,7 @@ _gpgme_conf_release (gpgme_conf_comp_t conf)
 struct engine_ops _gpgme_engine_ops_gpgconf =
   {
     /* Static functions.  */
-    _gpgme_get_gpgconf_path,
+    _gpgme_get_default_gpgconf_name,
     NULL,
     gpgconf_get_version,
     gpgconf_get_req_version,
@@ -893,11 +939,14 @@ struct engine_ops _gpgme_engine_ops_gpgconf =
     /* Member functions.  */
     gpgconf_release,
     NULL,              /* reset */
+    NULL,               /* set_status_cb */
     NULL,              /* set_status_handler */
     NULL,              /* set_command_handler */
     NULL,              /* set_colon_line_handler */
     NULL,              /* set_locale */
+    NULL,              /* set_protocol */
     NULL,              /* decrypt */
+    NULL,              /* decrypt_verify */
     NULL,              /* delete */
     NULL,              /* edit */
     NULL,              /* encrypt */
@@ -908,6 +957,8 @@ struct engine_ops _gpgme_engine_ops_gpgconf =
     NULL,              /* import */
     NULL,              /* keylist */
     NULL,              /* keylist_ext */
+    NULL,               /* keysign */
+    NULL,               /* tofu_policy */
     NULL,              /* sign */
     NULL,              /* trustlist */
     NULL,              /* verify */
@@ -917,5 +968,9 @@ struct engine_ops _gpgme_engine_ops_gpgconf =
     gpgconf_conf_save,
     gpgconf_set_io_cbs,
     NULL,              /* io_event */
-    NULL               /* cancel */
+    NULL,              /* cancel */
+    NULL,               /* cancel_op */
+    NULL,               /* passwd */
+    NULL,               /* set_pinentry_mode */
+    NULL                /* opspawn */
   };