gpg: Make decryption of -R work w/o --try-secret-key or --default-key.
[gnupg.git] / tests / asschk.c
index 298d7bb..a869841 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 /* This is a simple stand-alone Assuan server test program.  We don't
    want to use the assuan library because we don't want to hide errors
-   in that library. 
+   in that library.
 
    The script language is line based.  Empty lines or lines containing
    only white spaces are ignored, line with a hash sign as first non
    white space character are treated as comments.
-   
+
    A simple macro mechanism is implemnted.  Macros are expanded before
    a line is processed but after comment processing.  Macros are only
    expanded once and non existing macros expand to the empty string.
    A macro is dereferenced by prefixing its name with a dollar sign;
-   the end of the name is currently indicated by a white space.  To
-   use a dollor sign verbatim, double it. 
+   the end of the name is currently indicated by a white space, a
+   dollar sign or a slash.  To use a dollor sign verbatim, double it.
 
    A macro is assigned by prefixing a statement with the macro name
    and an equal sign.  The value is assigned verbatim if it does not
       Print VALUE.
 
    openfile <filename>
-      Open file FILENAME for read access and retrun the file descriptor.
+      Open file FILENAME for read access and return the file descriptor.
 
    createfile <filename>
-      Create file FILENAME, open for write access and retrun the file
+      Create file FILENAME, open for write access and return the file
       descriptor.
 
-   pipeserver [<path>]
-      Connect to an Assuan server with name PATH.  If PATH is not
-      specified the value ../sm/gpgsm is used.
+   pipeserver <program>
+      Connect to the Assuan server PROGRAM.
 
    send <line>
       Send LINE to the server.
       Expect an ERR response from the server.  Status and data out put
       is ignored.
 
+   count-status <code>
+      Initialize the assigned variable to 0 and assign it as an counter for
+      status code CODE.  This command must be called with an assignment.
+
    quit
       Terminate the process.
 
@@ -93,6 +95,9 @@
    cmpfiles <first> <second>
       Returns true when the content of the files FIRST and SECOND match.
 
+   getenv <name>
+      Return the value of the environment variable NAME.
+
 */
 
 #include <stdio.h>
 # define ATTR_PRINTF(f,a)
 #endif
 
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+#  define __func__ __FUNCTION__
+# else
+/* Let's try our luck here.  Some systems may provide __func__ without
+   providing __STDC_VERSION__ 199901L.  */
+#  if 0
+#   define __func__ "<unknown>"
+#  endif
+# endif
+#endif
+
 #define spacep(p) (*(p) == ' ' || *(p) == '\t')
 
+#define MAX_LINELEN 2048
+
 typedef enum {
   LINE_OK = 0,
   LINE_ERR,
@@ -120,17 +139,23 @@ typedef enum {
   LINE_END,
 } LINETYPE;
 
+typedef enum {
+  VARTYPE_SIMPLE = 0,
+  VARTYPE_FD,
+  VARTYPE_COUNTER
+} VARTYPE;
+
 
 struct variable_s {
   struct variable_s *next;
-  int is_fd;
+  VARTYPE type;
+  unsigned int count;
   char *value;
   char name[1];
 };
 typedef struct variable_s *VARIABLE;
 
 
-
 static void die (const char *format, ...)  ATTR_PRINTF(1,2);
 
 
@@ -140,6 +165,9 @@ static const char *invocation_name;
 /* Talk a bit about what is going on. */
 static int opt_verbose;
 
+/* Option to ignore the echo command. */
+static int opt_no_echo;
+
 /* File descriptors used to communicate with the current server. */
 static int server_send_fd = -1;
 static int server_recv_fd = -1;
@@ -147,7 +175,7 @@ static int server_recv_fd = -1;
 /* The Assuan protocol limits the line length to 1024, so we can
    safely use a (larger) buffer.  The buffer is filled using the
    read_assuan(). */
-static char recv_line[2048];
+static char recv_line[MAX_LINELEN];
 /* Tell the status of the current line. */
 static LINETYPE recv_type;
 
@@ -171,6 +199,11 @@ die (const char *format, ...)
   exit (1);
 }
 
+#define die_0(format)          (die) ("%s: " format, __func__)
+#define die_1(format, a)       (die) ("%s: " format, __func__, (a))
+#define die_2(format, a, b)    (die) ("%s: " format, __func__, (a),(b))
+#define die_3(format, a, b, c) (die) ("%s: " format, __func__, (a),(b),(c))
+
 static void
 err (const char *format, ...)
 {
@@ -219,7 +252,7 @@ writen (int fd, const char *buffer, size_t length)
   while (length)
     {
       int nwritten = write (fd, buffer, length);
-      
+
       if (nwritten < 0)
         {
           if (errno == EINTR)
@@ -241,44 +274,74 @@ writen (int fd, const char *buffer, size_t length)
    type and store that in recv_type.  The function terminates on a
    communication error.  Returns a pointer into the inputline to the
    first byte of the arguments.  The parsing is very strict to match
-   excalty what we want to send. */
+   exaclty what we want to send. */
 static char *
 read_assuan (int fd)
 {
+  /* FIXME: For general robustness, the pending stuff needs to be
+     associated with FD.  */
+  static char pending[MAX_LINELEN];
+  static size_t pending_len;
   size_t nleft = sizeof recv_line;
   char *buf = recv_line;
   char *p;
-  int nread = 0;
 
   while (nleft > 0)
     {
-      int n = read (fd, buf, nleft);
-      if (n < 0)
+      int n;
+
+      if (pending_len)
         {
-          if (errno == EINTR)
-            continue;
-          die ("reading fd %d failed: %s", fd, strerror (errno));
+          if (pending_len >= nleft)
+            die_0 ("received line too large");
+          memcpy (buf, pending, pending_len);
+          n = pending_len;
+          pending_len = 0;
+        }
+      else
+        {
+          do
+            {
+              n = read (fd, buf, nleft);
+            }
+          while (n < 0 && errno == EINTR);
         }
+
+      if (opt_verbose && n >= 0 )
+       {
+         int i;
+
+         printf ("%s: read \"", __func__);
+         for (i = 0; i < n; i ++)
+           putc (buf[i], stdout);
+         printf ("\"\n");
+       }
+
+      if (n < 0)
+        die_2 ("reading fd %d failed: %s", fd, strerror (errno));
       else if (!n)
-        die ("received incomplete line on fd %d", fd);
+        die_1 ("received incomplete line on fd %d", fd);
       p = buf;
       nleft -= n;
       buf += n;
-      nread += n;
-      
+
       for (; n && *p != '\n'; n--, p++)
         ;
       if (n)
         {
-          /* fixme: keep pending bytes for next read. */
+          if (n>1)
+            {
+              n--;
+              memcpy (pending, p + 1, n);
+              pending_len = n;
+            }
+         *p = '\0';
           break;
         }
     }
   if (!nleft)
-    die ("received line too large");
-  assert (nread>0);
-  recv_line[nread-1] = 0;
-  
+    die_0 ("received line too large");
+
   p = recv_line;
   if (p[0] == 'O' && p[1] == 'K' && (p[2] == ' ' || !p[2]))
     {
@@ -306,8 +369,8 @@ read_assuan (int fd)
       recv_type = LINE_END;
       p += 3;
     }
-  else 
-    die ("invalid line type (%.5s)", p);
+  else
+    die_1 ("invalid line type (%.5s)", p);
 
   return p;
 }
@@ -321,13 +384,14 @@ write_assuan (int fd, const char *line)
   size_t n = strlen (line);
 
   if (n > 1024)
-    die ("line too long for Assuan protocol");
+    die_0 ("line too long for Assuan protocol");
   strcpy (buffer, line);
   if (!n || buffer[n-1] != '\n')
     buffer[n++] = '\n';
 
   if (writen (fd, buffer, n))
-      die ("sending line to %d failed: %s", fd, strerror (errno));
+      die_3 ("sending line (\"%s\") to %d failed: %s", buffer, fd,
+          strerror (errno));
 }
 
 
@@ -343,15 +407,15 @@ start_server (const char *pgmname)
   pid_t pid;
 
   if (pipe (rp) < 0)
-    die ("pipe creation failed: %s", strerror (errno));
+    die_1 ("pipe creation failed: %s", strerror (errno));
   if (pipe (wp) < 0)
-    die ("pipe creation failed: %s", strerror (errno));
+    die_1 ("pipe creation failed: %s", strerror (errno));
 
   fflush (stdout);
   fflush (stderr);
   pid = fork ();
   if (pid < 0)
-    die ("fork failed");
+    die_0 ("fork failed");
 
   if (!pid)
     {
@@ -366,18 +430,29 @@ start_server (const char *pgmname)
       if (wp[0] != STDIN_FILENO)
         {
           if (dup2 (wp[0], STDIN_FILENO) == -1)
-              die ("dup2 failed in child: %s", strerror (errno));
+            die_1 ("dup2 failed in child: %s", strerror (errno));
           close (wp[0]);
         }
       if (rp[1] != STDOUT_FILENO)
         {
           if (dup2 (rp[1], STDOUT_FILENO) == -1)
-              die ("dup2 failed in child: %s", strerror (errno));
+            die_1 ("dup2 failed in child: %s", strerror (errno));
           close (rp[1]);
         }
+      if (!opt_verbose)
+        {
+         int fd = open ("/dev/null", O_WRONLY);
+         if (fd == -1)
+           die_1 ("can't open '/dev/null': %s", strerror (errno));
+          if (dup2 (fd, STDERR_FILENO) == -1)
+            die_1 ("dup2 failed in child: %s", strerror (errno));
+         close (fd);
+        }
 
-      execl (pgmname, arg0, "--server", NULL); 
-      die ("exec failed for `%s': %s", pgmname, strerror (errno));
+      close (wp[1]);
+      close (rp[0]);
+      execl (pgmname, arg0, "--server", NULL);
+      die_2 ("exec failed for '%s': %s", pgmname, strerror (errno));
     }
   close (wp[0]);
   close (rp[1]);
@@ -386,7 +461,7 @@ start_server (const char *pgmname)
 
   read_assuan (server_recv_fd);
   if (recv_type != LINE_OK)
-    die ("no greating message");
+    die_0 ("no greating message");
 }
 
 
@@ -404,9 +479,9 @@ unset_var (const char *name)
     ;
   if (!var)
     return;
-/*    fprintf (stderr, "unsetting `%s'\n", name); */
+/*    fprintf (stderr, "unsetting '%s'\n", name); */
 
-  if (var->is_fd && var->value)
+  if (var->type == VARTYPE_FD && var->value)
     {
       int fd;
 
@@ -417,17 +492,18 @@ unset_var (const char *name)
 
   free (var->value);
   var->value = NULL;
-  var->is_fd = 0;
+  var->type = 0;
+  var->count = 0;
 }
 
 
 static void
-set_fd_var (const char *name, const char *value, int is_fd)
+set_type_var (const char *name, const char *value, VARTYPE type)
 {
   VARIABLE var;
 
   if (!name)
-    name = "?"; 
+    name = "?";
   for (var=variable_list; var && strcmp (var->name, name); var = var->next)
     ;
   if (!var)
@@ -438,9 +514,12 @@ set_fd_var (const char *name, const char *value, int is_fd)
       variable_list = var;
     }
   else
-    free (var->value);
+    {
+      free (var->value);
+      var->value = NULL;
+    }
 
-  if (var->is_fd && var->value)
+  if (var->type == VARTYPE_FD && var->value)
     {
       int fd;
 
@@ -448,17 +527,23 @@ set_fd_var (const char *name, const char *value, int is_fd)
       if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
           close (fd);
     }
-  
-  var->is_fd = is_fd;
-  var->value = xstrdup (value);
-/*    fprintf (stderr, "setting `%s' to `%s'\n", var->name, var->value); */
 
+  var->type = type;
+  var->count = 0;
+  if (var->type == VARTYPE_COUNTER)
+    {
+      /* We need some extra sapce as scratch area for get_var. */
+      var->value = xmalloc (strlen (value) + 1 + 20);
+      strcpy (var->value, value);
+    }
+  else
+    var->value = xstrdup (value);
 }
 
 static void
 set_var (const char *name, const char *value)
 {
-  set_fd_var (name, value, 0);
+  set_type_var (name, value, 0);
 }
 
 
@@ -469,10 +554,35 @@ get_var (const char *name)
 
   for (var=variable_list; var && strcmp (var->name, name); var = var->next)
     ;
-  return var? var->value:NULL;
+  if (!var)
+    return NULL;
+  if (var->type == VARTYPE_COUNTER && var->value)
+    { /* Use the scratch space allocated by set_var. */
+      char *p = var->value + strlen(var->value)+1;
+      sprintf (p, "%u", var->count);
+      return p;
+    }
+  else
+    return var->value;
 }
 
 
+/* Incremente all counter type variables with NAME in their VALUE. */
+static void
+inc_counter (const char *name)
+{
+  VARIABLE var;
+
+  if (!*name)
+    return;
+  for (var=variable_list; var; var = var->next)
+    {
+      if (var->type == VARTYPE_COUNTER
+          && var->value && !strcmp (var->value, name))
+        var->count++;
+    }
+}
+
 
 /* Expand variables in LINE and return a new allocated buffer if
    required.  The function might modify LINE if the expanded version
@@ -491,14 +601,15 @@ expand_line (char *buffer)
       p = strchr (line, '$');
       if (!p)
         return result; /* nothing more to expand */
-      
+
       if (p[1] == '$') /* quoted */
         {
           memmove (p, p+1, strlen (p+1)+1);
           line = p + 1;
           continue;
         }
-      for (pend=p+1; *pend && !spacep (pend) && *pend != '$'; pend++)
+      for (pend=p+1; *pend && !spacep (pend)
+           && *pend != '$' && *pend != '/'; pend++)
         ;
       if (*pend)
         {
@@ -542,7 +653,7 @@ expand_line (char *buffer)
 
 
 /* Evaluate COND and return the result. */
-static int 
+static int
 eval_boolean (const char *cond)
 {
   int true = 1;
@@ -568,47 +679,98 @@ cmd_let (const char *assign_to, char *arg)
 static void
 cmd_echo (const char *assign_to, char *arg)
 {
-  printf ("%s\n", arg);
+  (void)assign_to;
+  if (!opt_no_echo)
+    printf ("%s\n", arg);
 }
 
 static void
 cmd_send (const char *assign_to, char *arg)
 {
+  (void)assign_to;
   if (opt_verbose)
-    fprintf (stderr, "sending `%s'\n", arg);
-  write_assuan (server_send_fd, arg); 
+    fprintf (stderr, "sending '%s'\n", arg);
+  write_assuan (server_send_fd, arg);
+}
+
+static void
+handle_status_line (char *arg)
+{
+  char *p;
+
+  for (p=arg; *p && !spacep (p); p++)
+    ;
+  if (*p)
+    {
+      int save = *p;
+      *p = 0;
+      inc_counter (arg);
+      *p = save;
+    }
+  else
+    inc_counter (arg);
 }
 
 static void
 cmd_expect_ok (const char *assign_to, char *arg)
 {
+  (void)assign_to;
+  (void)arg;
+
   if (opt_verbose)
     fprintf (stderr, "expecting OK\n");
   do
     {
-      read_assuan (server_recv_fd);
-      if (opt_verbose)
-        fprintf (stderr, "got line `%s'\n", recv_line);
+      char *p = read_assuan (server_recv_fd);
+      if (opt_verbose > 1)
+        fprintf (stderr, "got line '%s'\n", recv_line);
+      if (recv_type == LINE_STAT)
+        handle_status_line (p);
     }
   while (recv_type != LINE_OK && recv_type != LINE_ERR);
   if (recv_type != LINE_OK)
-    die ("expected OK but got `%s'", recv_line);
+    die_1 ("expected OK but got '%s'", recv_line);
 }
 
 static void
 cmd_expect_err (const char *assign_to, char *arg)
 {
+  (void)assign_to;
+  (void)arg;
+
   if (opt_verbose)
     fprintf (stderr, "expecting ERR\n");
   do
     {
-      read_assuan (server_recv_fd);
-      if (opt_verbose)
-        fprintf (stderr, "got line `%s'\n", recv_line);
+      char *p = read_assuan (server_recv_fd);
+      if (opt_verbose > 1)
+        fprintf (stderr, "got line '%s'\n", recv_line);
+      if (recv_type == LINE_STAT)
+        handle_status_line (p);
     }
   while (recv_type != LINE_OK && recv_type != LINE_ERR);
   if (recv_type != LINE_ERR)
-    die ("expected ERR but got `%s'", recv_line);
+    die_1 ("expected ERR but got '%s'", recv_line);
+}
+
+static void
+cmd_count_status (const char *assign_to, char *arg)
+{
+  char *p;
+
+  if (!*assign_to || !*arg)
+    die_0 ("syntax error: count-status requires an argument and a variable");
+
+  for (p=arg; *p && !spacep (p); p++)
+    ;
+  if (*p)
+    {
+      for (*p++ = 0; spacep (p); p++)
+        ;
+      if (*p)
+        die_0 ("cmpfiles: syntax error");
+    }
+  set_type_var (assign_to, arg, VARTYPE_COUNTER);
 }
 
 static void
@@ -617,14 +779,14 @@ cmd_openfile (const char *assign_to, char *arg)
   int fd;
   char numbuf[20];
 
-  do 
+  do
     fd = open (arg, O_RDONLY);
   while (fd == -1 && errno == EINTR);
   if (fd == -1)
-    die ("error opening `%s': %s", arg, strerror (errno));
-  
+    die_2 ("error opening '%s': %s", arg, strerror (errno));
+
   sprintf (numbuf, "%d", fd);
-  set_fd_var (assign_to, numbuf, 1);
+  set_type_var (assign_to, numbuf, VARTYPE_FD);
 }
 
 static void
@@ -633,22 +795,24 @@ cmd_createfile (const char *assign_to, char *arg)
   int fd;
   char numbuf[20];
 
-  do 
+  do
     fd = open (arg, O_WRONLY|O_CREAT|O_TRUNC, 0666);
   while (fd == -1 && errno == EINTR);
   if (fd == -1)
-    die ("error creating `%s': %s", arg, strerror (errno));
+    die_2 ("error creating '%s': %s", arg, strerror (errno));
 
   sprintf (numbuf, "%d", fd);
-  set_fd_var (assign_to, numbuf, 1);
+  set_type_var (assign_to, numbuf, VARTYPE_FD);
 }
 
 
 static void
 cmd_pipeserver (const char *assign_to, char *arg)
 {
+  (void)assign_to;
+
   if (!*arg)
-    arg = "../sm/gpgsm";
+    die_0 ("syntax error: servername missing");
 
   start_server (arg);
 }
@@ -657,6 +821,8 @@ cmd_pipeserver (const char *assign_to, char *arg)
 static void
 cmd_quit_if(const char *assign_to, char *arg)
 {
+  (void)assign_to;
+
   if (eval_boolean (arg))
     exit (0);
 }
@@ -664,6 +830,8 @@ cmd_quit_if(const char *assign_to, char *arg)
 static void
 cmd_fail_if(const char *assign_to, char *arg)
 {
+  (void)assign_to;
+
   if (eval_boolean (arg))
     exit (1);
 }
@@ -680,11 +848,11 @@ cmd_cmpfiles (const char *assign_to, char *arg)
   size_t nread1, nread2;
   int rc = 0;
 
-  set_var (assign_to, "0"); 
+  set_var (assign_to, "0");
   for (p=arg; *p && !spacep (p); p++)
     ;
   if (!*p)
-    die ("cmpfiles: syntax error");
+    die_0 ("cmpfiles: syntax error");
   for (*p++ = 0; spacep (p); p++)
     ;
   second = p;
@@ -695,19 +863,19 @@ cmd_cmpfiles (const char *assign_to, char *arg)
       for (*p++ = 0; spacep (p); p++)
         ;
       if (*p)
-        die ("cmpfiles: syntax error");
+        die_0 ("cmpfiles: syntax error");
     }
-  
+
   fp1 = fopen (arg, "rb");
   if (!fp1)
     {
-      err ("can't open `%s': %s", arg, strerror (errno));
+      err ("can't open '%s': %s", arg, strerror (errno));
       return;
     }
   fp2 = fopen (second, "rb");
   if (!fp2)
     {
-      err ("can't open `%s': %s", second, strerror (errno));
+      err ("can't open '%s': %s", second, strerror (errno));
       fclose (fp1);
       return;
     }
@@ -728,7 +896,7 @@ cmd_cmpfiles (const char *assign_to, char *arg)
     {
       if (opt_verbose)
         err ("files match");
-      set_var (assign_to, "1"); 
+      set_var (assign_to, "1");
     }
   else if (!rc)
     err ("cmpfiles: read error: %s", strerror (errno));
@@ -738,6 +906,17 @@ cmd_cmpfiles (const char *assign_to, char *arg)
   fclose (fp2);
 }
 
+static void
+cmd_getenv (const char *assign_to, char *arg)
+{
+  const char *s;
+  s = *arg? getenv (arg):"";
+  set_var (assign_to, s? s:"");
+}
+
+
+
+\f
 /* Process the current script line LINE. */
 static int
 interpreter (char *line)
@@ -751,6 +930,7 @@ interpreter (char *line)
     { "send"      , cmd_send },
     { "expect-ok" , cmd_expect_ok },
     { "expect-err", cmd_expect_err },
+    { "count-status", cmd_count_status },
     { "openfile"  , cmd_openfile },
     { "createfile", cmd_createfile },
     { "pipeserver", cmd_pipeserver },
@@ -758,6 +938,7 @@ interpreter (char *line)
     { "quit-if"   , cmd_quit_if },
     { "fail-if"   , cmd_fail_if },
     { "cmpfiles"  , cmd_cmpfiles },
+    { "getenv"    , cmd_getenv },
     { NULL }
   };
   char *p, *save_p;
@@ -783,7 +964,6 @@ interpreter (char *line)
           return 0; /* empty or comment */
         }
     }
-
   for (p=line; *p && !spacep (p) && *p != '='; p++)
     ;
   if (*p == '=')
@@ -799,7 +979,7 @@ interpreter (char *line)
         assign_to = line;
     }
   if (!*line)
-    die ("syntax error");
+    die_0 ("syntax error");
   stmt = line;
   save_c = 0;
   save_p = NULL;
@@ -829,7 +1009,7 @@ interpreter (char *line)
   if (!cmdtbl[i].name)
     {
       if (!assign_to)
-        die ("invalid statement `%s'\n", stmt);
+        die_1 ("invalid statement '%s'\n", stmt);
       if (save_p)
         *save_p = save_c;
       set_var (assign_to, stmt);
@@ -871,7 +1051,9 @@ main (int argc, char **argv)
       if (*p != '-')
         break;
       if (!strcmp (p, "--verbose"))
-        opt_verbose = 1;
+        opt_verbose++;
+      else if (!strcmp (p, "--no-echo"))
+        opt_no_echo++;
       else if (*p == '-' && p[1] == 'D')
         {
           p += 2;
@@ -902,7 +1084,7 @@ main (int argc, char **argv)
     {
       p = strchr (buffer,'\n');
       if (!p)
-        die ("incomplete script line");
+        die_0 ("incomplete script line");
       *p = 0;
       if (interpreter (buffer))
         break;
@@ -910,4 +1092,3 @@ main (int argc, char **argv)
     }
   return 0;
 }
-