static gint
do_perf (gint argc, gchar **argv)
{
	CamelIndex *idx;
	DIR *dir;
	const gchar *path = "/home/notzed/evolution/local/Inbox/mbox/cur";
	struct dirent *d;
	CamelStream *null, *filter, *stream;
	CamelMimeFilter *filter_index;
	gchar *name;
	CamelIndexName *idn;

	dir = opendir (path);
	if (dir == NULL) {
		perror("open dir");
		return 1;
	}

	idx = (CamelIndex *) camel_text_index_new (
		"/tmp/index", O_TRUNC|O_CREAT|O_RDWR);
	if (idx == NULL) {
		perror("open index");
		closedir (dir);
		return 1;
	}

	null = camel_stream_null_new ();
	filter = camel_stream_filter_new (null);
	g_object_unref (null);
	filter_index = camel_mime_filter_index_new (idx);
	camel_stream_filter_add ((CamelStreamFilter *)filter, filter_index);

	while ((d = readdir (dir))) {
		printf("indexing '%s'\n", d->d_name);

		idn = camel_index_add_name (idx, d->d_name);
		camel_mime_filter_index_set_name (
			CAMEL_MIME_FILTER_INDEX (filter_index), idn);
		name = g_strdup_printf("%s/%s", path, d->d_name);
		stream = camel_stream_fs_new_with_name (name, O_RDONLY, 0, NULL);
		camel_stream_write_to_stream (stream, filter, NULL, NULL);
		g_object_unref (stream);
		g_free (name);

		camel_index_write_name (idx, idn);
		g_object_unref (idn);
		camel_mime_filter_index_set_name (
			CAMEL_MIME_FILTER_INDEX (filter_index), NULL);
	}

	closedir (dir);

	camel_index_sync (idx);
	g_object_unref (idx);

	g_object_unref (filter);
	g_object_unref (filter_index);

	return 0;
}
static gboolean
data_wrapper_construct_from_stream_sync (CamelDataWrapper *data_wrapper,
                                         CamelStream *stream,
                                         GCancellable *cancellable,
                                         GError **error)
{
	CamelStream *memory_stream;
	gssize bytes_written;

	camel_data_wrapper_lock (
		data_wrapper, CAMEL_DATA_WRAPPER_STREAM_LOCK);

	/* Check for cancellation after locking. */
	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
		camel_data_wrapper_unlock (
			data_wrapper, CAMEL_DATA_WRAPPER_STREAM_LOCK);
		return FALSE;
	}

	if (G_IS_SEEKABLE (stream)) {
		if (!g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, cancellable, error)) {
			camel_data_wrapper_unlock (data_wrapper, CAMEL_DATA_WRAPPER_STREAM_LOCK);
			return FALSE;
		}
	}

	/* Wipe any previous contents from our byte array. */
	g_byte_array_set_size (data_wrapper->priv->byte_array, 0);

	memory_stream = camel_stream_mem_new ();

	/* We retain ownership of the byte array. */
	camel_stream_mem_set_byte_array (
		CAMEL_STREAM_MEM (memory_stream),
		data_wrapper->priv->byte_array);

	/* Transfer incoming contents to our byte array. */
	bytes_written = camel_stream_write_to_stream (
		stream, memory_stream, cancellable, error);

	g_object_unref (memory_stream);

	camel_data_wrapper_unlock (
		data_wrapper, CAMEL_DATA_WRAPPER_STREAM_LOCK);

	return (bytes_written >= 0);
}
/**
 * camel_imap_message_cache_insert_stream:
 * @cache: the cache
 * @uid: UID of the message data to cache
 * @part_spec: the IMAP part_spec of the data
 * @data_stream: the stream to cache
 *
 * Caches the provided data into @cache.
 **/
void
camel_imap_message_cache_insert_stream (CamelImapMessageCache *cache,
					const char *uid, const char *part_spec,
					CamelStream *data_stream, CamelException *ex)
{
	char *path, *key;
	CamelStream *stream;

	stream = insert_setup (cache, uid, part_spec, &path, &key, ex);
	if (!stream)
		return;

	if (camel_stream_write_to_stream (data_stream, stream) == -1) {
		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
				      _("Failed to cache message %s: %s"),
				      uid, g_strerror (errno));
		insert_abort (path, stream);
	} else {
		insert_finish (cache, uid, path, key, stream);
		camel_object_unref (CAMEL_OBJECT (stream));
	}
}
static gssize
data_wrapper_write_to_stream_sync (CamelDataWrapper *data_wrapper,
                                   CamelStream *stream,
                                   GCancellable *cancellable,
                                   GError **error)
{
	gssize ret;

	if (data_wrapper->stream == NULL) {
		g_set_error (
			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
			_("No stream available"));
		return -1;
	}

	camel_data_wrapper_lock (data_wrapper, CAMEL_DATA_WRAPPER_STREAM_LOCK);

	/* Check for cancellation after locking. */
	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
		camel_data_wrapper_unlock (
			data_wrapper, CAMEL_DATA_WRAPPER_STREAM_LOCK);
		return -1;
	}

	if (camel_stream_reset (data_wrapper->stream, error) == -1) {
		camel_data_wrapper_unlock (
			data_wrapper, CAMEL_DATA_WRAPPER_STREAM_LOCK);
		return -1;
	}

	ret = camel_stream_write_to_stream (
		data_wrapper->stream, stream, cancellable, error);

	camel_data_wrapper_unlock (data_wrapper, CAMEL_DATA_WRAPPER_STREAM_LOCK);

	return ret;
}
static CamelStream *
nntp_folder_download_message (CamelNNTPFolder *nntp_folder, const char *id, const char *msgid, CamelException *ex)
{
	CamelNNTPStore *nntp_store = (CamelNNTPStore *) ((CamelFolder *) nntp_folder)->parent_store;
	CamelStream *stream = NULL;
	int ret;
	char *line;

	ret = camel_nntp_command (nntp_store, ex, nntp_folder, &line, "article %s", id);
	if (ret == 220) {
		stream = camel_data_cache_add (nntp_store->cache, "cache", msgid, NULL);
		if (stream) {
			if (camel_stream_write_to_stream ((CamelStream *) nntp_store->stream, stream) == -1)
				goto fail;
			if (camel_stream_reset (stream) == -1)
				goto fail;
		} else {
			stream = (CamelStream *) nntp_store->stream;
			camel_object_ref (stream);
		}
	} else if (ret == 423 || ret == 430) {
		camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message %s: %s"), msgid, line);
	} else if (ret != -1) {
		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot get message %s: %s"), msgid, line);
	}

	return stream;

 fail:
	if (errno == EINTR)
		camel_exception_setv (ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled"));
	else
		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot get message %s: %s"), msgid, g_strerror (errno));

	return NULL;
}
static gssize
data_wrapper_write_to_stream_sync (CamelDataWrapper *data_wrapper,
                                   CamelStream *stream,
                                   GCancellable *cancellable,
                                   GError **error)
{
	CamelStream *memory_stream;
	gssize ret;

	camel_data_wrapper_lock (
		data_wrapper, CAMEL_DATA_WRAPPER_STREAM_LOCK);

	/* Check for cancellation after locking. */
	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
		camel_data_wrapper_unlock (
			data_wrapper, CAMEL_DATA_WRAPPER_STREAM_LOCK);
		return -1;
	}

	memory_stream = camel_stream_mem_new ();

	/* We retain ownership of the byte array. */
	camel_stream_mem_set_byte_array (
		CAMEL_STREAM_MEM (memory_stream),
		data_wrapper->priv->byte_array);

	ret = camel_stream_write_to_stream (
		memory_stream, stream, cancellable, error);

	g_object_unref (memory_stream);

	camel_data_wrapper_unlock (
		data_wrapper, CAMEL_DATA_WRAPPER_STREAM_LOCK);

	return ret;
}
Beispiel #7
0
static int
pipe_to_sa_full (CamelMimeMessage *msg, const char *in, char **argv, int rv_err, int wait_for_termination, GByteArray *output_buffer, GError **error)
{
	int result, status, errnosav, fds[2], out_fds[2];
	CamelStream *stream;
	char *program;
	pid_t pid;


	if (camel_debug_start ("junk")) {
		int i;

		printf ("pipe_to_sa ");
		for (i = 0; argv[i]; i++)
			printf ("%s ", argv[i]);
		printf ("\n");
		camel_debug_end ();
	}

	program = g_find_program_in_path (argv [0]);
	if (program == NULL) {
		d(printf ("program not found, returning %d\n", rv_err));
		g_set_error (error, EM_JUNK_ERROR, rv_err, _("SpamAssassin not found, code: %d"), rv_err);
		return rv_err;
	}
	g_free (program);

	if (pipe (fds) == -1) {
		errnosav = errno;
		d(printf ("failed to create a pipe (for use with spamassassin: %s\n", strerror (errno)));
		g_set_error (error, EM_JUNK_ERROR, errnosav, _("Failed to create pipe: %s"), strerror (errnosav));
		errno = errnosav;
		return rv_err;
	}

	if (output_buffer && pipe (out_fds) == -1) {
		errnosav = errno;
		d(printf ("failed to create a pipe (for use with spamassassin: %s\n", strerror (errno)));
		g_set_error (error, EM_JUNK_ERROR, errnosav, _("Failed to create pipe: %s"), strerror (errnosav));
		close (fds [0]);
		close (fds [1]);
		errno = errnosav;
		return rv_err;
	}

	if (!(pid = fork ())) {
		/* child process */
		int maxfd, fd, nullfd;

		nullfd = open ("/dev/null", O_WRONLY);

		if (dup2 (fds[0], STDIN_FILENO) == -1 ||
		    dup2 (nullfd, STDERR_FILENO) == -1 ||
		    (output_buffer == NULL && dup2 (nullfd, STDOUT_FILENO) == -1) ||
		    (output_buffer != NULL && dup2 (out_fds[1], STDOUT_FILENO) == -1))
			_exit (rv_err & 0377);
		close (fds [0]);
		if (output_buffer)
			close (out_fds [1]);

		setsid ();

		maxfd = sysconf (_SC_OPEN_MAX);
		for (fd = 3; fd < maxfd; fd++)
			fcntl (fd, F_SETFD, FD_CLOEXEC);

		execvp (argv[0], argv);
		_exit (rv_err & 0377);
	} else if (pid < 0) {
		errnosav = errno;
		close (fds[0]);
		close (fds[1]);
		if (output_buffer) {
			close (out_fds [0]);
			close (out_fds [1]);
		}
		if (errnosav != 0 && errnosav != -1)
			g_set_error (error, EM_JUNK_ERROR, errnosav, _("Error after fork: %s"), strerror (errnosav));
		errno = errnosav;
		return rv_err;
	}

	/* parent process */
	close (fds[0]);
	if (output_buffer)
		close (out_fds [1]);

	if (msg) {
		stream = camel_stream_fs_new_with_fd (fds[1]);

		camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (msg), stream);
		camel_stream_flush (stream);
		camel_stream_close (stream);
		camel_object_unref (stream);
	} else if (in) {
		camel_write (fds[1], in, strlen (in));
		close (fds[1]);
	}

	if (output_buffer) {
		CamelStreamMem *memstream;

		stream = camel_stream_fs_new_with_fd (out_fds[0]);

		memstream = (CamelStreamMem *) camel_stream_mem_new ();
		camel_stream_mem_set_byte_array (memstream, output_buffer);

		camel_stream_write_to_stream (stream, (CamelStream *) memstream);
		camel_object_unref (stream);
		g_byte_array_append (output_buffer, (unsigned char *)"", 1);

		d(printf ("child process output: %s len: %d\n", output_buffer->data, output_buffer->len));
	}

	if (wait_for_termination) {
		int res;

		d(printf ("wait for child %d termination\n", pid));
		result = waitpid (pid, &status, 0);

		d(printf ("child %d terminated with result %d status %d exited %d exitstatus %d\n", pid, result, status, WIFEXITED (status), WEXITSTATUS (status)));

		if (result == -1 && errno == EINTR) {
			/* child process is hanging... */
			kill (pid, SIGTERM);
			sleep (1);
			result = waitpid (pid, &status, WNOHANG);
			if (result == 0) {
				/* ...still hanging, set phasers to KILL */
				kill (pid, SIGKILL);
				sleep (1);
				result = waitpid (pid, &status, WNOHANG);
					g_set_error (error, EM_JUNK_ERROR, -2, _("SpamAssassin child process does not respond, killing..."));
			} else
				g_set_error (error, EM_JUNK_ERROR, -3, _("Wait for Spamassassin child process interrupted, terminating..."));
		}

		if (result != -1 && WIFEXITED (status))
			res = WEXITSTATUS (status);
		else
			res = rv_err;

		if (res != 0)
			g_set_error (error, EM_JUNK_ERROR, res, _("Pipe to SpamAssassin failed, error code: %d"), res);

		return res;
	} else
		return 0;
}
static CamelCipherValidity *
sm_verify_cmsg(CamelCipherContext *context, NSSCMSMessage *cmsg, CamelStream *extstream, CamelException *ex)
{
	struct _CamelSMIMEContextPrivate *p = ((CamelSMIMEContext *)context)->priv;
	NSSCMSSignedData *sigd = NULL;
	NSSCMSEnvelopedData *envd;
	NSSCMSEncryptedData *encd;
	SECAlgorithmID **digestalgs;
	NSSCMSDigestContext *digcx;
	int count, i, nsigners, j;
	SECItem **digests;
	PLArenaPool *poolp = NULL;
	CamelStreamMem *mem;
	NSSCMSVerificationStatus status;
	CamelCipherValidity *valid;
	GString *description;

	description = g_string_new("");
	valid = camel_cipher_validity_new();
	camel_cipher_validity_set_valid(valid, TRUE);
	status = NSSCMSVS_Unverified;

	/* NB: this probably needs to go into a decoding routine that can be used for processing
	   enveloped data too */
	count = NSS_CMSMessage_ContentLevelCount(cmsg);
	for (i = 0; i < count; i++) {
		NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(cmsg, i);
		SECOidTag typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);

		switch (typetag) {
		case SEC_OID_PKCS7_SIGNED_DATA:
			sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo);
			if (sigd == NULL) {
				camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("No signed data in signature"));
				goto fail;
			}

			/* need to build digests of the content */
			if (!NSS_CMSSignedData_HasDigests(sigd)) {
				if (extstream == NULL) {
					camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Digests missing from enveloped data"));
					goto fail;
				}

				if ((poolp = PORT_NewArena(1024)) == NULL) {
					camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, g_strerror (ENOMEM));
					goto fail;
				}

				digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd);

				digcx = NSS_CMSDigestContext_StartMultiple(digestalgs);
				if (digcx == NULL) {
					camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot calculate digests"));
					goto fail;
				}

				mem = (CamelStreamMem *)camel_stream_mem_new();
				camel_stream_write_to_stream(extstream, (CamelStream *)mem);
				NSS_CMSDigestContext_Update(digcx, mem->buffer->data, mem->buffer->len);
				camel_object_unref(mem);

				if (NSS_CMSDigestContext_FinishMultiple(digcx, poolp, &digests) != SECSuccess) {
					camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot calculate digests"));
					goto fail;
				}

				if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests) != SECSuccess) {
					camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot set message digests"));
					goto fail;
				}

				PORT_FreeArena(poolp, PR_FALSE);
				poolp = NULL;
			}

			/* import all certificates present */
			if (NSS_CMSSignedData_ImportCerts(sigd, p->certdb, certUsageEmailSigner, PR_TRUE) != SECSuccess) {
				camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Certificate import failed"));
				goto fail;
			}

			if (NSS_CMSSignedData_ImportCerts(sigd, p->certdb, certUsageEmailRecipient, PR_TRUE) != SECSuccess) {
				camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Certificate import failed"));
				goto fail;
			}

			/* check for certs-only message */
			nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
			if (nsigners == 0) {

				/* already imported certs above, not sure what usage we should use here or if this isn't handled above */
				if (NSS_CMSSignedData_VerifyCertsOnly(sigd, p->certdb, certUsageEmailSigner) != SECSuccess) {
					g_string_printf(description, _("Certificate is the only message, cannot verify certificates"));
				} else {
					status = NSSCMSVS_GoodSignature;
					g_string_printf(description, _("Certificate is the only message, certificates imported and verified"));
				}
			} else {
				if (!NSS_CMSSignedData_HasDigests(sigd)) {
					camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot find signature digests"));
					goto fail;
				}

				for (j = 0; j < nsigners; j++) {
					NSSCMSSignerInfo *si;
					char *cn, *em;

					si = NSS_CMSSignedData_GetSignerInfo(sigd, j);
					NSS_CMSSignedData_VerifySignerInfo(sigd, j, p->certdb, certUsageEmailSigner);

					status = NSS_CMSSignerInfo_GetVerificationStatus(si);

					cn = NSS_CMSSignerInfo_GetSignerCommonName(si);
					em = NSS_CMSSignerInfo_GetSignerEmailAddress(si);

					g_string_append_printf(description, _("Signer: %s <%s>: %s\n"),
							       cn?cn:"<unknown>", em?em:"<unknown>",
							       sm_status_description(status));

					camel_cipher_validity_add_certinfo(valid, CAMEL_CIPHER_VALIDITY_SIGN, cn, em);

					if (cn)
						PORT_Free(cn);
					if (em)
						PORT_Free(em);

					if (status != NSSCMSVS_GoodSignature)
						camel_cipher_validity_set_valid(valid, FALSE);
				}
			}
			break;
		case SEC_OID_PKCS7_ENVELOPED_DATA:
			envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo);
			break;
		case SEC_OID_PKCS7_ENCRYPTED_DATA:
			encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo);
			break;
		case SEC_OID_PKCS7_DATA:
			break;
		default:
			break;
		}
	}

	camel_cipher_validity_set_valid(valid, status == NSSCMSVS_GoodSignature);
	camel_cipher_validity_set_description(valid, description->str);
	g_string_free(description, TRUE);

	return valid;

fail:
	camel_cipher_validity_free(valid);
	g_string_free(description, TRUE);

	return NULL;
}
static void
test_filter(CamelMimeFilter *f, const char *inname, const char *outname)
{
	CamelStreamMem *in, *out;
	CamelStream *indisk, *outdisk, *filter;
	int id;

	camel_test_push("Data file `%s'", inname);

	camel_test_push("setup");

	indisk = camel_stream_fs_new_with_name(inname, O_RDONLY, 0);
	check(indisk);
	outdisk = camel_stream_fs_new_with_name(outname, O_RDONLY, 0);
	check(outdisk);

	out = (CamelStreamMem *)camel_stream_mem_new();
	check(camel_stream_write_to_stream(outdisk, (CamelStream *)out) > 0);

	camel_test_pull();

	camel_test_push("reading through filter stream");

	in = (CamelStreamMem *)camel_stream_mem_new();

	filter = (CamelStream *)camel_stream_filter_new_with_stream(indisk);
	check_count(indisk, 2);
	id = camel_stream_filter_add((CamelStreamFilter *)filter, f);
	check_count(f, 2);

	check(camel_stream_write_to_stream(filter, (CamelStream *)in) > 0);
	check_msg(in->buffer->len == out->buffer->len
		  && memcmp(in->buffer->data, out->buffer->data, in->buffer->len) == 0,
		  "Buffer content mismatch, %d != %d, in = '%.*s' != out = '%.*s'", in->buffer->len, out->buffer->len,
		  in->buffer->len, in->buffer->data, out->buffer->len, out->buffer->data);

	camel_test_pull();

	camel_stream_filter_remove((CamelStreamFilter *)filter, id);
	check_count(f, 1);
	camel_mime_filter_reset(f);

	check_unref(filter, 1);
	check_count(indisk, 1);
	check_count(f, 1);
	check_unref(in, 1);

	check(camel_stream_reset(indisk) == 0);

	camel_test_push("writing through filter stream");

	in = (CamelStreamMem *)camel_stream_mem_new();
	filter = (CamelStream *)camel_stream_filter_new_with_stream((CamelStream *)in);
	check_count(in, 2);
	id = camel_stream_filter_add((CamelStreamFilter *)filter, f);
	check_count(f, 2);

	check(camel_stream_write_to_stream(indisk, filter) > 0);
	check(camel_stream_flush(filter) == 0);
	check_msg(in->buffer->len == out->buffer->len
		  && memcmp(in->buffer->data, out->buffer->data, in->buffer->len) == 0,
		  "Buffer content mismatch, %d != %d, in = '%.*s' != out = '%.*s'", in->buffer->len, out->buffer->len,
		  in->buffer->len, in->buffer->data, out->buffer->len, out->buffer->data);

	camel_stream_filter_remove((CamelStreamFilter *)filter, id);
	check_unref(filter, 1);
	check_unref(in, 1);
	check_unref(indisk, 1);
	check_unref(outdisk, 1);
	check_unref(out, 1);

	camel_test_pull();

	camel_test_pull();
}
static gssize
multipart_signed_write_to_stream_sync (CamelDataWrapper *data_wrapper,
                                       CamelStream *stream,
                                       GCancellable *cancellable,
                                       GError **error)
{
	CamelMultipartSigned *mps = (CamelMultipartSigned *) data_wrapper;
	CamelMultipart *mp = (CamelMultipart *) mps;
	GByteArray *byte_array;
	const gchar *boundary;
	gssize total = 0;
	gssize count;
	gchar *content;

	byte_array = camel_data_wrapper_get_byte_array (data_wrapper);

	/* we have 3 basic cases:
	 * 1. constructed, we write out the data wrapper stream we got
	 * 2. signed content, we create and write out a new stream
	 * 3. invalid
	*/

	/* 1 */
	/* FIXME: locking? */
	if (byte_array->len > 0) {
		return camel_stream_write (
			stream, (gchar *) byte_array->data,
			byte_array->len, cancellable, error);
	}

	/* 3 */
	if (mps->contentraw == NULL) {
		g_set_error (
			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
			_("No content available"));
		return -1;
	}

	/* 3 */
	if (mps->signature == NULL) {
		g_set_error (
			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
			_("No signature available"));
		return -1;
	}

	/* 2 */
	boundary = camel_multipart_get_boundary (mp);
	if (mp->preface) {
		count = camel_stream_write_string (
			stream, mp->preface, cancellable, error);
		if (count == -1)
			return -1;
		total += count;
	}

	/* first boundary */
	content = g_strdup_printf ("\n--%s\n", boundary);
	count = camel_stream_write_string (
		stream, content, cancellable, error);
	g_free (content);
	if (count == -1)
		return -1;
	total += count;

	/* output content part */
	/* XXX Both CamelGpgContext and CamelSMIMEContext set this
	 *     to a memory stream, so assume it's always seekable. */
	g_seekable_seek (
		G_SEEKABLE (mps->contentraw), 0, G_SEEK_SET, NULL, NULL);
	count = camel_stream_write_to_stream (
		mps->contentraw, stream, cancellable, error);
	if (count == -1)
		return -1;
	total += count;

	/* boundary */
	content = g_strdup_printf ("\n--%s\n", boundary);
	count = camel_stream_write_string (
		stream, content, cancellable, error);
	g_free (content);
	if (count == -1)
		return -1;
	total += count;

	/* signature */
	count = camel_data_wrapper_write_to_stream_sync (
		CAMEL_DATA_WRAPPER (mps->signature),
		stream, cancellable, error);
	if (count == -1)
		return -1;
	total += count;

	/* write the terminating boudary delimiter */
	content = g_strdup_printf ("\n--%s--\n", boundary);
	count = camel_stream_write_string (
		stream, content, cancellable, error);
	g_free (content);
	if (count == -1)
		return -1;
	total += count;

	/* and finally the postface */
	if (mp->postface) {
		count = camel_stream_write_string (
			stream, mp->postface, cancellable, error);
		if (count == -1)
			return -1;
		total += count;
	}

	return total;
}
static CamelStream *
nntp_folder_download_message (CamelNNTPFolder *nntp_folder,
                              const gchar *id,
                              const gchar *msgid,
                              GCancellable *cancellable,
                              GError **error)
{
	CamelFolder *folder;
	CamelStore *parent_store;
	CamelDataCache *nntp_cache;
	CamelNNTPStore *nntp_store;
	CamelNNTPStream *nntp_stream = NULL;
	CamelStream *stream = NULL;
	gint ret;
	gchar *line;

	folder = CAMEL_FOLDER (nntp_folder);
	parent_store = camel_folder_get_parent_store (folder);

	nntp_store = CAMEL_NNTP_STORE (parent_store);
	nntp_cache = camel_nntp_store_ref_cache (nntp_store);

	ret = camel_nntp_command (
		nntp_store, cancellable, error,
		nntp_folder, &line, "article %s", id);

	if (ret == 220) {
		GIOStream *base_stream;

		nntp_stream = camel_nntp_store_ref_stream (nntp_store);

		base_stream = camel_data_cache_add (
			nntp_cache, "cache", msgid, NULL);
		if (base_stream != NULL) {
			gboolean success;

			stream = camel_stream_new (base_stream);
			g_object_unref (base_stream);

			success = (camel_stream_write_to_stream (
				CAMEL_STREAM (nntp_stream),
				stream, cancellable, error) != -1);
			if (!success)
				goto fail;

			success = g_seekable_seek (
				G_SEEKABLE (stream), 0,
				G_SEEK_SET, cancellable, error);
			if (!success)
				goto fail;
		} else {
			stream = g_object_ref (nntp_stream);
		}

	} else if (ret == 423 || ret == 430) {
		g_set_error (
			error, CAMEL_FOLDER_ERROR,
			CAMEL_FOLDER_ERROR_INVALID_UID,
			_("Cannot get message %s: %s"), msgid, line);

	} else if (ret != -1) {
		g_set_error (
			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
			_("Cannot get message %s: %s"), msgid, line);
	}

	goto exit;

fail:
	camel_data_cache_remove (nntp_cache, "cache", msgid, NULL);
	g_prefix_error (error, _("Cannot get message %s: "), msgid);

	g_clear_object (&stream);

exit:
	g_clear_object (&nntp_cache);
	g_clear_object (&nntp_stream);

	return stream;
}