diff options
Diffstat (limited to 'mail/mail-crypto.c')
-rw-r--r-- | mail/mail-crypto.c | 674 |
1 files changed, 660 insertions, 14 deletions
diff --git a/mail/mail-crypto.c b/mail/mail-crypto.c index de866bcda3..a013b72878 100644 --- a/mail/mail-crypto.c +++ b/mail/mail-crypto.c @@ -57,6 +57,8 @@ #include <unistd.h> #include <signal.h> +#include <camel/camel-mime-filter-from.h> + static int cleanup_child (pid_t child) { @@ -338,7 +340,8 @@ crypto_exec_with_passwd (const char *path, char *argv[], const char *input, int * the case that the cleartext is a binary stream. **/ char * -mail_crypto_openpgp_decrypt (const char *ciphertext, int *outlen, CamelException *ex) +mail_crypto_openpgp_decrypt (const char *ciphertext, int cipherlen, + int *outlen, CamelException *ex) { int retval, i; char *path, *argv[12]; @@ -398,14 +401,17 @@ mail_crypto_openpgp_decrypt (const char *ciphertext, int *outlen, CamelException putenv (passwd_fd); #endif argv[i++] = NULL; - + + /* initialize outlen */ + *outlen = 0; + retval = crypto_exec_with_passwd (path, argv, - ciphertext, strlen (ciphertext), + ciphertext, cipherlen, passwd_fds, passphrase, &plaintext, outlen, &diagnostics); - if (retval != 0 || !*plaintext) { + if (retval != 0 || *outlen == 0) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, "%s", diagnostics); g_free (plaintext); @@ -599,15 +605,15 @@ mail_crypto_openpgp_encrypt (const char *in, int inlen, * @plaintext: text to sign * @userid: user id to sign with * @hash: Preferred hash function (md5 or sha1) - * @detach: Specify whether or not to generate a detached clearsign. * @ex: a CamelException * * Clearsigns the plaintext using the user id **/ char * -mail_crypto_openpgp_clearsign (const char *plaintext, const char *userid, - PgpHashType hash, gboolean detach, +mail_crypto_openpgp_clearsign (const char *plaintext, + const char *userid, + PgpHashType hash, CamelException *ex) { int retval; @@ -661,9 +667,6 @@ mail_crypto_openpgp_clearsign (const char *plaintext, const char *userid, argv[i++] = "--clearsign"; - if (detach) - argv[i++] = "-b"; - if (hash_str) { argv[i++] = "--digest-algo"; argv[i++] = hash_str; @@ -692,9 +695,6 @@ mail_crypto_openpgp_clearsign (const char *plaintext, const char *userid, argv[i++] = "pgps"; - if (detach) - argv[i++] = "-b"; - if (userid) { argv[i++] = "-u"; argv[i++] = (char *) userid; @@ -710,7 +710,7 @@ mail_crypto_openpgp_clearsign (const char *plaintext, const char *userid, sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); putenv (passwd_fd); #else - /* FIXME: modify to respect hash and detach */ + /* FIXME: modify to respect hash */ path = PGP_PATH; argv[i++] = "pgp"; @@ -741,7 +741,653 @@ mail_crypto_openpgp_clearsign (const char *plaintext, const char *userid, } g_free (diagnostics); + return ciphertext; } + +/** + * mail_crypto_openpgp_sign: + * @in: input data to sign + * @inlen: length of input data + * @userid: userid to sign with + * @hash: preferred hash type (md5 or sha1) + * @ex: exception + * + * Returns an allocated string containing the detached signature using + * the preferred hash. + **/ +char * +mail_crypto_openpgp_sign (const char *in, int inlen, const char *userid, + PgpHashType hash, CamelException *ex) +{ + char *argv[20]; + char *cyphertext = NULL; + char *diagnostics = NULL; + char *passphrase = NULL; + char *hash_str = NULL; + char *path; + int passwd_fds[2]; + char passwd_fd[32]; + int retval, i; + + +#ifndef PGP_PROGRAM + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No GPG/PGP program available.")); + return NULL; +#endif + + passphrase = mail_session_request_dialog (_("Please enter your PGP/GPG passphrase."), + TRUE, "pgp", FALSE); + if (!passphrase) { + camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, + _("No password provided.")); + return NULL; + } + + if (pipe (passwd_fds) < 0) { + g_free (passphrase); + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Couldn't create pipe to GPG/PGP: %s"), + g_strerror (errno)); + return NULL; + } + + switch (hash) { + case PGP_HASH_TYPE_MD5: + hash_str = "MD5"; + break; + case PGP_HASH_TYPE_SHA1: + hash_str = "SHA1"; + break; + default: + hash_str = NULL; + } + + i = 0; +#if defined(GPG_PATH) + path = GPG_PATH; + argv[i++] = "gpg"; + + argv[i++] = "--sign"; + argv[i++] = "-b"; + if (hash_str) { + argv[i++] = "--digest-algo"; + argv[i++] = hash_str; + } + + if (userid) { + argv[i++] = "-u"; + argv[i++] = (char *) userid; + } + + argv[i++] = "--verbose"; + argv[i++] = "--yes"; + argv[i++] = "--batch"; + + argv[i++] = "--armor"; + + argv[i++] = "--output"; + argv[i++] = "-"; /* output to stdout */ + + argv[i++] = "--passphrase-fd"; + sprintf (passwd_fd, "%d", passwd_fds[0]); + argv[i++] = passwd_fd; +#elif defined(PGP5_PATH) + path = PGP5_PATH; + + /* FIXME: respect hash */ + argv[i++] = "pgps"; + + if (userid) { + argv[i++] = "-u"; + argv[i++] = (char *) userid; + } + + argv[i++] = "-b"; + argv[i++] = "-f"; + argv[i++] = "-z"; + argv[i++] = "-a"; + argv[i++] = "-o"; + argv[i++] = "-"; /* output to stdout */ + + argv[i++] = "-s"; + sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); + putenv (passwd_fd); +#else + path = PGP_PATH; + + /* FIXME: needs to respect hash and also return only the detached sig */ + argv[i++] = "pgp"; + + if (userid) { + argv[i++] = "-u"; + argv[i++] = (char *) userid; + } + + argv[i++] = "-f"; + argv[i++] = "-a"; + argv[i++] = "-o"; + argv[i++] = "-"; + + argv[i++] = "-s"; + sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); + putenv (passwd_fd); +#endif + + argv[i++] = NULL; + + retval = crypto_exec_with_passwd (path, argv, + in, inlen, + passwd_fds, passphrase, + &cyphertext, NULL, + &diagnostics); + + g_free (passphrase); + + if (retval != 0 || !*cyphertext) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + "%s", diagnostics); + g_free (cyphertext); + cyphertext = NULL; + } + + g_free (diagnostics); + + return cyphertext; +} + +gboolean +mail_crypto_openpgp_verify (const char *in, int inlen, const char *sigin, int siglen, CamelException *ex) +{ + char *argv[20]; + char *cleartext = NULL; + char *diagnostics = NULL; + char *path; + int passwd_fds[2]; + char passwd_fd[32]; + char *tmp = "/tmp/mail-crypto-XXXXXX"; + int retval, i, clearlen; + gboolean valid = TRUE; + + +#ifndef PGP_PROGRAM + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No GPG/PGP program available.")); + return FALSE; +#endif + + if (pipe (passwd_fds) < 0) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Couldn't create pipe to GPG/PGP: %s"), + g_strerror (errno)); + return FALSE; + } + + i = 0; +#if defined(GPG_PATH) + path = GPG_PATH; + + argv[i++] = "gpg"; + + argv[i++] = "--verify"; + + if (sigin != NULL && siglen) { + /* We are going to verify a detached signature so save + the signature to a temp file and write the data to + verify to stdin */ + int fd; + + fd = mkstemp (tmp); + if (fd == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Couldn't create temp file: %s"), + g_strerror (errno)); + return FALSE; + } + + write (fd, sigin, siglen); + close (fd); + + argv[i++] = tmp; + argv[i++] = "-"; + } else { + /* We are going to verify using stdin */ + argv[i++] = "-"; + } + + argv[i++] = "--verbose"; + argv[i++] = "--yes"; + argv[i++] = "--batch"; + + argv[i++] = "--output"; + argv[i++] = "-"; /* output to stdout */ +#elif defined (PGP5_PATH) + path = PGP5_PATH; + + argv[i++] = "pgpv"; +#else + path = PGP_PATH; + + argv[i++] = "pgp"; +#endif + + argv[i++] = NULL; + + clearlen = 0; + retval = crypto_exec_with_passwd (path, argv, + in, inlen, + passwd_fds, NULL, + &cleartext, &clearlen, + &diagnostics); + + /* FIXME: maybe we should always set an exception? */ + if (retval != 0 || clearlen == 0) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + "%s", diagnostics); + valid = FALSE; + } + + g_free (diagnostics); + g_free (cleartext); + + return valid; +} + +/** rfc2015 stuff *******************************/ + +gboolean +is_rfc2015_signed (CamelMimePart *mime_part) +{ + CamelDataWrapper *wrapper; + CamelMultipart *mp; + CamelMimePart *part; + GMimeContentField *type; + const gchar *param; + int nparts; + + /* check that we have a multipart/signed */ + type = camel_mime_part_get_content_type (mime_part); + if (!gmime_content_field_is_type (type, "multipart", "signed")) + return FALSE; + + /* check that we have a protocol param with the value: "application/pgp-signed" */ + param = gmime_content_field_get_parameter (type, "protocol"); + if (!param || g_strcasecmp (param, "\"application/pgp-signed\"")) + return FALSE; + + /* check that we have exactly 2 subparts */ + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + mp = CAMEL_MULTIPART (wrapper); + nparts = camel_multipart_get_number (mp); + if (nparts != 2) + return FALSE; + + /* The first part may be of any type except for + * application/pgp-signature - check it. */ + part = camel_multipart_get_part (mp, 0); + type = camel_mime_part_get_content_type (part); + if (gmime_content_field_is_type (type, "application","pgp-signature")) + return FALSE; + + /* The second part should be application/pgp-signature. */ + part = camel_multipart_get_part (mp, 1); + type = camel_mime_part_get_content_type (part); + if (!gmime_content_field_is_type (type, "application","pgp-siganture")) + return FALSE; + + /* FIXME: Implement multisig stuff */ + + return TRUE; +} + +gboolean +is_rfc2015_encrypted (CamelMimePart *mime_part) +{ + CamelDataWrapper *wrapper; + CamelMultipart *mp; + CamelMimePart *part; + GMimeContentField *type; + const gchar *param; + int nparts; + + /* check that we have a multipart/encrypted */ + type = camel_mime_part_get_content_type (mime_part); + if (!gmime_content_field_is_type (type, "multipart", "encrypted")) + return FALSE; + + /* check that we have a protocol param with the value: "application/pgp-encrypted" */ + param = gmime_content_field_get_parameter (type, "protocol"); + if (!param || g_strcasecmp (param, "\"application/pgp-encrypted\"")) + return FALSE; + + /* check that we have at least 2 subparts */ + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + mp = CAMEL_MULTIPART (wrapper); + nparts = camel_multipart_get_number (mp); + if (nparts < 2) + return FALSE; + + /* The first part should be application/pgp-encrypted */ + part = camel_multipart_get_part (mp, 0); + type = camel_mime_part_get_content_type (part); + if (!gmime_content_field_is_type (type, "application","pgp-encrypted")) + return FALSE; + + /* The second part should be application/octet-stream - this + is the one we care most about */ + part = camel_multipart_get_part (mp, 1); + type = camel_mime_part_get_content_type (part); + if (!gmime_content_field_is_type (type, "application","octet-stream")) + return FALSE; + + return TRUE; +} + +/** + * pgp_mime_part_sign: + * @mime_part: a MIME part that will be replaced by a pgp signed part + * @userid: userid to sign with + * @hash: one of PGP_HASH_TYPE_MD5 or PGP_HASH_TYPE_SHA1 + * @ex: exception which will be set if there are any errors. + * + * Constructs a PGP/MIME multipart in compliance with rfc2015 and + * replaces #part with the generated multipart/signed. On failure, + * #ex will be set and #part will remain untouched. + **/ +void +pgp_mime_part_sign (CamelMimePart **mime_part, const gchar *userid, PgpHashType hash, CamelException *ex) +{ + CamelMimePart *part, *signed_part; + CamelMultipart *multipart; + CamelMimePartEncodingType encoding; + GMimeContentField *mime_type; + CamelStreamFilter *filtered_stream; + CamelMimeFilter *crlf_filter, *from_filter; + CamelStream *stream; + GByteArray *array; + gchar *cleartext, *signature; + gchar *hash_type; + gint clearlen; + + g_return_if_fail (*mime_part != NULL); + g_return_if_fail (userid != NULL); + g_return_if_fail (hash != PGP_HASH_TYPE_NONE); + + part = *mime_part; + encoding = camel_mime_part_get_encoding (part); + + /* the encoding should really be QP or Base64 */ + if (encoding != CAMEL_MIME_PART_ENCODING_BASE64) + camel_mime_part_set_encoding (part, CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE); + + /* get the cleartext */ + array = g_byte_array_new (); + stream = camel_stream_mem_new_with_byte_array (array); + crlf_filter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, + CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); + from_filter = CAMEL_MIME_FILTER (camel_mime_filter_from_new ()); + filtered_stream = camel_stream_filter_new_with_stream (stream); + camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (crlf_filter)); + camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (from_filter)); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (part), CAMEL_STREAM (filtered_stream)); + camel_object_unref (CAMEL_OBJECT (filtered_stream)); + camel_object_unref (CAMEL_OBJECT (stream)); + cleartext = array->data; + clearlen = array->len; + + /* get the signature */ + signature = mail_crypto_openpgp_sign (cleartext, clearlen, userid, hash, ex); + g_byte_array_free (array, TRUE); + if (camel_exception_is_set (ex)) { + /* restore the original encoding */ + camel_mime_part_set_encoding (part, encoding); + return; + } + + /* construct the pgp-signature mime part */ + fprintf (stderr, "signature:\n%s\n", signature); + signed_part = camel_mime_part_new (); + camel_mime_part_set_content (signed_part, signature, strlen (signature), + "application/pgp-signature"); + g_free (signature); + camel_mime_part_set_encoding (signed_part, CAMEL_MIME_PART_ENCODING_DEFAULT); + + /* construct the container multipart/signed */ + multipart = camel_multipart_new (); + camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), + "multipart/signed"); + switch (hash) { + case PGP_HASH_TYPE_MD5: + hash_type = "pgp-md5"; + break; + case PGP_HASH_TYPE_SHA1: + hash_type = "pgp-sha1"; + break; + default: + hash_type = NULL; + } + mime_type = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (multipart)); + gmime_content_field_set_parameter (mime_type, "micalg", hash_type); + gmime_content_field_set_parameter (mime_type, "protocol", "application/pgp-signature"); + camel_multipart_set_boundary (multipart, NULL); + + /* add the parts to the multipart */ + camel_multipart_add_part (multipart, part); + camel_object_unref (CAMEL_OBJECT (part)); + camel_multipart_add_part (multipart, signed_part); + camel_object_unref (CAMEL_OBJECT (signed_part)); + + /* replace the input part with the output part */ + *mime_part = camel_mime_part_new (); + camel_medium_set_content_object (CAMEL_MEDIUM (*mime_part), + CAMEL_DATA_WRAPPER (multipart)); + camel_object_unref (CAMEL_OBJECT (multipart)); +} + + +/** + * pgp_mime_part_verify: + * @mime_part: a multipart/signed MIME Part + * @ex: exception + * + * Returns TRUE if the signature is valid otherwise returns + * FALSE. Note: you may want to check the exception if it fails as + * there may be useful information to give to the user; example: + * verification may have failed merely because the user doesn't have + * the sender's key on her system. + **/ +gboolean +pgp_mime_part_verify (CamelMimePart *mime_part, CamelException *ex) +{ + CamelDataWrapper *wrapper; + CamelMultipart *multipart; + CamelMimePart *part, *sigpart; + GByteArray *content, *sig; + CamelStreamFilter *filtered_stream; + CamelMimeFilter *crlf_filter; + CamelStream *stream; + gboolean valid = FALSE; + + g_return_val_if_fail (mime_part != NULL, FALSE); + + if (!is_rfc2015_signed (mime_part)) + return FALSE; + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + multipart = CAMEL_MULTIPART (wrapper); + + /* get the plain part */ + part = camel_multipart_get_part (multipart, 0); + content = g_byte_array_new (); + stream = camel_stream_mem_new_with_byte_array (content); + crlf_filter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, + CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); + filtered_stream = camel_stream_filter_new_with_stream (stream); + camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (crlf_filter)); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (part), CAMEL_STREAM (filtered_stream)); + camel_object_unref (CAMEL_OBJECT (filtered_stream)); + camel_object_unref (CAMEL_OBJECT (stream)); + + /* get the signed part */ + sigpart = camel_multipart_get_part (multipart, 1); + sig = g_byte_array_new (); + stream = camel_stream_mem_new_with_byte_array (sig); + camel_data_wrapper_write_to_stream (camel_medium_get_content_object (CAMEL_MEDIUM (sigpart)), stream); + camel_object_unref (CAMEL_OBJECT (stream)); + + /* verify */ + valid = mail_crypto_openpgp_verify (content->data, content->len, + sig->data, sig->len, ex); + + g_byte_array_free (content, TRUE); + g_byte_array_free (sig, TRUE); + + return valid; +} + + +/** + * pgp_mime_part_encrypt: + * @mime_part: a MIME part that will be replaced by a pgp encrypted part + * @recipients: list of recipient PGP Key IDs + * @ex: exception which will be set if there are any errors. + * + * Constructs a PGP/MIME multipart in compliance with rfc2015 and + * replaces #mime_part with the generated multipart/signed. On failure, + * #ex will be set and #part will remain untouched. + **/ +void +pgp_mime_part_encrypt (CamelMimePart **mime_part, const GPtrArray *recipients, CamelException *ex) +{ + CamelMultipart *multipart; + CamelMimePart *part, *version_part, *encrypted_part; + GMimeContentField *mime_type; + CamelStream *stream; + GByteArray *contents; + gchar *ciphertext; + + g_return_if_fail (*mime_part != NULL); + g_return_if_fail (recipients != NULL); + + part = *mime_part; + + /* get the contents */ + contents = g_byte_array_new (); + stream = camel_stream_mem_new_with_byte_array (contents); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (part), stream); + camel_object_unref (CAMEL_OBJECT (stream)); + + /* pgp encrypt */ + ciphertext = mail_crypto_openpgp_encrypt (contents->data, + contents->len, + recipients, FALSE, NULL, ex); + if (camel_exception_is_set (ex)) + return; + + /* construct the version part */ + version_part = camel_mime_part_new (); + camel_mime_part_set_encoding (version_part, CAMEL_MIME_PART_ENCODING_7BIT); + camel_mime_part_set_content (version_part, "Version: 1", strlen ("Version: 1"), + "application/pgp-encrypted"); + + /* construct the pgp-encrypted mime part */ + encrypted_part = camel_mime_part_new (); + camel_mime_part_set_encoding (encrypted_part, CAMEL_MIME_PART_ENCODING_7BIT); + camel_mime_part_set_content (encrypted_part, ciphertext, strlen (ciphertext), + "application/octet-stream"); + g_free (ciphertext); + + /* construct the container multipart/signed */ + multipart = camel_multipart_new (); + camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), + "multipart/encrypted"); + camel_multipart_set_boundary (multipart, NULL); + mime_type = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (multipart)); + gmime_content_field_set_parameter (mime_type, "protocol", "application/pgp-encrypted"); + + /* add the parts to the multipart */ + camel_multipart_add_part (multipart, version_part); + camel_object_unref (CAMEL_OBJECT (version_part)); + camel_multipart_add_part (multipart, encrypted_part); + camel_object_unref (CAMEL_OBJECT (encrypted_part)); + + /* replace the input part with the output part */ + *mime_part = camel_mime_part_new (); + camel_medium_set_content_object (CAMEL_MEDIUM (*mime_part), + CAMEL_DATA_WRAPPER (multipart)); + camel_object_unref (CAMEL_OBJECT (multipart)); + + /* destroy the original part */ + camel_object_unref (CAMEL_OBJECT (part)); +} + + +/** + * pgp_mime_part_decrypt: + * @mime_part: a multipart/encrypted MIME Part + * @ex: exception + * + * Returns the decrypted MIME Part on success or NULL on fail. + **/ +CamelMimePart * +pgp_mime_part_decrypt (CamelMimePart *mime_part, CamelException *ex) +{ + CamelDataWrapper *wrapper; + CamelMultipart *multipart; + CamelMimeParser *parser; + CamelMimePart *encrypted_part, *part; + GMimeContentField *mime_type; + CamelStream *stream; + GByteArray *content; + gchar *cleartext, *ciphertext = NULL; + int cipherlen, clearlen; + + g_return_val_if_fail (mime_part != NULL, NULL); + + /* make sure the mime part is a multipart/encrypted */ + if (!is_rfc2015_encrypted (mime_part)) + return NULL; + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + multipart = CAMEL_MULTIPART (wrapper); + + /* get the encrypted part (second part) */ + encrypted_part = camel_multipart_get_part (multipart, 1 /* second part starting at 0 */); + mime_type = camel_mime_part_get_content_type (encrypted_part); + if (!gmime_content_field_is_type (mime_type, "application", "octet-stream")) + return NULL; + + /* get the ciphertext */ + content = g_byte_array_new (); + stream = camel_stream_mem_new_with_byte_array (content); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (encrypted_part), stream); + camel_object_unref (CAMEL_OBJECT (stream)); + ciphertext = content->data; + cipherlen = content->len; + + /* get the cleartext */ + cleartext = mail_crypto_openpgp_decrypt (ciphertext, cipherlen, &clearlen, ex); + g_byte_array_free (content, TRUE); + if (camel_exception_is_set (ex)) + return NULL; + + /* create a stream based on the returned cleartext */ + stream = camel_stream_mem_new (); + camel_stream_write (stream, cleartext, clearlen); + camel_stream_reset (stream); + g_free (cleartext); + + /* construct the new decrypted mime part from the stream */ + part = camel_mime_part_new (); + parser = camel_mime_parser_new (); + camel_mime_parser_init_with_stream (parser, stream); + camel_mime_part_construct_from_parser (part, parser); + camel_object_unref (CAMEL_OBJECT (parser)); + camel_object_unref (CAMEL_OBJECT (stream)); + + return part; +} + #endif /* PGP_PROGRAM */ |