Doc fixes, moved some fizmes to TODO, fixed minor bugs.
[gnupg.git] / sm / server.c
index f1d0031..9ec4834 100644 (file)
@@ -1,5 +1,5 @@
 /* server.c - Server mode and main entry point 
- *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *     Copyright (C) 2001, 2002 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdarg.h>
 #include <ctype.h>
 #include <unistd.h>
 
@@ -42,46 +43,44 @@ struct server_local_s {
   CERTLIST recplist;
 };
 
-/* Map GNUPG_xxx error codes to Assuan status codes */
-static int
-rc_to_assuan_status (int rc)
+
+
+/* note, that it is sufficient to allocate the target string D as
+   long as the source string S, i.e.: strlen(s)+1; */
+static void
+strcpy_escaped_plus (char *d, const unsigned char *s)
 {
-  switch (rc)
+  while (*s)
     {
-    case 0: break;
-    case GNUPG_Bad_Certificate:   rc = ASSUAN_Bad_Certificate; break;
-    case GNUPG_Bad_Certificate_Path: rc = ASSUAN_Bad_Certificate_Path; break;
-    case GNUPG_Missing_Certificate: rc = ASSUAN_Missing_Certificate; break;
-    case GNUPG_No_Data:           rc = ASSUAN_No_Data_Available; break;
-    case GNUPG_Bad_Signature:     rc = ASSUAN_Bad_Signature; break;
-    case GNUPG_Not_Implemented:   rc = ASSUAN_Not_Implemented; break;
-    case GNUPG_No_Agent:          rc = ASSUAN_No_Agent; break;
-    case GNUPG_Agent_Error:       rc = ASSUAN_Agent_Error; break;
-    case GNUPG_No_Public_Key:     rc = ASSUAN_No_Public_Key; break;
-    case GNUPG_No_Secret_Key:     rc = ASSUAN_No_Secret_Key; break;
-    case GNUPG_Invalid_Data:      rc = ASSUAN_Invalid_Data; break;
-    case GNUPG_Invalid_Name:      rc = ASSUAN_Invalid_Name; break;
-
-    case GNUPG_Read_Error: 
-    case GNUPG_Write_Error:
-    case GNUPG_IO_Error: 
-      rc = ASSUAN_Server_IO_Error;
-      break;
-    case GNUPG_Out_Of_Core:    
-    case GNUPG_Resource_Limit: 
-      rc = ASSUAN_Server_Resource_Problem;
-      break;
-    case GNUPG_Bug: 
-    case GNUPG_Internal_Error:   
-      rc = ASSUAN_Server_Bug;
-      break;
-    default: 
-      rc = ASSUAN_Server_Fault;
-      break;
+      if (*s == '%' && s[1] && s[2])
+        { 
+          s++;
+          *d++ = xtoi_2 ( s);
+          s += 2;
+        }
+      else if (*s == '+')
+        *d++ = ' ', s++;
+      else
+        *d++ = *s++;
     }
-  return rc;
+  *d = 0; 
+}
+
+
+
+
+/* Check whether the option NAME appears in LINE */
+static int
+has_option (const char *line, const char *name)
+{
+  const char *s;
+  int n = strlen (name);
+
+  s = strstr (line, name);
+  return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
 }
 
+
 static void 
 close_message_fd (CTRL ctrl)
 {
@@ -92,6 +91,28 @@ close_message_fd (CTRL ctrl)
     }
 }
 
+
+static int
+option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
+{
+  CTRL ctrl = assuan_get_pointer (ctx);
+
+  if (!strcmp (key, "include-certs"))
+    {
+      int i = *value? atoi (value) : -1;
+      if (ctrl->include_certs < -2)
+        return ASSUAN_Parameter_Error;
+      ctrl->include_certs = i;
+    }
+  else
+    return ASSUAN_Invalid_Option;
+
+  return 0;
+}
+
+
+
+
 static void
 reset_notify (ASSUAN_CONTEXT ctx)
 {
@@ -100,6 +121,8 @@ reset_notify (ASSUAN_CONTEXT ctx)
   gpgsm_release_certlist (ctrl->server_local->recplist);
   ctrl->server_local->recplist = NULL;
   close_message_fd (ctrl);
+  assuan_close_input_fd (ctx);
+  assuan_close_output_fd (ctx);
 }
 
 
@@ -154,8 +177,13 @@ cmd_recipient (ASSUAN_CONTEXT ctx, char *line)
   int rc;
 
   rc = gpgsm_add_to_certlist (line, &ctrl->server_local->recplist);
+  if (rc)
+    gpgsm_status2 (ctrl, STATUS_INV_RECP,
+                   rc == -1? "1":
+                   rc == GNUPG_Ambiguous_Name? "2 ": "0 ",
+                   line, NULL);
 
-  return rc_to_assuan_status (rc);
+  return map_to_assuan_status (rc);
 }
 
 
@@ -195,16 +223,13 @@ cmd_encrypt (ASSUAN_CONTEXT ctx, char *line)
                       inp_fd, out_fp);
   fclose (out_fp);
 
-  if (!rc)
-    {
-      gpgsm_release_certlist (ctrl->server_local->recplist);
-      ctrl->server_local->recplist = NULL;
-      /* close and reset the fd */
-      close_message_fd (ctrl);
-      assuan_close_input_fd (ctx);
-      assuan_close_output_fd (ctx);
-    }
-  return rc_to_assuan_status (rc);
+  gpgsm_release_certlist (ctrl->server_local->recplist);
+  ctrl->server_local->recplist = NULL;
+  /* close and reset the fd */
+  close_message_fd (ctrl);
+  assuan_close_input_fd (ctx);
+  assuan_close_output_fd (ctx);
+  return map_to_assuan_status (rc);
 }
 
 /* DECRYPT
@@ -235,15 +260,12 @@ cmd_decrypt (ASSUAN_CONTEXT ctx, char *line)
   rc = gpgsm_decrypt (ctrl, inp_fd, out_fp); 
   fclose (out_fp);
 
-  if (!rc)
-    {
-      /* close and reset the fd */
-      close_message_fd (ctrl);
-      assuan_close_input_fd (ctx);
-      assuan_close_output_fd (ctx);
-    }
+  /* close and reset the fd */
+  close_message_fd (ctrl);
+  assuan_close_input_fd (ctx);
+  assuan_close_output_fd (ctx);
 
-  return rc_to_assuan_status (rc);
+  return map_to_assuan_status (rc);
 }
 
 
@@ -262,21 +284,30 @@ cmd_verify (ASSUAN_CONTEXT ctx, char *line)
   int rc;
   CTRL ctrl = assuan_get_pointer (ctx);
   int fd = assuan_get_input_fd (ctx);
+  int out_fd = assuan_get_output_fd (ctx);
+  FILE *out_fp = NULL;
 
   if (fd == -1)
     return set_error (No_Input, NULL);
 
-  rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
-                     ctrl->server_local->message_fd);
-  if (!rc)
+  if (out_fd != -1)
     {
-      /* close and reset the fd */
-      close_message_fd (ctrl);
-      assuan_close_input_fd (ctx);
-      assuan_close_output_fd (ctx);
+      out_fp = fdopen ( dup(out_fd), "w");
+      if (!out_fp)
+        return set_error (General_Error, "fdopen() failed");
     }
 
-  return rc_to_assuan_status (rc);
+  rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
+                     ctrl->server_local->message_fd, out_fp);
+  if (out_fp)
+    fclose (out_fp);
+
+  /* close and reset the fd */
+  close_message_fd (ctrl);
+  assuan_close_input_fd (ctx);
+  assuan_close_output_fd (ctx);
+
+  return map_to_assuan_status (rc);
 }
 
 
@@ -301,7 +332,7 @@ cmd_sign (ASSUAN_CONTEXT ctx, char *line)
   if (out_fd == -1)
     return set_error (No_Output, NULL);
 
-  detached = !!strstr (line, "--detached");  /* fixme: this is ambiguous */
+  detached = has_option (line, "--detached"); 
 
   out_fp = fdopen ( dup(out_fd), "w");
   if (!out_fp)
@@ -309,15 +340,12 @@ cmd_sign (ASSUAN_CONTEXT ctx, char *line)
   rc = gpgsm_sign (assuan_get_pointer (ctx), inp_fd, detached, out_fp);
   fclose (out_fp);
 
-  if (!rc)
-    {
-      /* close and reset the fd */
-      close_message_fd (ctrl);
-      assuan_close_input_fd (ctx);
-      assuan_close_output_fd (ctx);
-    }
+  /* close and reset the fd */
+  close_message_fd (ctrl);
+  assuan_close_input_fd (ctx);
+  assuan_close_output_fd (ctx);
 
-  return rc_to_assuan_status (rc);
+  return map_to_assuan_status (rc);
 }
 
 
@@ -339,14 +367,12 @@ cmd_import (ASSUAN_CONTEXT ctx, char *line)
 
   rc = gpgsm_import (assuan_get_pointer (ctx), fd);
 
-  if (!rc)
-    {
-      /* close and reset the fd */
-      close_message_fd (ctrl);
-      assuan_close_input_fd (ctx);
-      assuan_close_output_fd (ctx);
-    }
-  return rc_to_assuan_status (rc);
+  /* close and reset the fd */
+  close_message_fd (ctrl);
+  assuan_close_input_fd (ctx);
+  assuan_close_output_fd (ctx);
+
+  return map_to_assuan_status (rc);
 }
 
 /* MESSAGE FD=<n>
@@ -375,19 +401,93 @@ cmd_message (ASSUAN_CONTEXT ctx, char *line)
   return 0;
 }
 
+
 static int 
-cmd_listkeys (ASSUAN_CONTEXT ctx, char *line)
+do_listkeys (ASSUAN_CONTEXT ctx, char *line, int mode)
 {
   CTRL ctrl = assuan_get_pointer (ctx);
+  FILE *fp = assuan_get_data_fp (ctx);
+  char *p;
+  STRLIST list, sl;
 
-  ctrl->with_colons = 1;
-  /* fixme: check that the returned data_fp is not NULL */
-  gpgsm_list_keys (assuan_get_pointer (ctx), NULL,
-                        assuan_get_data_fp (ctx));
+  if (!fp)
+    return set_error (General_Error, "no data stream");
+  
+  /* break the line down into an STRLIST */
+  list = NULL;
+  for (p=line; *p; line = p)
+    {
+      while (*p && *p != ' ')
+        p++;
+      if (*p)
+        *p++ = 0;
+      if (*line)
+        {
+          sl = xtrymalloc (sizeof *sl + strlen (line));
+          if (!sl)
+            {
+              free_strlist (list);
+              return ASSUAN_Out_Of_Core;
+            }
+          sl->flags = 0;
+          strcpy_escaped_plus (sl->d, line);
+          sl->next = list;
+          list = sl;
+        }
+    }
 
+  ctrl->with_colons = 1;
+  gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, 3);
+  free_strlist (list);
   return 0;
 }
 
+static int 
+cmd_listkeys (ASSUAN_CONTEXT ctx, char *line)
+{
+  return do_listkeys (ctx, line, 3);
+}
+
+static int 
+cmd_listsecretkeys (ASSUAN_CONTEXT ctx, char *line)
+{
+  return do_listkeys (ctx, line, 2);
+}
+
+\f
+/* GENKEY
+
+   Read the parameters in native format from the input fd and write a
+   certificate request to the output.
+ */
+static int 
+cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
+{
+  CTRL ctrl = assuan_get_pointer (ctx);
+  int inp_fd, out_fd;
+  FILE *out_fp;
+  int rc;
+
+  inp_fd = assuan_get_input_fd (ctx);
+  if (inp_fd == -1)
+    return set_error (No_Input, NULL);
+  out_fd = assuan_get_output_fd (ctx);
+  if (out_fd == -1)
+    return set_error (No_Output, NULL);
+
+  out_fp = fdopen ( dup(out_fd), "w");
+  if (!out_fp)
+    return set_error (General_Error, "fdopen() failed");
+  rc = gpgsm_genkey (ctrl, inp_fd, out_fp);
+  fclose (out_fp);
+
+  /* close and reset the fds */
+  assuan_close_input_fd (ctx);
+  assuan_close_output_fd (ctx);
+
+  return map_to_assuan_status (rc);
+}
+
 
 
 
@@ -411,6 +511,8 @@ register_commands (ASSUAN_CONTEXT ctx)
     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
     { "MESSAGE",    0,  cmd_message },
     { "LISTKEYS",   0,  cmd_listkeys },
+    { "LISTSECRETKEYS",  0,  cmd_listsecretkeys },
+    { "GENKEY",     0,  cmd_genkey },
     { NULL }
   };
   int i, j, rc;
@@ -437,6 +539,7 @@ gpgsm_server (void)
   struct server_control_s ctrl;
 
   memset (&ctrl, 0, sizeof ctrl);
+  gpgsm_init_default_ctrl (&ctrl);
 
   /* For now we use a simple pipe based server so that we can work
      from scripts.  We will later add options to run as a daemon and
@@ -462,12 +565,16 @@ gpgsm_server (void)
   assuan_register_reset_notify (ctx, reset_notify);
   assuan_register_input_notify (ctx, input_notify);
   assuan_register_output_notify (ctx, output_notify);
+  assuan_register_option_handler (ctx, option_handler);
 
   assuan_set_pointer (ctx, &ctrl);
   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
   ctrl.server_local->assuan_ctx = ctx;
   ctrl.server_local->message_fd = -1;
 
+  if (DBG_AGENT)
+    assuan_set_log_stream (ctx, log_get_stream ());
+
   for (;;)
     {
       rc = assuan_accept (ctx);
@@ -492,7 +599,7 @@ gpgsm_server (void)
   gpgsm_release_certlist (ctrl.server_local->recplist);
   ctrl.server_local->recplist = NULL;
 
-  assuan_deinit_pipe_server (ctx);
+  assuan_deinit_server (ctx);
 }
 
 
@@ -571,10 +678,14 @@ get_status_string ( int no )
 }
 
 
-
 void
-gpgsm_status (CTRL ctrl, int no, const char *text)
+gpgsm_status2 (CTRL ctrl, int no, ...)
 {
+  va_list arg_ptr;
+  const char *text;
+
+  va_start (arg_ptr, no);
+
   if (ctrl->no_server)
     {
       if (ctrl->status_fd == -1)
@@ -598,7 +709,7 @@ gpgsm_status (CTRL ctrl, int no, const char *text)
       fputs ("[GNUPG:] ", statusfp);
       fputs (get_status_string (no), statusfp);
     
-      if (text)
+      while ( (text = va_arg (arg_ptr, const char*) ))
         {
           putc ( ' ', statusfp );
           for (; *text; text++) 
@@ -617,11 +728,30 @@ gpgsm_status (CTRL ctrl, int no, const char *text)
   else 
     {
       ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
+      char buf[950], *p;
+      size_t n;
 
-      assuan_write_status (ctx, get_status_string (no), text);
+      p = buf; 
+      n = 0;
+      while ( (text = va_arg (arg_ptr, const char *)) )
+        {
+          for ( ; *text && n < DIM (buf)-1; n++)
+            *p++ = *text++;
+        }
+      *p = 0;
+      assuan_write_status (ctx, get_status_string (no), buf);
     }
+
+  va_end (arg_ptr);
 }
 
+void
+gpgsm_status (CTRL ctrl, int no, const char *text)
+{
+  gpgsm_status2 (ctrl, no, text, NULL);
+}
+
+
 
 #if 0
 /*