Support gpgme_op_apsswd for GPG.
[gpgme.git] / src / engine-gpg.c
index 3c06edf..abfaaa3 100644 (file)
@@ -1,7 +1,7 @@
 /* engine-gpg.c - Gpg Engine.
    Copyright (C) 2000 Werner Koch (dd9jn)
    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007,
-                 2009 g10 Code GmbH
+                 2009, 2010 g10 Code GmbH
  
    This file is part of GPGME.
  
@@ -646,10 +646,10 @@ gpg_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
 static gpgme_error_t
 command_handler (void *opaque, int fd)
 {
+  struct io_cb_data *data = (struct io_cb_data *) opaque;
+  engine_gpg_t gpg = (engine_gpg_t) data->handler_value;
   gpgme_error_t err;
-  engine_gpg_t gpg = (engine_gpg_t) opaque;
   int processed = 0;
-
   assert (gpg->cmd.used);
   assert (gpg->cmd.code);
   assert (gpg->cmd.fnc);
@@ -1139,7 +1139,8 @@ read_status (engine_gpg_t gpg)
 static gpgme_error_t
 status_handler (void *opaque, int fd)
 {
-  engine_gpg_t gpg = opaque;
+  struct io_cb_data *data = (struct io_cb_data *) opaque;
+  engine_gpg_t gpg = (engine_gpg_t) data->handler_value;
   int err;
 
   assert (fd == gpg->status.fd[0]);
@@ -1246,7 +1247,8 @@ read_colon_line (engine_gpg_t gpg)
 static gpgme_error_t
 colon_line_handler (void *opaque, int fd)
 {
-  engine_gpg_t gpg = opaque;
+  struct io_cb_data *data = (struct io_cb_data *) opaque;
+  engine_gpg_t gpg = (engine_gpg_t) data->handler_value;
   gpgme_error_t rc = 0;
 
   assert (fd == gpg->colon.fd[0]);
@@ -1330,7 +1332,7 @@ start (engine_gpg_t gpg)
   status = _gpgme_io_spawn (gpg->file_name ? gpg->file_name :
                            _gpgme_get_gpg_path (), gpg->argv,
                             IOSPAWN_FLAG_ALLOW_SET_FG,
-                            fd_list, &pid);
+                            fd_list, NULL, NULL, &pid);
   saved_errno = errno;
 
   free (fd_list);
@@ -1435,6 +1437,24 @@ gpg_delete (void *engine, gpgme_key_t key, int allow_secret)
 
 
 static gpgme_error_t
+gpg_passwd (void *engine, gpgme_key_t key, unsigned int flags)
+{
+  engine_gpg_t gpg = engine;
+  gpgme_error_t err;
+
+  if (!key || !key->subkeys || !key->subkeys->fpr)
+    return gpg_error (GPG_ERR_INV_CERT_OBJ);
+
+  err = add_arg (gpg, "--passwd");
+  if (!err)
+    err = add_arg (gpg, key->subkeys->fpr);
+  if (!err)
+    start (gpg);
+  return err;
+}
+
+
+static gpgme_error_t
 append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
 {
   gpgme_error_t err = 0;
@@ -1803,6 +1823,107 @@ gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
   return err;
 }
 
+/* Return the next DELIM delimited string from DATA as a C-string.
+   The caller needs to provide the address of a pointer variable which
+   he has to set to NULL before the first call.  After the last call
+   to this function, this function needs to be called once more with
+   DATA set to NULL so that the function can release its internal
+   state.  After that the pointer variable is free for use again.
+   Note that we use a delimiter and thus a trailing delimiter is not
+   required.  DELIM may not be changed after the first call. */
+static const char *
+string_from_data (gpgme_data_t data, int delim, 
+                  void **helpptr, gpgme_error_t *r_err)
+{
+#define MYBUFLEN 2000 /* Fixme: We don't support URLs longer than that.  */
+  struct {
+    int  eof_seen;
+    int  nbytes;      /* Length of the last returned string including
+                         the delimiter. */
+    int  buflen;      /* Valid length of BUF.  */
+    char buf[MYBUFLEN+1];  /* Buffer with one byte extra space.  */
+  } *self;
+  char *p;
+  int nread;
+
+  *r_err = 0;
+  if (!data)
+    {
+      if (*helpptr)
+        {
+          free (*helpptr);
+          *helpptr = NULL;
+        }
+      return NULL;
+    }
+
+  if (*helpptr)
+    self = *helpptr;
+  else
+    {
+      self = malloc (sizeof *self);
+      if (!self)
+        {
+          *r_err = gpg_error_from_syserror ();
+          return NULL;
+        }
+      *helpptr = self;
+      self->eof_seen = 0;
+      self->nbytes = 0;
+      self->buflen = 0;
+    }
+
+  if (self->eof_seen)
+    return NULL;
+
+  assert (self->nbytes <= self->buflen);
+  memmove (self->buf, self->buf + self->nbytes, self->buflen - self->nbytes);
+  self->buflen -= self->nbytes;
+  self->nbytes = 0;
+
+  do
+    {
+      /* Fixme: This is fairly infective scanning because we may scan
+         the buffer several times.  */
+      p = memchr (self->buf, delim, self->buflen);
+      if (p)
+        {
+          *p = 0;
+          self->nbytes = p - self->buf + 1;
+          return self->buf;
+        }
+
+      if ( !(MYBUFLEN - self->buflen) )
+        {
+          /* Not enough space - URL too long.  */
+          *r_err = gpg_error (GPG_ERR_TOO_LARGE);
+          return NULL;
+        }
+
+      nread = gpgme_data_read (data, self->buf + self->buflen, 
+                               MYBUFLEN - self->buflen);
+      if (nread < 0)
+        {
+          *r_err = gpg_error_from_syserror ();
+          return NULL;
+        }
+      self->buflen += nread;
+    }
+  while (nread);
+
+  /* EOF reached.  If we have anything in the buffer, append a Nul and
+     return it. */
+  self->eof_seen = 1;
+  if (self->buflen)
+    {
+      self->buf[self->buflen] = 0;  /* (we allocated one extra byte)  */
+      return self->buf;
+    }
+  return NULL;
+#undef MYBUFLEN
+}
+
+
 
 static gpgme_error_t
 gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
@@ -1810,9 +1931,12 @@ gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
   engine_gpg_t gpg = engine;
   gpgme_error_t err;
   int idx;
+  gpgme_data_encoding_t dataenc;
 
   if (keydata && keyarray)
-    gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed.  */
+    return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed.  */
+
+  dataenc = gpgme_data_get_encoding (keydata);
 
   if (keyarray)
     {
@@ -1831,6 +1955,38 @@ gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
             err = add_arg (gpg, keyarray[idx]->subkeys->keyid);
         }
     }
+  else if (dataenc == GPGME_DATA_ENCODING_URL
+           || dataenc == GPGME_DATA_ENCODING_URL0)
+    {
+      void *helpptr;
+      const char *string;
+      gpgme_error_t xerr;
+      int delim = (dataenc == GPGME_DATA_ENCODING_URL)? '\n': 0;
+
+      /* FIXME: --fetch-keys is probably not correct because it can't
+         grok all kinds of URLs.  On Unix it should just work but on
+         Windows we will build the command line and that may fail for
+         some embedded control characters.  It is anyway limited to
+         the maximum size of the command line.  We need another
+         command which can take its input from a file.  Maybe we
+         should use an option to gpg to modify such commands (ala
+         --multifile).  */
+      err = add_arg (gpg, "--fetch-keys");
+      if (!err)
+        err = add_arg (gpg, "--");
+      helpptr = NULL;
+      while (!err
+             && (string = string_from_data (keydata, delim, &helpptr, &xerr)))
+        err = add_arg (gpg, string);
+      if (!err)
+        err = xerr;
+      string_from_data (NULL, delim, &helpptr, &xerr);
+    }
+  else if (dataenc == GPGME_DATA_ENCODING_URLESC)
+    {
+      /* Already escaped URLs are not yet supported.  */
+      err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+    }
   else
     {
       err = add_arg (gpg, "--import");
@@ -2004,8 +2160,8 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
                  gpg >= 2.0.10.  FIXME: We should check that we have
                  such a version to that we can return a proper error
                  code.  The problem is that we don't know the context
-                 here and thus can't accesses the cached version
-                 number for the engine info structure.  */
+                 here and thus can't access the cached version number
+                 for the engine info structure.  */
               err = add_arg (gpg, "--locate-keys");
               if ((mode & GPGME_KEYLIST_MODE_SIGS))
                 err = add_arg (gpg, "--with-sig-check");
@@ -2209,7 +2365,9 @@ struct engine_ops _gpgme_engine_ops_gpg =
     gpg_set_command_handler,
     gpg_set_colon_line_handler,
     gpg_set_locale,
+    NULL,                              /* set_protocol */
     gpg_decrypt,
+    gpg_decrypt,                       /* decrypt_verify */
     gpg_delete,
     gpg_edit,
     gpg_encrypt,
@@ -2229,5 +2387,7 @@ struct engine_ops _gpgme_engine_ops_gpg =
     NULL,              /* conf_save */
     gpg_set_io_cbs,
     gpg_io_event,
-    gpg_cancel
+    gpg_cancel,
+    NULL,              /* cancel_op */
+    gpg_passwd
   };