2002-02-02 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / sign.c
index 5b6c61e..0c2514c 100644 (file)
@@ -1,5 +1,6 @@
 /* sign.c -  signing functions
  *     Copyright (C) 2000 Werner Koch (dd9jn)
+ *      Copyright (C) 2001 g10 Code GmbH
  *
  * This file is part of GPGME.
  *
 #include "context.h"
 #include "ops.h"
 
+#define SKIP_TOKEN_OR_RETURN(a) do { \
+    while (*(a) && *(a) != ' ') (a)++; \
+    while (*(a) == ' ') (a)++; \
+    if (!*(a)) \
+        return; /* oops */ \
+} while (0)
 
-struct  sign_result_s {
-    int no_passphrase;
-    int okay;
-    void *last_pw_handle;
-    char *userid_hint;
-    char *passphrase_info;
-    int bad_passphrase;
+struct sign_result_s
+{
+  int okay;
+  GpgmeData xmlinfo;
 };
 
-
 void
-_gpgme_release_sign_result ( SignResult res )
+_gpgme_release_sign_result (SignResult result)
 {
-    xfree (res->userid_hint);
-    xfree (res->passphrase_info);
-    xfree (res);
+  if (!result)
+    return;
+  gpgme_data_release (result->xmlinfo);
+  xfree (result);
 }
 
-
-
+/* Parse the args and save the information 
+ * <type> <pubkey algo> <hash algo> <class> <timestamp> <key fpr>
+ * in an XML structure.  With args of NULL the xml structure is closed.
+ */
 static void
-sign_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args )
+append_xml_siginfo (GpgmeData *rdh, char *args)
 {
-    if ( ctx->out_of_core )
-        return;
-    if ( ctx->result_type == RESULT_TYPE_NONE ) {
-        assert ( !ctx->result.sign );
-        ctx->result.sign = xtrycalloc ( 1, sizeof *ctx->result.sign );
-        if ( !ctx->result.sign ) {
-            ctx->out_of_core = 1;
-            return;
+  GpgmeData dh;
+  char helpbuf[100];
+  int i;
+  char *s;
+  unsigned long ul;
+
+  if (!*rdh)
+    {
+      if (gpgme_data_new (rdh))
+       {
+         return; /* fixme: We are ignoring out-of-core */
         }
-        ctx->result_type = RESULT_TYPE_SIGN;
+      dh = *rdh;
+      _gpgme_data_append_string (dh, "<GnupgOperationInfo>\n");
     }
-    assert ( ctx->result_type == RESULT_TYPE_SIGN );
-
-    switch (code) {
-      case STATUS_EOF:
-        break;
-
-      case STATUS_USERID_HINT:
-        xfree (ctx->result.sign->userid_hint);
-        if (!(ctx->result.sign->userid_hint = xtrystrdup (args)) )
-            ctx->out_of_core = 1;
-        break;
-
-      case STATUS_BAD_PASSPHRASE:
-        ctx->result.sign->bad_passphrase++;
-        break;
-
-      case STATUS_GOOD_PASSPHRASE:
-        ctx->result.sign->bad_passphrase = 0;
-        break;
-
-      case STATUS_NEED_PASSPHRASE:
-      case STATUS_NEED_PASSPHRASE_SYM:
-        xfree (ctx->result.sign->passphrase_info);
-        if (!(ctx->result.sign->passphrase_info = xtrystrdup (args)) )
-            ctx->out_of_core = 1;
-        break;
-
-      case STATUS_MISSING_PASSPHRASE:
-        DEBUG0 ("missing passphrase - stop\n");
-        ctx->result.sign->no_passphrase = 1;
-        break;
-
-      case STATUS_SIG_CREATED: 
-        /* fixme: we have no error return for multiple signatures */
-        ctx->result.sign->okay =1;
-        /* parse the line and save the information 
-         * <type> <pubkey algo> <hash algo> <class> <timestamp> <key fpr>
-         */
-        break;
-
-      default:
-        break;
+  else
+    {
+      dh = *rdh;
+      _gpgme_data_append_string (dh, "  </signature>\n");
     }
-}
-
-static const char *
-command_handler ( void *opaque, GpgStatusCode code, const char *key )
-{
-    GpgmeCtx c = opaque;
 
-    if ( c->result_type == RESULT_TYPE_NONE ) {
-        assert ( !c->result.sign );
-        c->result.sign = xtrycalloc ( 1, sizeof *c->result.sign );
-        if ( !c->result.sign ) {
-            c->out_of_core = 1;
-            return NULL;
-        }
-        c->result_type = RESULT_TYPE_SIGN;
+  if (!args)
+    {
+      /* Just close the XML containter.  */
+      _gpgme_data_append_string (dh, "</GnupgOperationInfo>\n");
+      return;
     }
 
-    if ( !code ) {
-        /* We have been called for cleanup */
-        if ( c->passphrase_cb ) { 
-            /* Fixme: take the key in account */
-            c->passphrase_cb (c->passphrase_cb_value, 0, 
-                              &c->result.sign->last_pw_handle );
-        }
-        
-        return NULL;
+  _gpgme_data_append_string (dh, "  <signature>\n");
+    
+  _gpgme_data_append_string (dh,
+                            *args == 'D' ? "    <detached/>\n" :
+                            *args == 'C' ? "    <cleartext/>\n" :
+                            *args == 'S' ? "    <standard/>\n" : "");
+  SKIP_TOKEN_OR_RETURN (args);
+
+  sprintf (helpbuf, "    <algo>%d</algo>\n", atoi (args));
+  _gpgme_data_append_string (dh, helpbuf);
+  SKIP_TOKEN_OR_RETURN (args);
+
+  i = atoi (args);
+  sprintf (helpbuf, "    <hashalgo>%d</hashalgo>\n", atoi (args));
+  _gpgme_data_append_string (dh, helpbuf);
+  switch (i)
+    {
+    case  1: s = "pgp-md5"; break;
+    case  2: s = "pgp-sha1"; break;
+    case  3: s = "pgp-ripemd160"; break;
+    case  5: s = "pgp-md2"; break;
+    case  6: s = "pgp-tiger192"; break;
+    case  7: s = "pgp-haval-5-160"; break;
+    case  8: s = "pgp-sha256"; break;
+    case  9: s = "pgp-sha384"; break;
+    case 10: s = "pgp-sha512"; break;
+    default: s = "pgp-unknown"; break;
     }
-
-    if ( !key || !c->passphrase_cb )
-        return NULL;
+  sprintf (helpbuf, "    <micalg>%s</micalg>\n", s);
+  _gpgme_data_append_string (dh,helpbuf);
+  SKIP_TOKEN_OR_RETURN (args);
     
-    if ( code == STATUS_GET_HIDDEN && !strcmp (key, "passphrase.enter") ) {
-        const char *userid_hint = c->result.sign->userid_hint;
-        const char *passphrase_info = c->result.sign->passphrase_info;
-        int bad_passphrase = c->result.sign->bad_passphrase;
-        char *buf;
-        const char *s;
+  sprintf (helpbuf, "    <sigclass>%.2s</sigclass>\n", args);
+  _gpgme_data_append_string (dh, helpbuf);
+  SKIP_TOKEN_OR_RETURN (args);
+
+  ul = strtoul (args, NULL, 10);
+  sprintf (helpbuf, "    <created>%lu</created>\n", ul);
+  _gpgme_data_append_string (dh, helpbuf);
+  SKIP_TOKEN_OR_RETURN (args);
+
+  /* Count the length of the finperprint.  */
+  for (i = 0; args[i] && args[i] != ' '; i++)
+    ;
+  _gpgme_data_append_string (dh, "    <fpr>");
+  _gpgme_data_append (dh, args, i);
+  _gpgme_data_append_string (dh, "</fpr>\n");
+}
 
-        c->result.sign->bad_passphrase = 0;
-        if (!userid_hint)
-            userid_hint = "[User ID hint missing]";
-        if (!passphrase_info)
-            passphrase_info = "[passphrase info missing]";
-        buf = xtrymalloc ( 20 + strlen (userid_hint)
-                           + strlen (passphrase_info) + 3);
-        if (!buf) {
-            c->out_of_core = 1;
-            return NULL;
+static void
+sign_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args)
+{
+  _gpgme_passphrase_status_handler (ctx, code, args);
+
+  if (ctx->error)
+    return;
+  test_and_allocate_result (ctx, sign);
+
+  switch (code)
+    {
+    case STATUS_EOF:
+      if (ctx->result.sign->okay)
+       {
+         append_xml_siginfo (&ctx->result.sign->xmlinfo, NULL);
+         _gpgme_set_op_info (ctx, ctx->result.sign->xmlinfo);
+         ctx->result.sign->xmlinfo = NULL;
         }
-        sprintf (buf, "%s\n%s\n%s",
-                 bad_passphrase? "TRY_AGAIN":"ENTER",
-                 userid_hint, passphrase_info );
-
-        s = c->passphrase_cb (c->passphrase_cb_value,
-                              buf, &c->result.sign->last_pw_handle );
-        xfree (buf);
-        return s;
+      if (!ctx->error && !ctx->result.sign->okay)
+       ctx->error = mk_error (No_Data); /* Hmmm: choose a better error? */
+      break;
+
+    case STATUS_SIG_CREATED: 
+      /* FIXME: We have no error return for multiple signatures.  */
+      append_xml_siginfo (&ctx->result.sign->xmlinfo, args);
+      ctx->result.sign->okay = 1;
+      break;
+
+    default:
+      break;
     }
-    
-    return NULL;
 }
 
-
 GpgmeError
-gpgme_op_sign_start ( GpgmeCtx c, GpgmeData in, GpgmeData out,
-                      GpgmeSigMode mode )
+gpgme_op_sign_start (GpgmeCtx ctx, GpgmeData in, GpgmeData out,
+                    GpgmeSigMode mode)
 {
-    int rc = 0;
-    int i;
-    GpgmeKey key;
+  GpgmeError err = 0;
 
-    fail_on_pending_request( c );
-    c->pending = 1;
+  fail_on_pending_request (ctx);
+  ctx->pending = 1;
 
-    _gpgme_release_result (c);
-    c->out_of_core = 0;
+  _gpgme_release_result (ctx);
 
-
-    if ( mode != GPGME_SIG_MODE_NORMAL
-         && mode != GPGME_SIG_MODE_DETACH
-         && mode != GPGME_SIG_MODE_CLEAR )
-        return mk_error (Invalid_Value);
+  if (mode != GPGME_SIG_MODE_NORMAL
+      && mode != GPGME_SIG_MODE_DETACH
+      && mode != GPGME_SIG_MODE_CLEAR)
+    return mk_error (Invalid_Value);
         
-    /* create a process object */
-    _gpgme_gpg_release (c->gpg);
-    c->gpg = NULL;
-    rc = _gpgme_gpg_new ( &c->gpg );
-    if (rc)
-        goto leave;
-
-    _gpgme_gpg_set_status_handler ( c->gpg, sign_status_handler, c );
-    if (c->passphrase_cb) {
-        rc = _gpgme_gpg_set_command_handler ( c->gpg, command_handler, c );
-        if (rc)
-            goto leave;
-    }
-
-    /* build the commandline */
-    if ( mode == GPGME_SIG_MODE_CLEAR ) {
-        _gpgme_gpg_add_arg ( c->gpg, "--clearsign" );
-    }
-    else {
-        _gpgme_gpg_add_arg ( c->gpg, "--sign" );
-        if ( mode == GPGME_SIG_MODE_DETACH )
-            _gpgme_gpg_add_arg ( c->gpg, "--detach" );
-        if ( c->use_armor )
-            _gpgme_gpg_add_arg ( c->gpg, "--armor" );
-        if ( c->use_textmode )
-            _gpgme_gpg_add_arg ( c->gpg, "--textmode" );
+  /* Create a process object.  */
+  _gpgme_engine_release (ctx->engine);
+  ctx->engine = NULL;
+  err = _gpgme_engine_new (ctx->use_cms ? GPGME_PROTOCOL_CMS
+                          : GPGME_PROTOCOL_OpenPGP, &ctx->engine);
+  if (err)
+    goto leave;
+
+  /* Check the supplied data.  */
+  if (gpgme_data_get_type (in) == GPGME_DATA_TYPE_NONE)
+    {
+      err = mk_error (No_Data);
+      goto leave;
     }
-    for (i=0; i < c->verbosity; i++)
-        _gpgme_gpg_add_arg ( c->gpg, "--verbose" );
-    for (i=0; (key = gpgme_signers_enum (c, i)); i++ ) {
-        const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEYID,
-                                                   NULL, 0);
-        if (s) {
-            _gpgme_gpg_add_arg (c->gpg, "-u");
-            _gpgme_gpg_add_arg (c->gpg, s);
-        }
-        gpgme_key_unref (key);
+  _gpgme_data_set_mode (in, GPGME_DATA_MODE_OUT);
+  if (!out || gpgme_data_get_type (out) != GPGME_DATA_TYPE_NONE)
+    {
+      err = mk_error (Invalid_Value);
+      goto leave;
     }
+  _gpgme_data_set_mode (out, GPGME_DATA_MODE_IN);
 
-    
-    /* Check the supplied data */
-    if ( gpgme_data_get_type (in) == GPGME_DATA_TYPE_NONE ) {
-        rc = mk_error (No_Data);
-        goto leave;
-    }
-    _gpgme_data_set_mode (in, GPGME_DATA_MODE_OUT );
-    if ( !out || gpgme_data_get_type (out) != GPGME_DATA_TYPE_NONE ) {
-        rc = mk_error (Invalid_Value);
-        goto leave;
-    }
-    _gpgme_data_set_mode (out, GPGME_DATA_MODE_IN );
+  err = _gpgme_passphrase_start (ctx);
+  if (err)
+    goto leave;
 
-    /* Tell the gpg object about the data */
-    _gpgme_gpg_add_data ( c->gpg, in, 0 );
-    _gpgme_gpg_add_data ( c->gpg, out, 1 );
+  _gpgme_engine_set_status_handler (ctx->engine, sign_status_handler, ctx);
+  _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
 
-    /* and kick off the process */
-    rc = _gpgme_gpg_spawn ( c->gpg, c );
+  _gpgme_engine_op_sign (ctx->engine, in, out, mode, ctx->use_armor,
+                        ctx->use_textmode, ctx /* FIXME */);
 
+  /* And kick off the process.  */
+  err = _gpgme_engine_start (ctx->engine, ctx);
+  
  leave:
-    if (rc) {
-        c->pending = 0; 
-        _gpgme_gpg_release ( c->gpg ); c->gpg = NULL;
+  if (err)
+    {
+      ctx->pending = 0; 
+      _gpgme_engine_release (ctx->engine);
+      ctx->engine = NULL;
     }
-    return rc;
+  return err;
 }
 
-
 /**
  * gpgme_op_sign:
- * @c: The context
+ * @ctx: The context
  * @in: Data to be signed
  * @out: Detached signature
  * @mode: Signature creation mode
  * 
  * Create a detached signature for @in and write it to @out.
  * The data will be signed using either the default key or the ones
- * defined through @c.
+ * defined through @ctx.
  * The defined modes for signature create are:
  * <literal>
  * GPGME_SIG_MODE_NORMAL (or 0) 
@@ -279,32 +251,13 @@ gpgme_op_sign_start ( GpgmeCtx c, GpgmeData in, GpgmeData out,
  * Return value: 0 on success or an error code.
  **/
 GpgmeError
-gpgme_op_sign ( GpgmeCtx c, GpgmeData in, GpgmeData out, GpgmeSigMode mode )
+gpgme_op_sign (GpgmeCtx ctx, GpgmeData in, GpgmeData out, GpgmeSigMode mode)
 {
-    GpgmeError err = gpgme_op_sign_start ( c, in, out, mode );
-    if ( !err ) {
-        gpgme_wait (c, 1);
-        if ( c->result_type != RESULT_TYPE_SIGN )
-            err = mk_error (General_Error);
-        else if ( c->out_of_core )
-            err = mk_error (Out_Of_Core);
-        else {
-            assert ( c->result.sign );
-            if ( c->result.sign->no_passphrase ) 
-                err = mk_error (No_Passphrase);
-            else if (!c->result.sign->okay)
-                err = mk_error (No_Data); /* Hmmm: choose a better error? */
-        }
-        c->pending = 0;
+  GpgmeError err = gpgme_op_sign_start (ctx, in, out, mode);
+  if (!err)
+    {
+      gpgme_wait (ctx, 1);
+      err = ctx->error;
     }
-    return err;
+  return err;
 }
-
-
-
-
-
-
-
-
-