コード例 #1
0
/**
 * g_mime_stream_flush:
 * @stream: a #GMimeStream
 *
 * Sync's the stream to disk.
 *
 * Returns: %0 on success or %-1 on fail.
 **/
int
g_mime_stream_flush (GMimeStream *stream)
{
	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
	
	return GMIME_STREAM_GET_CLASS (stream)->flush (stream);
}
コード例 #2
0
/**
 * g_mime_stream_write_string:
 * @stream: a #GMimeStream
 * @str: string to write
 *
 * Writes @string to @stream.
 *
 * Returns: the number of bytes written or %-1 on fail.
 **/
ssize_t
g_mime_stream_write_string (GMimeStream *stream, const char *str)
{
	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
	g_return_val_if_fail (str != NULL, -1);
	
	return g_mime_stream_write (stream, str, strlen (str));
}
コード例 #3
0
/**
 * g_mime_stream_eos:
 * @stream: a #GMimeStream
 *
 * Tests the end-of-stream indicator for @stream.
 *
 * Returns: %TRUE on EOS or %FALSE otherwise.
 **/
gboolean
g_mime_stream_eos (GMimeStream *stream)
{
	g_return_val_if_fail (GMIME_IS_STREAM (stream), TRUE);
	
	if (stream->bound_end != -1 && stream->position >= stream->bound_end)
		return TRUE;
	
	return GMIME_STREAM_GET_CLASS (stream)->eos (stream);
}
コード例 #4
0
/**
 * g_mime_stream_write:
 * @stream: a #GMimeStream
 * @buf: buffer
 * @len: buffer length
 *
 * Attempts to write up to @len bytes of @buf to @stream.
 *
 * Returns: the number of bytes written or %-1 on fail.
 **/
ssize_t
g_mime_stream_write (GMimeStream *stream, const char *buf, size_t len)
{
	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
	g_return_val_if_fail (buf != NULL, -1);
	
	if (len == 0)
		return 0;
	
	return GMIME_STREAM_GET_CLASS (stream)->write (stream, buf, len);
}
コード例 #5
0
/**
 * g_mime_stream_reset:
 * @stream: a #GMimeStream
 *
 * Resets the stream.
 *
 * Returns: %0 on success or %-1 on fail.
 **/
int
g_mime_stream_reset (GMimeStream *stream)
{
	int rv;
	
	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
	
	if ((rv = GMIME_STREAM_GET_CLASS (stream)->reset (stream)) == 0)
		stream->position = stream->bound_start;
	
	return rv;
}
コード例 #6
0
/**
 * g_mime_stream_set_bounds:
 * @stream: a #GMimeStream
 * @start: start boundary
 * @end: end boundary
 *
 * Set the bounds on a stream.
 **/
void
g_mime_stream_set_bounds (GMimeStream *stream, gint64 start, gint64 end)
{
	g_return_if_fail (GMIME_IS_STREAM (stream));
	
	stream->bound_start = start;
	stream->bound_end = end;
	
	if (stream->position < start)
		stream->position = start;
	else if (stream->position > end && end != -1)
		stream->position = end;
}
コード例 #7
0
/**
 * g_mime_stream_substream:
 * @stream: a #GMimeStream
 * @start: start boundary
 * @end: end boundary
 *
 * Creates a new substream of @stream with bounds @start and @end.
 *
 * Returns: a substream of @stream with bounds @start and @end.
 **/
GMimeStream *
g_mime_stream_substream (GMimeStream *stream, gint64 start, gint64 end)
{
	GMimeStream *sub;
	
	g_return_val_if_fail (GMIME_IS_STREAM (stream), NULL);
	
	if ((sub = GMIME_STREAM_GET_CLASS (stream)->substream (stream, start, end))) {
		sub->super_stream = stream;
		g_object_ref (stream);
	}
	
	return sub;
}
コード例 #8
0
/**
 * g_mime_header_list_set_stream:
 * @headers: a #GMimeHeaderList
 * @stream: a #GMimeStream
 *
 * Set the raw header stream.
 **/
void
g_mime_header_list_set_stream (GMimeHeaderList *headers, GMimeStream *stream)
{
	g_return_if_fail (stream == NULL || GMIME_IS_STREAM (stream));
	g_return_if_fail (headers != NULL);
	
	if (headers->stream == stream)
		return;
	
	if (stream)
		g_object_ref (stream);
	
	if (headers->stream)
		g_object_unref (headers->stream);
	
	headers->stream = stream;
	
	g_mime_event_emit (headers->changed, NULL);
}
コード例 #9
0
ファイル: mux-message-part.c プロジェクト: djcb/mux
gboolean
mux_message_part_write (MuxMessagePart *self, GMimeStream *ostream,
			GError **err)
{
	ssize_t		 bytes;
	
	g_return_val_if_fail (MUX_IS_MESSAGE_PART(self), FALSE);
	g_return_val_if_fail (GMIME_IS_STREAM(ostream), FALSE);

	if (GMIME_IS_PART(self->mime_object)) {

		GMimeDataWrapper *wrapper;
		wrapper = get_data_wrapper (self->mime_object, err);
		if (!wrapper)
			return FALSE;
		bytes = g_mime_data_wrapper_write_to_stream (
			wrapper, ostream);
	} else  {	

		GMimeStream	 *istream;
		istream = get_mime_stream (self->mime_object, err);
		if (!istream)
			return FALSE;
		bytes = g_mime_stream_write_to_stream (istream, ostream);
	}
	
	if (bytes == -1) {
		g_set_error (err, G_IO_ERROR, G_IO_ERROR_FAILED,
			     "failed to write part to stream");
		return FALSE;
	}

	bytes = g_mime_stream_flush (ostream);
	if (bytes == -1) {
		g_set_error (err, G_IO_ERROR, G_IO_ERROR_FAILED,
			     "failed to flush stream");
		return FALSE;
	}
		
	return TRUE;
}
コード例 #10
0
/**
 * g_mime_stream_printf:
 * @stream: a #GMimeStream
 * @fmt: format
 * @Varargs: arguments
 *
 * Write formatted output to a stream.
 *
 * Returns: the number of bytes written or %-1 on fail.
 **/
ssize_t
g_mime_stream_printf (GMimeStream *stream, const char *fmt, ...)
{
	va_list args;
	char *string;
	ssize_t ret;
	
	g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
	g_return_val_if_fail (fmt != NULL, -1);
	
	va_start (args, fmt);
	string = g_strdup_vprintf (fmt, args);
	va_end (args);
	
	if (!string)
		return -1;
	
	ret = g_mime_stream_write (stream, string, strlen (string));
	g_free (string);
	
	return ret;
}
コード例 #11
0
ファイル: gmime-test.c プロジェクト: Chris00/mu
static void
print_body (GMimeMessage *msg)
{
	GMimeObject		*body;
	GMimeDataWrapper	*wrapper;
	GMimeStream		*stream;

	body = g_mime_message_get_body (msg);

	if (GMIME_IS_MULTIPART(body))
		body = g_mime_multipart_get_part (GMIME_MULTIPART(body), 0);

	if (!GMIME_IS_PART(body))
		return;

	wrapper = g_mime_part_get_content_object (GMIME_PART(body));
	if (!GMIME_IS_DATA_WRAPPER(wrapper))
		return;

	stream = g_mime_data_wrapper_get_stream (wrapper);
	if (!GMIME_IS_STREAM(stream))
		return;

	do {
		char	buf[512];
		ssize_t	len;

		len = g_mime_stream_read (stream, buf, sizeof(buf));
		if (len == -1)
			break;

		if (write (fileno(stdout), buf, len) == -1)
			break;

		if (len < (int)sizeof(buf))
			break;

	} while (1);
}
コード例 #12
0
ファイル: mime-stream-shared.c プロジェクト: staceyson/balsa
/**
 * libbalsa_mime_stream_shared_unlock:
 * @stream: shared stream
 *
 * Unlock the shared stream
 **/
void
libbalsa_mime_stream_shared_unlock(GMimeStream * stream)
{
    LibBalsaMimeStreamShared *stream_shared;
    LibBalsaMimeStreamSharedLock *lock;

    g_return_if_fail(GMIME_IS_STREAM(stream));

    if (GMIME_IS_STREAM_FILTER(stream))
        stream = ((GMimeStreamFilter *) stream)->source;

    if (!LIBBALSA_IS_MIME_STREAM_SHARED(stream))
        return;

    stream_shared = (LibBalsaMimeStreamShared *) stream;
    lock = stream_shared->lock;
    g_return_if_fail(lock->count > 0);

    g_mutex_lock(&lbmss_mutex);
    if (--lock->count == 0)
        g_cond_signal(&lbmss_cond);
    g_mutex_unlock(&lbmss_mutex);
}
コード例 #13
0
ファイル: mu-msg-part.c プロジェクト: otfrom/mu
gboolean
write_part_to_fd (GMimePart *part, int fd, GError **err)
{
	GMimeStream *stream;
	GMimeDataWrapper *wrapper;
	gboolean rv;

	stream = g_mime_stream_fs_new (fd);
	if (!GMIME_IS_STREAM(stream)) {
		g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME,
			     "failed to create stream");
		return FALSE;
	}
	g_mime_stream_fs_set_owner (GMIME_STREAM_FS(stream), FALSE);

	wrapper = g_mime_part_get_content_object (part);
	if (!GMIME_IS_DATA_WRAPPER(wrapper)) {
		g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME,
			     "failed to create wrapper");
		g_object_unref (stream);
		return FALSE;
	}
	g_object_ref (part); /* FIXME: otherwise, the unrefs below
			      * give errors...*/

	if (g_mime_data_wrapper_write_to_stream (wrapper, stream) == -1) {
		rv = FALSE;
		g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_GMIME,
			     "failed to write to stream");
	} else
		rv = TRUE;

	g_object_unref (wrapper);
	g_object_unref (stream);

	return rv;
}
コード例 #14
0
ファイル: libbalsa-gpgme.c プロジェクト: staceyson/balsa
/** \brief Decrypt data
 *
 * \param istream GMime input (encrypted) stream.
 * \param ostream GMime output (decrypted) stream.
 * \param protocol GpgME crypto protocol to use.
 * \param parent Parent window to be passed to the passphrase callback
 *        function.
 * \param error Filled with error information on error.
 * \return A new signature status object on success, or NULL on error.
 *
 * Decrypt and -if applicable- verify the signature of the passed data
 * stream.  If the input is not signed the returned signature status will
 * be GPG_ERR_NOT_SIGNED.
 */
GMimeGpgmeSigstat *
libbalsa_gpgme_decrypt(GMimeStream * crypted, GMimeStream * plain,
		       gpgme_protocol_t protocol, GtkWindow * parent,
		       GError ** error)
{
    gpgme_ctx_t ctx;
    gpgme_error_t err;
    gpgme_data_t plain_data;
    gpgme_data_t crypt_data;
    GMimeGpgmeSigstat *result;
    struct gpgme_data_cbs cbs = {
	(gpgme_data_read_cb_t) g_mime_gpgme_stream_rd,	/* read method */
	(gpgme_data_write_cb_t) g_mime_gpgme_stream_wr,	/* write method */
	NULL,			/* seek method */
	cb_data_release		/* release method */
    };

    /* paranoia checks */
    g_return_val_if_fail(GMIME_IS_STREAM(crypted), NULL);
    g_return_val_if_fail(GMIME_IS_STREAM(plain), NULL);
    g_return_val_if_fail(protocol == GPGME_PROTOCOL_OpenPGP ||
			 protocol == GPGME_PROTOCOL_CMS, NULL);

    /* create the GpgME context */
    if ((err =
	 gpgme_new_with_protocol(&ctx, protocol, parent,
				 error)) != GPG_ERR_NO_ERROR)
	return NULL;

    /* create the data streams */
    if ((err =
	 gpgme_data_new_from_cbs(&crypt_data, &cbs,
				 crypted)) != GPG_ERR_NO_ERROR) {
	g_set_error_from_gpgme(error, err,
			       _("could not get data from stream"));
	gpgme_release(ctx);
	return NULL;
    }
    if ((err =
	 gpgme_data_new_from_cbs(&plain_data, &cbs,
				 plain)) != GPG_ERR_NO_ERROR) {
	g_set_error_from_gpgme(error, err,
			       _("could not create new data object"));
	gpgme_data_release(crypt_data);
	gpgme_release(ctx);
	return NULL;
    }

    /* try to decrypt */
    if ((err =
	 gpgme_op_decrypt_verify(ctx, crypt_data,
				 plain_data)) != GPG_ERR_NO_ERROR) {
	g_set_error_from_gpgme(error, err, _("decryption failed"));
	result = NULL;
    } else {
	/* decryption successful, check for signature */
	result = g_mime_gpgme_sigstat_new_from_gpgme_ctx(ctx);
    }

    /* clean up */
    gpgme_data_release(plain_data);
    gpgme_data_release(crypt_data);
    gpgme_release(ctx);

    return result;
}
コード例 #15
0
ファイル: libbalsa-gpgme.c プロジェクト: staceyson/balsa
/** \brief Encrypt data
 *
 * \param recipients Array of User ID for which the matter shall be
 *        encrypted using their public keys.
 * \param sign_for User ID of the signer or NULL if the matter shall not be
 *        signed.  Note that combined signing and encryption is allowed \em
 *        only in OpenPGP single-part (i.e. RFC 2440) mode.
 * \param istream GMime input stream.
 * \param ostream GMime output stream.
 * \param protocol GpgME crypto protocol to use for encryption.
 * \param singlepart_mode TRUE indicates single-part mode (integrated
 *        signature), FALSE a detached signature.
 * \param trust_all_keys TRUE if all low-truct keys shall be accepted for
 *        encryption.  Otherwise, the function will use the global callback
 *        to ask the user whether a low-trust key shall be accepted.
 * \param parent Parent window to be passed to the callback functions.
 * \param error Filled with error information on error.
 * \return 0 on success, or -1 on error.
 *
 * Encrypt the passed matter and write the result to the output stream.
 * Combined signing and encryption is allowed for single-part OpenPGP mode
 * only.
 */
int
libbalsa_gpgme_encrypt(GPtrArray * recipients, const char *sign_for,
		       GMimeStream * istream, GMimeStream * ostream,
		       gpgme_protocol_t protocol, gboolean singlepart_mode,
		       gboolean trust_all_keys, GtkWindow * parent,
		       GError ** error)
{
    gpgme_ctx_t ctx;
    gpgme_error_t err;
    gpgme_key_t *rcpt_keys;
    gpgme_data_t plain;
    gpgme_data_t crypt;
    struct gpgme_data_cbs cbs = {
	(gpgme_data_read_cb_t) g_mime_gpgme_stream_rd,	/* read method */
	(gpgme_data_write_cb_t) g_mime_gpgme_stream_wr,	/* write method */
	NULL,			/* seek method */
	cb_data_release		/* release method */
    };

    /* paranoia checks */
    g_return_val_if_fail(recipients != NULL, -1);
    g_return_val_if_fail(GMIME_IS_STREAM(istream), -1);
    g_return_val_if_fail(GMIME_IS_STREAM(ostream), GPGME_MD_NONE);
    g_return_val_if_fail(protocol == GPGME_PROTOCOL_OpenPGP ||
			 protocol == GPGME_PROTOCOL_CMS, -1);

    /* create the GpgME context */
    if ((err =
	 gpgme_new_with_protocol(&ctx, protocol, parent,
				 error)) != GPG_ERR_NO_ERROR)
	return -1;

    /* sign & encrypt is valid only for single-part OpenPGP */
    if (sign_for != NULL
	&& (!singlepart_mode || protocol != GPGME_PROTOCOL_OpenPGP)) {
	if (error)
	    g_set_error(error, GPGME_ERROR_QUARK, GPG_ERR_INV_ENGINE,
			_
			("combined signing and encryption is defined only for RFC 2440"));
	gpgme_release(ctx);
	return -1;
    }

    /* if requested, find the secret key for "userid" */
    if (sign_for && !gpgme_add_signer(ctx, sign_for, parent, error)) {
	gpgme_release(ctx);
	return -1;
    }

    /* build the list of recipients */
    if (!
	(rcpt_keys =
	 gpgme_build_recipients(ctx, recipients, trust_all_keys, parent,
				error))) {
	gpgme_release(ctx);
	return -1;
    }

    /* create the data objects */
    if (protocol == GPGME_PROTOCOL_OpenPGP) {
	gpgme_set_armor(ctx, 1);
	gpgme_set_textmode(ctx, singlepart_mode);
    } else {
	gpgme_set_armor(ctx, 0);
	gpgme_set_textmode(ctx, 0);
    }
    if ((err =
	 gpgme_data_new_from_cbs(&plain, &cbs,
				 istream)) != GPG_ERR_NO_ERROR) {
	g_set_error_from_gpgme(error, err,
			       _("could not get data from stream"));
	release_keylist(rcpt_keys);
	gpgme_release(ctx);
	return -1;
    }
    if ((err =
	 gpgme_data_new_from_cbs(&crypt, &cbs,
				 ostream)) != GPG_ERR_NO_ERROR) {
	g_set_error_from_gpgme(error, err,
			       _("could not create new data object"));
	release_keylist(rcpt_keys);
	gpgme_data_release(plain);
	gpgme_release(ctx);
	return -1;
    }

    /* do the encrypt or sign and encrypt operation
     * Note: we set "always trust" here, as if we detected an untrusted key
     * earlier, the user already accepted it */
    if (sign_for)
	err =
	    gpgme_op_encrypt_sign(ctx, rcpt_keys,
				  GPGME_ENCRYPT_ALWAYS_TRUST, plain,
				  crypt);
    else
	err =
	    gpgme_op_encrypt(ctx, rcpt_keys, GPGME_ENCRYPT_ALWAYS_TRUST,
			     plain, crypt);

    release_keylist(rcpt_keys);
    gpgme_data_release(plain);
    gpgme_data_release(crypt);
    gpgme_release(ctx);
    if (err != GPG_ERR_NO_ERROR) {
	if (sign_for)
	    g_set_error_from_gpgme(error, err,
				   _("signing and encryption failed"));
	else
	    g_set_error_from_gpgme(error, err, _("encryption failed"));
	return -1;
    } else
	return 0;
}
コード例 #16
0
ファイル: libbalsa-gpgme.c プロジェクト: staceyson/balsa
/** \brief Sign data
 *
 * \param userid User ID of the signer.
 * \param istream GMime input stream.
 * \param ostream GMime output stream.
 * \param protocol GpgME crypto protocol of the signature.
 * \param singlepart_mode TRUE indicates single-part mode (integrated
 *        signature), FALSE a detached signature.
 * \param parent Parent window to be passed to the passphrase callback
 *        function.
 * \param error Filled with error information on error.
 * \return The hash algorithm used for creating the signature, or
 *         GPGME_MD_NONE on error.
 *
 * Sign the passed matter and write the detached signature or the signed
 * input and the signature, respectively, to the output stream.  The global
 * callback to read the passphrase for the user's private key will be
 * called by GpgME if no GPG Agent is running.
 */
gpgme_hash_algo_t
libbalsa_gpgme_sign(const gchar * userid, GMimeStream * istream,
		    GMimeStream * ostream, gpgme_protocol_t protocol,
		    gboolean singlepart_mode, GtkWindow * parent,
		    GError ** error)
{
    gpgme_error_t err;
    gpgme_ctx_t ctx;
    gpgme_sig_mode_t sig_mode;
    gpgme_data_t in;
    gpgme_data_t out;
    gpgme_hash_algo_t hash_algo;
    struct gpgme_data_cbs cbs = {
	(gpgme_data_read_cb_t) g_mime_gpgme_stream_rd,	/* read method */
	(gpgme_data_write_cb_t) g_mime_gpgme_stream_wr,	/* write method */
	NULL,			/* seek method */
	cb_data_release		/* release method */
    };

    /* paranoia checks */
    g_return_val_if_fail(GMIME_IS_STREAM(istream), GPGME_MD_NONE);
    g_return_val_if_fail(GMIME_IS_STREAM(ostream), GPGME_MD_NONE);
    g_return_val_if_fail(protocol == GPGME_PROTOCOL_OpenPGP ||
			 protocol == GPGME_PROTOCOL_CMS, GPGME_MD_NONE);

    /* create the GpgME context */
    if ((err =
	 gpgme_new_with_protocol(&ctx, protocol, parent,
				 error)) != GPG_ERR_NO_ERROR)
	return GPGME_MD_NONE;

    /* set the signature mode */
    if (singlepart_mode) {
	if (protocol == GPGME_PROTOCOL_OpenPGP)
	    sig_mode = GPGME_SIG_MODE_CLEAR;
	else
	    sig_mode = GPGME_SIG_MODE_NORMAL;
    } else
	sig_mode = GPGME_SIG_MODE_DETACH;

    /* find the secret key for the "sign_for" address */
    if (!gpgme_add_signer(ctx, userid, parent, error)) {
	gpgme_release(ctx);
	return GPGME_MD_NONE;
    }

    /* OpenPGP signatures are ASCII armored */
    gpgme_set_armor(ctx, protocol == GPGME_PROTOCOL_OpenPGP);

    /* create gpgme data objects */
    if ((err =
	 gpgme_data_new_from_cbs(&in, &cbs,
				 istream)) != GPG_ERR_NO_ERROR) {
	g_set_error_from_gpgme(error, err,
			       _("could not get data from stream"));
	gpgme_release(ctx);
	return GPGME_MD_NONE;
    }
    if ((err =
	 gpgme_data_new_from_cbs(&out, &cbs,
				 ostream)) != GPG_ERR_NO_ERROR) {
	g_set_error_from_gpgme(error, err,
			       _("could not create new data object"));
	gpgme_data_release(in);
	gpgme_release(ctx);
	return GPGME_MD_NONE;
    }

    /* sign and get the used hash algorithm */
    err = gpgme_op_sign(ctx, in, out, sig_mode);
    if (err != GPG_ERR_NO_ERROR) {
	g_set_error_from_gpgme(error, err, _("signing failed"));
	hash_algo = GPGME_MD_NONE;
    } else
	hash_algo = gpgme_op_sign_result(ctx)->signatures->hash_algo;

    /* clean up */
    gpgme_data_release(in);
    gpgme_data_release(out);
    gpgme_release(ctx);
    return hash_algo;
}
コード例 #17
0
ファイル: libbalsa-gpgme.c プロジェクト: staceyson/balsa
/** \brief Verify a signature
 *
 * \param content GMime stream of the signed matter.
 * \param sig_plain GMime signature stream for a detached signature, or the
 *        output stream for the checked matter in single-part mode.
 * \param protocol GpgME crypto protocol of the signature.
 * \param singlepart_mode TRUE indicates single-part mode (i.e. sig_plain
 *        an output stream).
 * \param error Filled with error information on error.
 * \return A new signature status object on success, or NULL on error.
 *
 * Verify a signature by calling GpgME on the passed streams, and create a
 * new signature object on success.
 */
GMimeGpgmeSigstat *
libbalsa_gpgme_verify(GMimeStream * content, GMimeStream * sig_plain,
		      gpgme_protocol_t protocol, gboolean singlepart_mode,
		      GError ** error)
{
    gpgme_error_t err;
    gpgme_ctx_t ctx;
    struct gpgme_data_cbs cbs = {
	(gpgme_data_read_cb_t) g_mime_gpgme_stream_rd,	/* read method */
	(gpgme_data_write_cb_t) g_mime_gpgme_stream_wr,	/* write method */
	NULL,			/* seek method */
	cb_data_release		/* release method */
    };
    gpgme_data_t cont_data;
    gpgme_data_t sig_plain_data;
    GMimeGpgmeSigstat *result;

    /* paranoia checks */
    g_return_val_if_fail(GMIME_IS_STREAM(content), NULL);
    g_return_val_if_fail(GMIME_IS_STREAM(sig_plain), NULL);
    g_return_val_if_fail(protocol == GPGME_PROTOCOL_OpenPGP ||
			 protocol == GPGME_PROTOCOL_CMS, NULL);

    /* create the GpgME context */
    if ((err =
	 gpgme_new_with_protocol(&ctx, protocol, NULL,
				 error)) != GPG_ERR_NO_ERROR)
	return NULL;

    /* create the message stream */
    if ((err =
	 gpgme_data_new_from_cbs(&cont_data, &cbs,
				 content)) != GPG_ERR_NO_ERROR) {
	g_set_error_from_gpgme(error, err,
			       _("could not get data from stream"));
	gpgme_release(ctx);
	return NULL;
    }

    /* create data object for the detached signature stream or the
     * "decrypted" plaintext */
    if ((err =
	 gpgme_data_new_from_cbs(&sig_plain_data, &cbs,
				 sig_plain)) != GPG_ERR_NO_ERROR) {
	g_set_error_from_gpgme(error, err,
			       _("could not get data from stream"));
	gpgme_data_release(cont_data);
	gpgme_release(ctx);
	return NULL;
    }

    /* verify the signature */
    if (singlepart_mode)
	err = gpgme_op_verify(ctx, cont_data, NULL, sig_plain_data);
    else
	err = gpgme_op_verify(ctx, sig_plain_data, cont_data, NULL);
    if (err != GPG_ERR_NO_ERROR) {
	g_set_error_from_gpgme(error, err,
			       _("signature verification failed"));
	result = g_mime_gpgme_sigstat_new();
	result->status = err;
	result->protocol = gpgme_get_protocol(ctx);
    } else
	result = g_mime_gpgme_sigstat_new_from_gpgme_ctx(ctx);

    /* release gmgme data buffers, destroy the context and return the
     * signature object */
    gpgme_data_release(cont_data);
    gpgme_data_release(sig_plain_data);
    gpgme_release(ctx);
    return result;
}