Make --runtime option of gpgconf under W32 work.
[gnupg.git] / tools / gpgconf-comp.c
index 5dc55de..579025e 100644 (file)
@@ -1,5 +1,5 @@
 /* gpgconf-comp.c - Configuration utility for GnuPG.
- * Copyright (C) 2004, 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2007, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -228,6 +228,12 @@ typedef enum
     /* A 40 character fingerprint.  */
     GC_ARG_TYPE_KEY_FPR = 34,
 
+    /* A user ID or key ID or fingerprint for a certificate.  */
+    GC_ARG_TYPE_PUB_KEY = 35,
+
+    /* A user ID or key ID or fingerprint for a certificate with a key.  */
+    GC_ARG_TYPE_SEC_KEY = 36,
+
     /* ADD NEW COMPLEX TYPE ENTRIES HERE.  */
 
     /* The number of the above entries.  */
@@ -273,6 +279,8 @@ static struct
     { GC_ARG_TYPE_STRING, "pathname" },
     { GC_ARG_TYPE_STRING, "ldap server" },
     { GC_ARG_TYPE_STRING, "key fpr" },
+    { GC_ARG_TYPE_STRING, "pub key" },
+    { GC_ARG_TYPE_STRING, "sec key" },
   };
 
 
@@ -673,6 +681,9 @@ static gc_option_t gc_options_gpg[] =
    { "allow-pka-lookup", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
      "gnupg", N_("allow PKA lookups (DNS requests)"),
      GC_ARG_TYPE_NONE, GC_BACKEND_GPG },
+   { "auto-key-locate", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+     "gnupg", N_("|MECHANISMS|use MECHANISMS to locate keys by mail address"),
+     GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
 
 
    GC_OPTION_NULL
@@ -715,6 +726,9 @@ static gc_option_t gc_options_gpgsm[] =
    { "prefer-system-dirmngr", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
      "gnupg", "use system's dirmngr if available",
      GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+   { "disable-dirmngr", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+     "gnupg", N_("disable all access to the dirmngr"),
+     GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
    { "p12-charset", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
      "gnupg", N_("|NAME|use encoding NAME for PKCS#12 passphrases"),
      GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM },
@@ -840,7 +854,7 @@ static gc_option_t gc_options_dirmngr[] =
      "dirmngr", "|URL|redirect all HTTP requests to URL",
      GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR },
    { "honor-http-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
-     "dirmngr", N_("use system's HTTP proxy setting"),
+     "gnupg", N_("use system's HTTP proxy setting"),
      GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
 
    { "LDAP",
@@ -874,7 +888,7 @@ static gc_option_t gc_options_dirmngr[] =
       GC_BACKEND_DIRMNGR in this component, so that the entry for
       "ldapserverlist-file will be initialized before this one.  */
    { "LDAP Server", GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_LIST, GC_LEVEL_BASIC,
-     NULL, "LDAP server list",
+     "gnupg", N_("LDAP server list"),
      GC_ARG_TYPE_LDAP_SERVER, GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST },
    { "max-replies", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
      "dirmngr", "|N|do not return more than N items in one query",
@@ -1000,9 +1014,26 @@ gpg_agent_runtime_change (void)
 
   /* Ignore any errors here.  */
   kill (pid, SIGHUP);
+#else
+  gpg_error_t err;
+  const char *pgmname;
+  const char *argv[2];
+  pid_t pid;
+  
+  pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
+  argv[0] = "reloadagent";
+  argv[1] = NULL;
+  
+  err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+  if (!err)
+    err = gnupg_wait_process (pgmname, pid, NULL);
+  if (err)
+    gc_error (0, 0, "error running `%s%s': %s",
+              pgmname, " reloadagent", gpg_strerror (err));
 #endif /*!HAVE_W32_SYSTEM*/
 }
 
+
 \f
 /* More or less Robust version of dgettext.  It has the side effect of
    switching the codeset to utf-8 because this is what we want to
@@ -1013,7 +1044,27 @@ gpg_agent_runtime_change (void)
 static const char *
 my_dgettext (const char *domain, const char *msgid)
 {
-#ifdef ENABLE_NLS
+#ifdef USE_SIMPLE_GETTEXT
+  if (domain)
+    {
+      static int switched_codeset;
+      char *text;
+      
+      if (!switched_codeset)
+        {
+          switched_codeset = 1;
+          gettext_select_utf8 (1);
+        }
+
+      if (!strcmp (domain, "gnupg"))
+        domain = PACKAGE_GT;
+
+      /* FIXME: we have no dgettext, thus we can't switch.  */
+
+      text = gettext (msgid);
+      return text ? text : msgid;
+    }
+#elif defined(ENABLE_NLS)
   if (domain)
     {
       static int switched_codeset;
@@ -1283,118 +1334,140 @@ collect_error_output (int fd, const char *tag)
 }
 
 
-
-/* Check all components that are available.  */
-void
-gc_component_check_programs (FILE *out)
+/* Check the options of a single component.  Returns 0 if everything
+   is OK.  */
+int
+gc_component_check_options (int component, FILE *out, const char *conf_file)
 {
   gpg_error_t err;
-  gc_component_t component;
   unsigned int result;
   int backend_seen[GC_BACKEND_NR];
   gc_backend_t backend;
   gc_option_t *option;
-  const char *desc;
   const char *pgmname;
-  const char *argv[2];
+  const char *argv[4];
+  int i;
   pid_t pid;
   int exitcode;
   int filedes[2];
-  error_line_t errlines, errptr;
+  error_line_t errlines;
 
   /* We use a temporary file to collect the error output.  It would be
      better to use a pipe here but as of now we have no suitable
      fucntion to create a portable pipe outside of exechelp.  Thus it
      is easier to use the tempfile approach.  */
-  for (component = 0; component < GC_COMPONENT_NR; component++)
+
+  for (backend = 0; backend < GC_BACKEND_NR; backend++)
+    backend_seen[backend] = 0;
+
+  option = gc_component[component].options;
+  for (; option && option->name; option++)
     {
-      if (!gc_component[component].options)
-        continue;
+      if ((option->flags & GC_OPT_FLAG_GROUP))
+       continue;
+      backend = option->backend;
+      if (backend_seen[backend])
+       continue;
+      backend_seen[backend] = 1;
+      assert (backend != GC_BACKEND_ANY);
+      if (!gc_backend[backend].program)
+       continue;
+      if (!gc_backend[backend].module_name)
+       continue;
+
+      break;
+    }
+  if (! option || ! option->name)
+    return 0;
 
-      for (backend = 0; backend < GC_BACKEND_NR; backend++)
-        backend_seen[backend] = 0;
+  pgmname = gnupg_module_name (gc_backend[backend].module_name);
+  i = 0;
+  if (conf_file)
+    {
+      argv[i++] = "--options";
+      argv[i++] = conf_file;
+    }
+  argv[i++] = "--gpgconf-test";
+  argv[i++] = NULL;
+  
+  err = gnupg_create_inbound_pipe (filedes);
+  if (err)
+    gc_error (1, 0, _("error creating a pipe: %s\n"), 
+             gpg_strerror (err));
+  
+  result = 0;
+  errlines = NULL;
+  if (gnupg_spawn_process_fd (pgmname, argv, -1, -1, filedes[1], &pid))
+    {
+      close (filedes[0]);
+      close (filedes[1]);
+      result |= 1; /* Program could not be run.  */
+    }
+  else 
+    {
+      close (filedes[1]);
+      errlines = collect_error_output (filedes[0], 
+                                      gc_component[component].name);
+      if (gnupg_wait_process (pgmname, pid, &exitcode))
+       {
+         if (exitcode == -1)
+           result |= 1; /* Program could not be run or it
+                           terminated abnormally.  */
+         result |= 2; /* Program returned an error.  */
+       }
+    }
+  
+  /* If the program could not be run, we can't tell whether
+     the config file is good.  */
+  if (result & 1)
+    result |= 2;  
+  
+  if (out)
+    {
+      const char *desc;
+      error_line_t errptr;
+
+      desc = gc_component[component].desc;
+      desc = my_dgettext (gc_component[component].desc_domain, desc);
+      fprintf (out, "%s:%s:",
+              gc_component[component].name, my_percent_escape (desc));
+      fputs (my_percent_escape (pgmname), out);
+      fprintf (out, ":%d:%d:", !(result & 1), !(result & 2));
+      for (errptr = errlines; errptr; errptr = errptr->next)
+       {
+         if (errptr != errlines)
+           fputs ("\n:::::", out); /* Continuation line.  */
+         if (errptr->fname)
+           fputs (my_percent_escape (errptr->fname), out);
+         putc (':', out);
+         if (errptr->fname)
+           fprintf (out, "%u", errptr->lineno);
+         putc (':', out);
+         fputs (my_percent_escape (errptr->errtext), out);
+         putc (':', out);
+       }
+      putc ('\n', out);
+    }
 
-      option = gc_component[component].options;
-      for (; option && option->name; option++)
-        {
-          if ((option->flags & GC_OPT_FLAG_GROUP))
-            continue;
-          backend = option->backend;
-          if (backend_seen[backend])
-            continue;
-          backend_seen[backend] = 1;
-          assert (backend != GC_BACKEND_ANY);
-          if (!gc_backend[backend].program)
-            continue;
-          if (!gc_backend[backend].module_name)
-            continue;
-
-          pgmname = gnupg_module_name (gc_backend[backend].module_name);
-          argv[0] = "--gpgconf-test";
-          argv[1] = NULL;
-
-          err = gnupg_create_inbound_pipe (filedes);
-          if (err)
-            gc_error (1, 0, _("error creating a pipe: %s\n"), 
-                      gpg_strerror (err));
-
-          result = 0;
-          errlines = NULL;
-          if (gnupg_spawn_process_fd (pgmname, argv, -1, -1, filedes[1], &pid))
-            {
-              close (filedes[0]);
-              close (filedes[1]);
-              result |= 1; /* Program could not be run.  */
-            }
-          else 
-            {
-              close (filedes[1]);
-              errlines = collect_error_output (filedes[0], 
-                                               gc_component[component].name);
-              if (gnupg_wait_process (pgmname, pid, &exitcode))
-                {
-                  if (exitcode == -1)
-                    result |= 1; /* Program could not be run or it
-                                    terminated abnormally.  */
-                  result |= 2; /* Program returned an error.  */
-                }
-            }
-          
-          /* If the program could not be run, we can't tell whether
-             the config file is good.  */
-          if ((result&1))
-            result |= 2;  
-          
-          desc = gc_component[component].desc;
-          desc = my_dgettext (gc_component[component].desc_domain, desc);
-          fprintf (out, "%s:%s:",
-                   gc_component[component].name, my_percent_escape (desc));
-          fputs (my_percent_escape (pgmname), out);
-          fprintf (out, ":%d:%d:", !(result & 1), !(result & 2));
-          for (errptr = errlines; errptr; errptr = errptr->next)
-            {
-              if (errptr != errlines)
-                fputs ("\n:::::", out); /* Continuation line.  */
-              if (errptr->fname)
-                fputs (my_percent_escape (errptr->fname), out);
-              putc (':', out);
-              if (errptr->fname)
-                fprintf (out, "%u", errptr->lineno);
-              putc (':', out);
-              fputs (my_percent_escape (errptr->errtext), out);
-              putc (':', out);
-            }
-          putc ('\n', out);
-          
-          while (errlines)
-            {
-              error_line_t tmp = errlines->next;
-              xfree (errlines);
-              errlines = tmp;
-            }
-          break; /* Loop over options of this component  */
-        }
-    } 
+  while (errlines)
+    {
+      error_line_t tmp = errlines->next;
+      xfree (errlines);
+      errlines = tmp;
+    }
+
+  return result;
+}
+
+
+/* Check all components that are available.  */
+void
+gc_check_programs (FILE *out)
+{
+  gc_component_t component;
+
+  for (component = 0; component < GC_COMPONENT_NR; component++)
+    gc_component_check_options (component, out, NULL);
 }
 
 
@@ -1531,7 +1604,6 @@ void
 gc_component_list_options (int component, FILE *out)
 {  
   const gc_option_t *option = gc_component[component].options;
-  const gc_option_t *group_option = NULL;
 
   while (option && option->name)
     {
@@ -1544,17 +1616,38 @@ gc_component_list_options (int component, FILE *out)
        }
 
       if (option->flags & GC_OPT_FLAG_GROUP)
-       group_option = option;
-      else
        {
-         if (group_option)
+         const gc_option_t *group_option = option + 1;
+         gc_expert_level_t level = GC_LEVEL_NR;
+
+         /* The manual states that the group level is always the
+            minimum of the levels of all contained options.  Due to
+            different active options, and because it is hard to
+            maintain manually, we calculate it here.  The value in
+            the global static table is ignored.  */
+         
+         while (group_option->name)
            {
-             list_one_option (group_option, out);
-             group_option = NULL;
+             if (group_option->flags & GC_OPT_FLAG_GROUP)
+               break;
+             if (group_option->level < level)
+               level = group_option->level;
+             group_option++;
            }
 
-         list_one_option (option, out);
+         /* Check if group is empty.  */
+         if (level != GC_LEVEL_NR)
+           {
+             gc_option_t opt_copy;
+
+             /* Fix up the group level.  */
+             memcpy (&opt_copy, option, sizeof (opt_copy));
+             opt_copy.level = level;
+             list_one_option (&opt_copy, out);
+           }
        }
+      else
+       list_one_option (option, out);
 
       option++;
     }
@@ -2036,6 +2129,15 @@ option_check_validity (gc_option_t *option, unsigned long flags,
          if (*arg != '"')
            gc_error (1, 0, "string argument for option %s must begin "
                      "with a quote (\") character", option->name);
+
+         /* FIXME: We do not allow empty string arguments for now, as
+            we do not quote arguments in configuration files, and
+            thus no argument is indistinguishable from the empty
+            string.  */
+         if (arg[1] == '\0' || arg[1] == ',')
+           gc_error (1, 0, "empty string argument for option %s is "
+                     "currently not allowed.  Please report this!",
+                     option->name);
        }
       else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_INT32)
        {
@@ -2447,6 +2549,8 @@ change_options_program (gc_component_t component, gc_backend_t backend,
   char *src_filename;
   char *dest_filename;
   char *orig_filename;
+  /* Special hack for gpg, see below.  */
+  int utf8strings_seen = 0;
 
   /* FIXME.  Throughout the function, do better error reporting.  */
   dest_filename = xstrdup (get_config_pathname (component, backend));
@@ -2506,6 +2610,15 @@ change_options_program (gc_component_t component, gc_backend_t backend,
              else
                break;
            }
+         else if (backend == GC_BACKEND_GPG && in_marker
+                  && ! strcmp ("utf8-strings\n", line))
+           {
+             /* Strip duplicated entries.  */
+             if (utf8strings_seen)
+               disable = 1;
+             else
+               utf8strings_seen = 1;
+           }
 
          start = line;
          while (*start == ' ' || *start == '\t')
@@ -2571,7 +2684,7 @@ change_options_program (gc_component_t component, gc_backend_t backend,
      followed by the rest of the original file.  */
 
   /* We have to turn on UTF8 strings for GnuPG.  */
-  if (backend == GC_BACKEND_GPG)
+  if (backend == GC_BACKEND_GPG && ! utf8strings_seen)
     fprintf (src_file, "utf8-strings\n");
 
   option = gc_component[component].options;
@@ -2757,7 +2870,7 @@ change_one_value (gc_option_t *option, int *runtime,
    modifications are expected to already have been set to the global
    table. */
 void
-gc_component_change_options (int component, FILE *in)
+gc_component_change_options (int component, FILE *in, FILE *out)
 {
   int err = 0;
   int runtime[GC_BACKEND_NR];
@@ -2861,10 +2974,26 @@ gc_component_change_options (int component, FILE *in)
        }
 
       if (gc_backend[option->backend].program)
-       err = change_options_program (component, option->backend,
-                                     &src_pathname[option->backend],
-                                     &dest_pathname[option->backend],
-                                     &orig_pathname[option->backend]);
+       {
+         err = change_options_program (component, option->backend,
+                                       &src_pathname[option->backend],
+                                       &dest_pathname[option->backend],
+                                       &orig_pathname[option->backend]);
+         if (! err)
+           {
+             /* External verification.  */
+             err = gc_component_check_options (component, out,
+                                               src_pathname[option->backend]);
+             if (err)
+               {
+                 gc_error (0, 0,
+                           _("External verification of component %s failed"),
+                           gc_component[component].name);
+                 errno = EINVAL;
+               }
+           }
+
+       }
       else
        err = change_options_file (component, option->backend,
                                   &src_pathname[option->backend],
@@ -2877,7 +3006,7 @@ gc_component_change_options (int component, FILE *in)
       option++;
     }
 
-  if (!err)
+  if (! err && ! opt.dry_run)
     {
       int i;
 
@@ -2920,12 +3049,12 @@ gc_component_change_options (int component, FILE *in)
        }
     }
 
-  if (err)
+  if (err || opt.dry_run)
     {
       int i;
       int saved_errno = errno;
 
-      /* An error occured.  */
+      /* An error occured or a dry-run is requested.  */
       for (i = 0; i < GC_BACKEND_NR; i++)
        {
          if (src_pathname[i])
@@ -2953,7 +3082,11 @@ gc_component_change_options (int component, FILE *in)
                unlink (dest_pathname[i]);
            }
        }
-      gc_error (1, saved_errno, "could not commit changes");
+      if (err)
+       gc_error (1, saved_errno, "could not commit changes");
+
+      /* Fall-through for dry run.  */
+      goto leave;
     }
 
   /* If it all worked, notify the daemons of the changes.  */
@@ -2981,6 +3114,7 @@ gc_component_change_options (int component, FILE *in)
        rename (orig_pathname[backend], backup_pathname);
       }
 
+ leave:
   xfree (line);
 }
 
@@ -3389,7 +3523,7 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
 
       for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++)
         {
-          gc_component_change_options (component_id, NULL);
+          gc_component_change_options (component_id, NULL, NULL);
         }
       opt.runtime = save_opt_runtime;