diff --git a/NEWS b/NEWS index f01424ca..4476d408 100644 --- a/NEWS +++ b/NEWS @@ -3,10 +3,15 @@ to set and get the number of certifications to include in S/MIME signed messages. + * New interfaces gpgme_op_encrypt_sign and gpgme_op_encrypt_sign_start + to encrypt and sign a message in a combined operation. + * Interface changes relative to the 0.3.3 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_set_include_certs NEW gpgme_get_include_certs NEW +gpgme_op_encrypt_sign NEW +gpgme_op_encrypt_sign_start NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.3.3 (2002-02-12) diff --git a/doc/ChangeLog b/doc/ChangeLog index da067da4..fe5eb4e5 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,8 @@ +2002-02-26 Marcus Brinkmann + + * gpgme.texi (Encrypting a Plaintext): Document + gpgme_op_encrypt_sign and gpgme_op_encrypt_sign_start. + 2002-02-25 Marcus Brinkmann * gpgme.texi (Creating a Signature): Add a note about diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 708110c3..2be6170b 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -2201,7 +2201,7 @@ The function @code{gpgme_recipients_enum_close} releases the iterator @subsubsection Encrypting a Plaintext @deftypefun GpgmeError gpgme_op_encrypt (@w{GpgmeCtx @var{ctx}}, @w{GpgmeRecipients @var{rset}}, @w{GpgmeData @var{plain}}, @w{GpgmeData @var{cipher}}) -The function @code{gpgme_op_crypt} encrypts the plaintext in the data +The function @code{gpgme_op_encrypt} encrypts the plaintext in the data object @var{plain} for the recipients @var{rset} and stores the ciphertext in the data object @var{cipher}. The type of the ciphertext created is determined by the @acronym{ASCII} armor and text @@ -2232,6 +2232,30 @@ recipients. @end deftypefun +@deftypefun GpgmeError gpgme_op_encrypt_sign (@w{GpgmeCtx @var{ctx}}, @w{GpgmeRecipients @var{rset}}, @w{GpgmeData @var{plain}}, @w{GpgmeData @var{cipher}}) +The function @code{gpgme_op_encrypt_sign} does a combined encrypt and +sign operation. It is used like @code{gpgme_op_encrypt}, but the +ciphertext also contains signatures for the signers listed in +@var{ctx}. + +The combined encrypt and sign operation is currently only available +for the OpenPGP crypto engine. +@end deftypefun + +@deftypefun GpgmeError gpgme_op_encrypt_sign_start (@w{GpgmeCtx @var{ctx}}, @w{GpgmeRecipients @var{rset}}, @w{GpgmeData @var{plain}}, @w{GpgmeData @var{cipher}}) +The function @code{gpgme_op_encrypt_sign_start} initiates a +@code{gpgme_op_encrypt_sign} operation. It can be completed by +calling @code{gpgme_wait} on the context. @xref{Waiting For +Completion}. + +The function returns @code{GPGME_No_Error} if the operation could be +started successfully, @code{GPGME_Invalid_Value} if @var{ctx}, +@var{rset}, @var{plain} or @var{cipher} is not a valid pointer, and +@code{GPGME_No_Recipient} if @var{rset} does not contain any valid +recipients. +@end deftypefun + + @node Detailed Results @subsection Detailed Results @cindex cryptographic operation, detailed results diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index ebecd264..1b7930ff 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,3 +1,23 @@ +2002-02-25 Marcus Brinkmann + + * engine.c (_gpgme_engine_op_encrypt_sign): New function. + * engine.h (_gpgme_engine_op_encrypt_sign): New prototype. + * rungpg.c (_gpgme_append_gpg_args_from_signers): New function. + (_gpgme_gpg_op_sign): Use that new function. + (_gpgme_gpg_op_encrypt_sign): New function. + * rungpg.h (_gpgme_gpg_op_encrypt_sign): New prototype. + * gpgme.h (gpgme_op_encrypt_sign_start): New prototype. + (gpgme_op_encrypt_sign): Likewise. + * Makefile.am (libgpgme_la_SOURCES): Add encrypt-sign.c. + * ops.h (_gpgme_encrypt_status_handler): Add prototype. + (_gpgme_sign_status_handler): Add prototype. + * sign.c (sign_status_handler): Rename to ... + (_gpgme_sign_status_handler): ... this and make non-static. + * encrypt.c (encrypt_status_handler): Rename to ... + (_gpgme_encrypt_status_handler): ... this and make non-static. + * encrypt.c (gpgme_op_encrypt_start): Use new status handler name. + * sign.c (gpgme_op_sign_start): Likewise. + 2002-02-25 Marcus Brinkmann * verify.c (_gpgme_verify_status_handler): Parse the args line to diff --git a/gpgme/Makefile.am b/gpgme/Makefile.am index 9bec612d..aa6ef831 100644 --- a/gpgme/Makefile.am +++ b/gpgme/Makefile.am @@ -42,6 +42,7 @@ libgpgme_la_SOURCES = \ data.c recipient.c signers.c \ wait.c wait.h \ encrypt.c \ + encrypt-sign.c \ decrypt.c \ decrypt-verify.c \ verify.c \ diff --git a/gpgme/encrypt-sign.c b/gpgme/encrypt-sign.c new file mode 100644 index 00000000..1c8e596d --- /dev/null +++ b/gpgme/encrypt-sign.c @@ -0,0 +1,150 @@ +/* encrypt-sign.c - encrypt and verify functions + * Copyright (C) 2000 Werner Koch (dd9jn) + * Copyright (C) 2001, 2002 g10 Code GmbH + * + * This file is part of GPGME. + * + * GPGME 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 + * (at your option) any later version. + * + * GPGME is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 + */ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "context.h" +#include "ops.h" + + +static void +encrypt_sign_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args) +{ + char *encrypt_info = 0; + int encrypt_info_len; + + _gpgme_encrypt_status_handler (ctx, code, args); + + if (code == STATUS_EOF) + { + encrypt_info = gpgme_data_release_and_get_mem (ctx->op_info, + &encrypt_info_len); + ctx->op_info = NULL; + } + _gpgme_sign_status_handler (ctx, code, args); + if (code == STATUS_EOF && encrypt_info) + _gpgme_data_append (ctx->op_info, encrypt_info, encrypt_info_len); +} + + +GpgmeError +gpgme_op_encrypt_sign_start (GpgmeCtx ctx, GpgmeRecipients recp, + GpgmeData plain, GpgmeData cipher) +{ + int err = 0; + + fail_on_pending_request (ctx); + ctx->pending = 1; + + _gpgme_release_result (ctx); + + /* Do some checks. */ + if (!gpgme_recipients_count (recp)) + { + /* FIXME: In this case we should do symmetric encryption. */ + err = mk_error (No_Recipients); + goto leave; + } + + /* Create an engine 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; + + err = _gpgme_passphrase_start (ctx); + if (err) + goto leave; + + _gpgme_engine_set_status_handler (ctx->engine, + encrypt_sign_status_handler, ctx); + _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity); + + /* Check the supplied data */ + if (gpgme_data_get_type (plain) == GPGME_DATA_TYPE_NONE) + { + err = mk_error (No_Data); + goto leave; + } + _gpgme_data_set_mode (plain, GPGME_DATA_MODE_OUT); + if (!cipher || gpgme_data_get_type (cipher) != GPGME_DATA_TYPE_NONE) + { + err = mk_error (Invalid_Value); + goto leave; + } + _gpgme_data_set_mode (cipher, GPGME_DATA_MODE_IN); + + err = _gpgme_engine_op_encrypt_sign (ctx->engine, recp, plain, cipher, + ctx->use_armor, ctx /* FIXME */); + + if (!err) /* And kick off the process. */ + err = _gpgme_engine_start (ctx->engine, ctx); + + leave: + if (err) + { + ctx->pending = 0; + _gpgme_engine_release (ctx->engine); + ctx->engine = NULL; + } + return err; +} + + +/** + * gpgme_op_encrypt_sign: + * @ctx: The context + * @recp: The set of recipients + * @plain: plaintext input + * @cipher: signed ciphertext + * + * This function encrypts @plain for all recipients in recp, signs it, + * and returns the ciphertext in @out. The function does wait for the + * result. + * + * Return value: 0 on success or an errorcode. + **/ +GpgmeError +gpgme_op_encrypt_sign (GpgmeCtx ctx, GpgmeRecipients recp, + GpgmeData plain, GpgmeData cipher) +{ + GpgmeError err = gpgme_op_encrypt_sign_start (ctx, recp, plain, cipher); + + if (!err) + { + gpgme_wait (ctx, &err, 1); + /* Old gpg versions don't return status info for invalid + recipients, so we simply check whether we got any output at + all, and if not we assume that we don't have valid + recipients. */ + if (!ctx->error && gpgme_data_get_type (cipher) == GPGME_DATA_TYPE_NONE) + ctx->error = mk_error (No_Recipients); + err = ctx->error; + } + return err; +} diff --git a/gpgme/encrypt.c b/gpgme/encrypt.c index d0b8fc6e..2f6abdae 100644 --- a/gpgme/encrypt.c +++ b/gpgme/encrypt.c @@ -97,8 +97,8 @@ append_xml_encinfo (GpgmeData *rdh, char *args) } -static void -encrypt_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args) +void +_gpgme_encrypt_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args) { if (ctx->error) return; @@ -158,7 +158,8 @@ gpgme_op_encrypt_start (GpgmeCtx ctx, GpgmeRecipients recp, GpgmeData plain, if (err) goto leave; - _gpgme_engine_set_status_handler (ctx->engine, encrypt_status_handler, ctx); + _gpgme_engine_set_status_handler (ctx->engine, _gpgme_encrypt_status_handler, + ctx); _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity); /* Check the supplied data */ diff --git a/gpgme/engine.c b/gpgme/engine.c index e53327a3..8841b20b 100644 --- a/gpgme/engine.c +++ b/gpgme/engine.c @@ -333,6 +333,7 @@ _gpgme_engine_op_delete (EngineObject engine, GpgmeKey key, int allow_secret) return 0; } + GpgmeError _gpgme_engine_op_encrypt (EngineObject engine, GpgmeRecipients recp, GpgmeData plain, GpgmeData ciph, int use_armor) @@ -354,6 +355,29 @@ _gpgme_engine_op_encrypt (EngineObject engine, GpgmeRecipients recp, return 0; } + +GpgmeError +_gpgme_engine_op_encrypt_sign (EngineObject engine, GpgmeRecipients recp, + GpgmeData plain, GpgmeData ciph, int use_armor, + GpgmeCtx ctx /* FIXME */) +{ + if (!engine) + return mk_error (Invalid_Value); + + switch (engine->protocol) + { + case GPGME_PROTOCOL_OpenPGP: + return _gpgme_gpg_op_encrypt_sign (engine->engine.gpg, recp, plain, ciph, + use_armor, ctx); + case GPGME_PROTOCOL_CMS: + return mk_error (Not_Implemented); + default: + break; + } + return 0; +} + + GpgmeError _gpgme_engine_op_export (EngineObject engine, GpgmeRecipients recp, GpgmeData keydata, int use_armor) diff --git a/gpgme/engine.h b/gpgme/engine.h index 7e4acd30..d8881a10 100644 --- a/gpgme/engine.h +++ b/gpgme/engine.h @@ -46,6 +46,11 @@ GpgmeError _gpgme_engine_op_delete (EngineObject engine, GpgmeKey key, GpgmeError _gpgme_engine_op_encrypt (EngineObject engine, GpgmeRecipients recp, GpgmeData plain, GpgmeData ciph, int use_armor); +GpgmeError _gpgme_engine_op_encrypt_sign (EngineObject engine, + GpgmeRecipients recp, + GpgmeData plain, GpgmeData ciph, + int use_armor, + GpgmeCtx ctx /* FIXME */); GpgmeError _gpgme_engine_op_export (EngineObject engine, GpgmeRecipients recp, GpgmeData keydata, int use_armor); GpgmeError _gpgme_engine_op_genkey (EngineObject engine, GpgmeData help_data, diff --git a/gpgme/gpgme.h b/gpgme/gpgme.h index 5c7ff878..713125a5 100644 --- a/gpgme/gpgme.h +++ b/gpgme/gpgme.h @@ -427,6 +427,16 @@ GpgmeError gpgme_op_encrypt (GpgmeCtx ctx, GpgmeRecipients recp, GpgmeData plain, GpgmeData cipher); +/* Encrypt plaintext PLAIN within CTX for the recipients RECP and + store the resulting ciphertext in CIPHER. Also sign the ciphertext + with the signers in CTX. */ +GpgmeError gpgme_op_encrypt_sign_start (GpgmeCtx ctx, + GpgmeRecipients recp, + GpgmeData plain, GpgmeData cipher); +GpgmeError gpgme_op_encrypt_sign (GpgmeCtx ctx, + GpgmeRecipients recp, + GpgmeData plain, GpgmeData cipher); + /* Decrypt ciphertext CIPHER within CTX and store the resulting plaintext in PLAIN. */ GpgmeError gpgme_op_decrypt_start (GpgmeCtx ctx, diff --git a/gpgme/ops.h b/gpgme/ops.h index 5b09a9ff..2ff8b559 100644 --- a/gpgme/ops.h +++ b/gpgme/ops.h @@ -101,9 +101,13 @@ GpgmeError _gpgme_decrypt_result (GpgmeCtx ctx); /*-- sign.c --*/ void _gpgme_release_sign_result ( SignResult res ); +void _gpgme_sign_status_handler (GpgmeCtx ctx, GpgStatusCode code, + char *args); /*-- encrypt.c --*/ void _gpgme_release_encrypt_result ( EncryptResult res ); +void _gpgme_encrypt_status_handler (GpgmeCtx ctx, GpgStatusCode code, + char *args); /*-- passphrase.c --*/ void _gpgme_release_passphrase_result (PassphraseResult result); diff --git a/gpgme/rungpg.c b/gpgme/rungpg.c index b30bb04c..8738fc38 100644 --- a/gpgme/rungpg.c +++ b/gpgme/rungpg.c @@ -1256,6 +1256,7 @@ _gpgme_gpg_op_delete (GpgObject gpg, GpgmeKey key, int allow_secret) return err; } + static GpgmeError _gpgme_append_gpg_args_from_recipients (GpgObject gpg, const GpgmeRecipients rset) @@ -1275,6 +1276,33 @@ _gpgme_append_gpg_args_from_recipients (GpgObject gpg, return err; } + +static GpgmeError +_gpgme_append_gpg_args_from_signers (GpgObject gpg, + GpgmeCtx ctx /* FIXME */) +{ + GpgmeError err = 0; + int i; + GpgmeKey key; + + for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++) + { + const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEYID, + NULL, 0); + if (s) + { + if (!err) + err = _gpgme_gpg_add_arg (gpg, "-u"); + if (!err) + err = _gpgme_gpg_add_arg (gpg, s); + } + gpgme_key_unref (key); + if (err) break; + } + return err; +} + + GpgmeError _gpgme_gpg_op_encrypt (GpgObject gpg, GpgmeRecipients recp, GpgmeData plain, GpgmeData ciph, int use_armor) @@ -1308,6 +1336,45 @@ _gpgme_gpg_op_encrypt (GpgObject gpg, GpgmeRecipients recp, return err; } +GpgmeError +_gpgme_gpg_op_encrypt_sign (GpgObject gpg, GpgmeRecipients recp, + GpgmeData plain, GpgmeData ciph, int use_armor, + GpgmeCtx ctx /* FIXME */) +{ + GpgmeError err; + + err = _gpgme_gpg_add_arg (gpg, "--encrypt"); + if (!err) + err = _gpgme_gpg_add_arg (gpg, "--sign"); + if (!err && use_armor) + err = _gpgme_gpg_add_arg (gpg, "--armor"); + + /* If we know that all recipients are valid (full or ultimate trust) + * we can suppress further checks */ + if (!err && _gpgme_recipients_all_valid (recp)) + err = _gpgme_gpg_add_arg (gpg, "--always-trust"); + + if (!err) + err = _gpgme_append_gpg_args_from_recipients (gpg, recp); + + if (!err) + err = _gpgme_append_gpg_args_from_signers (gpg, ctx); + + /* Tell the gpg object about the data. */ + if (!err) + err = _gpgme_gpg_add_arg (gpg, "--output"); + if (!err) + err = _gpgme_gpg_add_arg (gpg, "-"); + if (!err) + err = _gpgme_gpg_add_data (gpg, ciph, 1); + if (!err) + err = _gpgme_gpg_add_arg (gpg, "--"); + if (!err) + err = _gpgme_gpg_add_data (gpg, plain, 0); + + return err; +} + GpgmeError _gpgme_gpg_op_export (GpgObject gpg, GpgmeRecipients recp, GpgmeData keydata, int use_armor) @@ -1404,8 +1471,6 @@ _gpgme_gpg_op_sign (GpgObject gpg, GpgmeData in, GpgmeData out, int use_textmode, GpgmeCtx ctx /* FIXME */) { GpgmeError err; - GpgmeKey key; - int i; if (mode == GPGME_SIG_MODE_CLEAR) err = _gpgme_gpg_add_arg (gpg, "--clearsign"); @@ -1419,20 +1484,9 @@ _gpgme_gpg_op_sign (GpgObject gpg, GpgmeData in, GpgmeData out, if (!err && use_textmode) _gpgme_gpg_add_arg (gpg, "--textmode"); } - for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++) - { - const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEYID, - NULL, 0); - if (s) - { - if (!err) - err = _gpgme_gpg_add_arg (gpg, "-u"); - if (!err) - err = _gpgme_gpg_add_arg (gpg, s); - } - gpgme_key_unref (key); - if (err) break; - } + + if (!err) + err = _gpgme_append_gpg_args_from_signers (gpg, ctx); /* Tell the gpg object about the data. */ if (!err) diff --git a/gpgme/rungpg.h b/gpgme/rungpg.h index 36bd5c4f..ed65cf15 100644 --- a/gpgme/rungpg.h +++ b/gpgme/rungpg.h @@ -126,6 +126,9 @@ GpgmeError _gpgme_gpg_op_delete (GpgObject gpg, GpgmeKey key, int allow_secret); GpgmeError _gpgme_gpg_op_encrypt (GpgObject gpg, GpgmeRecipients recp, GpgmeData plain, GpgmeData ciph, int use_armor); +GpgmeError _gpgme_gpg_op_encrypt_sign (GpgObject gpg, GpgmeRecipients recp, + GpgmeData plain, GpgmeData ciph, + int use_armor, GpgmeCtx ctx); GpgmeError _gpgme_gpg_op_export (GpgObject gpg, GpgmeRecipients recp, GpgmeData keydata, int use_armor); GpgmeError _gpgme_gpg_op_genkey (GpgObject gpg, GpgmeData help_data, diff --git a/gpgme/sign.c b/gpgme/sign.c index f63974d1..494eed81 100644 --- a/gpgme/sign.c +++ b/gpgme/sign.c @@ -135,8 +135,8 @@ append_xml_siginfo (GpgmeData *rdh, char *args) _gpgme_data_append_string (dh, "\n"); } -static void -sign_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args) +void +_gpgme_sign_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args) { _gpgme_passphrase_status_handler (ctx, code, args); @@ -210,7 +210,8 @@ gpgme_op_sign_start (GpgmeCtx ctx, GpgmeData in, GpgmeData out, if (err) goto leave; - _gpgme_engine_set_status_handler (ctx->engine, sign_status_handler, ctx); + _gpgme_engine_set_status_handler (ctx->engine, _gpgme_sign_status_handler, + ctx); _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity); _gpgme_engine_op_sign (ctx->engine, in, out, mode, ctx->use_armor, diff --git a/tests/ChangeLog b/tests/ChangeLog index 3a7ae6b1..5eaf4713 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,8 @@ +2002-02-26 Marcus Brinkmann + + * gpg/t-encrypt-sign.c: New file. + * gpg/Makefile.am (TESTS): Add t-encrypt-sign. + 2002-02-13 Werner Koch * gpgsm/Makefile.am (private-keys-v1.d): Don't diff --git a/tests/gpg/Makefile.am b/tests/gpg/Makefile.am index 7d249bae..424e9d2e 100644 --- a/tests/gpg/Makefile.am +++ b/tests/gpg/Makefile.am @@ -23,7 +23,7 @@ GPG = @GPG@ TESTS_ENVIRONMENT = GNUPGHOME=. -TESTS = t-encrypt t-sign t-signers t-decrypt t-verify \ +TESTS = t-encrypt t-encrypt-sign t-sign t-signers t-decrypt t-verify \ t-decrypt-verify t-keylist t-export t-import t-trustlist CLEANFILES = secring.gpg pubring.gpg trustdb.gpg random_seed diff --git a/tests/gpg/t-encrypt-sign.c b/tests/gpg/t-encrypt-sign.c new file mode 100644 index 00000000..a4620d29 --- /dev/null +++ b/tests/gpg/t-encrypt-sign.c @@ -0,0 +1,139 @@ +/* t-encrypt-sign.c - regression test + * Copyright (C) 2000 Werner Koch (dd9jn) + * Copyright (C) 2001, 2002 g10 Code GmbH + * + * This file is part of GPGME. + * + * GPGME 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 + * (at your option) any later version. + * + * GPGME is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 + */ + +#include +#include +#include +#include + +#include + +#define fail_if_err(a) do { if(a) { \ + fprintf (stderr, "%s:%d: GpgmeError %s\n", \ + __FILE__, __LINE__, gpgme_strerror(a)); \ + exit (1); } \ + } while(0) + +static void +print_op_info (GpgmeCtx c) +{ + char *s = gpgme_get_op_info (c, 0); + + if (!s) + puts (""); + else { + puts (s); + free (s); + } +} + + +static void +print_data ( GpgmeData dh ) +{ + char buf[100]; + size_t nread; + GpgmeError err; + + err = gpgme_data_rewind ( dh ); + fail_if_err (err); + while ( !(err = gpgme_data_read ( dh, buf, 100, &nread )) ) { + fwrite ( buf, nread, 1, stdout ); + } + if (err != GPGME_EOF) + fail_if_err (err); +} + + +static const char * +passphrase_cb ( void *opaque, const char *desc, void **r_hd ) +{ + const char *pass; + + if ( !desc ) { + /* cleanup by looking at *r_hd */ + + + return NULL; + } + + pass = "abc"; + fprintf (stderr, "%% requesting passphrase for `%s': ", desc ); + fprintf (stderr, "sending `%s'\n", pass ); + + return pass; +} + + +int +main (int argc, char **argv ) +{ + GpgmeCtx ctx; + GpgmeError err; + GpgmeData in, out; + GpgmeRecipients rset; + + err = gpgme_check_engine (); + fail_if_err (err); + puts ( gpgme_get_engine_info() ); + + do { + err = gpgme_new (&ctx); + fail_if_err (err); + gpgme_set_armor (ctx, 1); + if (!getenv("GPG_AGENT_INFO")) + gpgme_set_passphrase_cb (ctx, passphrase_cb, NULL); + + err = gpgme_data_new_from_mem ( &in, "Hallo Leute\n", 12, 0 ); + fail_if_err (err); + + err = gpgme_data_new ( &out ); + fail_if_err (err); + + err = gpgme_recipients_new (&rset); + fail_if_err (err); + err = gpgme_recipients_add_name_with_validity (rset, "Bob", + GPGME_VALIDITY_FULL); + fail_if_err (err); + err = gpgme_recipients_add_name_with_validity (rset, "Alpha", + GPGME_VALIDITY_FULL); + fail_if_err (err); + + + err = gpgme_op_encrypt_sign (ctx, rset, in, out); + print_op_info (ctx); + fail_if_err (err); + + fflush (NULL); + fputs ("Begin Result:\n", stdout ); + print_data (out); + fputs ("End Result.\n", stdout ); + + gpgme_recipients_release (rset); + gpgme_data_release (in); + gpgme_data_release (out); + gpgme_release (ctx); + } while ( argc > 1 && !strcmp( argv[1], "--loop" ) ); + + return 0; +} + +