smtp_data (CamelSmtpTransport *transport,
	   CamelStreamBuffer *istream,
	   CamelStream *ostream,
           CamelMimeMessage *message,
           GCancellable *cancellable,
           GError **error)
{
	CamelNameValueArray *previous_headers;
	const gchar *header_name = NULL, *header_value = NULL;
	CamelBestencEncoding enctype = CAMEL_BESTENC_8BIT;
	CamelStream *filtered_stream;
	gchar *cmdbuf, *respbuf = NULL;
	CamelMimeFilter *filter;
	gsize bytes_written;
	gint ret;
	guint ii;

	/* If the server doesn't support 8BITMIME, set our required encoding to be 7bit */
	if (!(transport->flags & CAMEL_SMTP_TRANSPORT_8BITMIME))
		enctype = CAMEL_BESTENC_7BIT;

	/* FIXME: should we get the best charset too?? */
	/* Changes the encoding of all mime parts to fit within our required
	 * encoding type and also force any text parts with long lines (longer
	 * than 998 octets) to wrap by QP or base64 encoding them. */
	camel_mime_message_set_best_encoding (
		message, CAMEL_BESTENC_GET_ENCODING, enctype);

	cmdbuf = g_strdup ("DATA\r\n");

	d (fprintf (stderr, "[SMTP] sending: %s", cmdbuf));

	if (camel_stream_write_string (ostream, cmdbuf, cancellable, error) == -1) {
		g_free (cmdbuf);
		g_prefix_error (error, _("DATA command failed: "));
		camel_service_disconnect_sync (
			CAMEL_SERVICE (transport),
			FALSE, cancellable, NULL);
		return FALSE;
	}
	g_free (cmdbuf);

	respbuf = camel_stream_buffer_read_line (istream, cancellable, error);
	d (fprintf (stderr, "[SMTP] received: %s\n", respbuf ? respbuf : "(null)"));
	if (respbuf == NULL) {
		g_prefix_error (error, _("DATA command failed: "));
		camel_service_disconnect_sync (
			CAMEL_SERVICE (transport),
			FALSE, cancellable, NULL);
		return FALSE;
	}
	if (strncmp (respbuf, "354", 3) != 0) {
		/* We should have gotten instructions on how to use the DATA
		 * command: 354 Enter mail, end with "." on a line by itself
		 */
		smtp_set_error (transport, istream, respbuf, cancellable, error);
		g_prefix_error (error, _("DATA command failed: "));
		g_free (respbuf);
		return FALSE;
	}

	g_free (respbuf);
	respbuf = NULL;

	/* unlink the bcc headers and keep a copy of them */
	previous_headers = camel_medium_dup_headers (CAMEL_MEDIUM (message));
	camel_medium_remove_header (CAMEL_MEDIUM (message), "Bcc");

	/* find out how large the message is... */
	bytes_written = camel_data_wrapper_calculate_size_sync (CAMEL_DATA_WRAPPER (message), NULL, NULL);

	/* Set the upload timeout to an equal of 512 bytes per second */
	smtp_maybe_update_socket_timeout (ostream, bytes_written / 512);

	filtered_stream = camel_stream_filter_new (ostream);

	/* setup progress reporting for message sending... */
	filter = camel_mime_filter_progress_new (cancellable, bytes_written);
	camel_stream_filter_add (
		CAMEL_STREAM_FILTER (filtered_stream), filter);
	g_object_unref (filter);

	/* setup LF->CRLF conversion */
	filter = camel_mime_filter_crlf_new (
		CAMEL_MIME_FILTER_CRLF_ENCODE,
		CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
	camel_stream_filter_add (
		CAMEL_STREAM_FILTER (filtered_stream), filter);
	g_object_unref (filter);

	/* write the message */
	ret = camel_data_wrapper_write_to_stream_sync (
		CAMEL_DATA_WRAPPER (message),
		filtered_stream, cancellable, error);

	if (camel_debug ("smtp")) {
		CamelStream *mem_stream, *sec_filtered_stream;
		GError *local_error = NULL;

		mem_stream = camel_stream_mem_new ();
		sec_filtered_stream = camel_stream_filter_new (mem_stream);

		filter = camel_mime_filter_crlf_new (
			CAMEL_MIME_FILTER_CRLF_ENCODE,
			CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
		camel_stream_filter_add (CAMEL_STREAM_FILTER (sec_filtered_stream), filter);
		g_object_unref (filter);

		if (camel_data_wrapper_write_to_stream_sync (CAMEL_DATA_WRAPPER (message), sec_filtered_stream, cancellable, &local_error) == -1) {
			printf ("[SMTP] Failed to write message to memory stream: %s\n", local_error ? local_error->message : "Unknown error");
		} else {
			GByteArray *bytes;

			camel_stream_flush (sec_filtered_stream, cancellable, NULL);

			bytes = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem_stream));

			if (!bytes) {
				printf ("[SMTP] Failed to get filtered stream bytes\n");
			} else {
				printf ("[SMTP] sent data (re-encoded):\n%.*s\n", (gint) bytes->len, (const gchar *) bytes->data);
			}
		}

		g_clear_object (&sec_filtered_stream);
		g_clear_object (&mem_stream);
		g_clear_error (&local_error);
	}

	/* restore the bcc headers */
	for (ii = 0; camel_name_value_array_get (previous_headers, ii, &header_name, &header_value); ii++) {
		if (!g_ascii_strcasecmp (header_name, "Bcc")) {
			camel_medium_add_header (CAMEL_MEDIUM (message), header_name, header_value);
		}
	}

	camel_name_value_array_free (previous_headers);

	if (ret == -1) {
		g_prefix_error (error, _("DATA command failed: "));

		g_object_unref (filtered_stream);

		camel_service_disconnect_sync (
			CAMEL_SERVICE (transport),
			FALSE, cancellable, NULL);
		return FALSE;
	}

	camel_stream_flush (filtered_stream, cancellable, NULL);
	g_object_unref (filtered_stream);

	/* terminate the message body */

	d (fprintf (stderr, "[SMTP] sending: \\r\\n.\\r\\n\n"));

	if (camel_stream_write (ostream, "\r\n.\r\n", 5, cancellable, error) == -1) {
		g_prefix_error (error, _("DATA command failed: "));
		camel_service_disconnect_sync (
			CAMEL_SERVICE (transport),
			FALSE, cancellable, NULL);
		return FALSE;
	}

	do {
		/* Check for "250 Sender OK..." */
		g_free (respbuf);
		respbuf = camel_stream_buffer_read_line (istream, cancellable, error);
		d (fprintf (stderr, "[SMTP] received: %s\n", respbuf ? respbuf : "(null)"));
		if (respbuf == NULL) {
			g_prefix_error (error, _("DATA command failed: "));
			camel_service_disconnect_sync (
				CAMEL_SERVICE (transport),
				FALSE, cancellable, NULL);
			return FALSE;
		}
		if (strncmp (respbuf, "250", 3) != 0) {
			smtp_set_error (transport, istream, respbuf, cancellable, error);
			g_prefix_error (error, _("DATA command failed: "));
			g_free (respbuf);
			return FALSE;
		}
	} while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
	g_free (respbuf);

	return TRUE;
}
smtp_data (CamelSmtpTransport *transport,
	   CamelStreamBuffer *istream,
	   CamelStream *ostream,
           CamelMimeMessage *message,
           GCancellable *cancellable,
           GError **error)
{
	CamelNameValueArray *previous_headers;
	const gchar *header_name = NULL, *header_value = NULL;
	CamelBestencEncoding enctype = CAMEL_BESTENC_8BIT;
	CamelStream *filtered_stream;
	gchar *cmdbuf, *respbuf = NULL;
	CamelMimeFilter *filter;
	gsize bytes_written;
	gint ret;
	guint ii;

	 
	if (!(transport->flags & CAMEL_SMTP_TRANSPORT_8BITMIME))
		enctype = CAMEL_BESTENC_7BIT;

	 
	 
	camel_mime_message_set_best_encoding (
		message, CAMEL_BESTENC_GET_ENCODING, enctype);

	cmdbuf = g_strdup ("DATA\r\n");

	d (fprintf (stderr, "[SMTP] sending: %s", cmdbuf));

	if (camel_stream_write_string (ostream, cmdbuf, cancellable, error) == -1) {
		g_free (cmdbuf);
		g_prefix_error (error, _("DATA command failed: "));
		camel_service_disconnect_sync (
			CAMEL_SERVICE (transport),
			FALSE, cancellable, NULL);
		return FALSE;
	}
	g_free (cmdbuf);

	respbuf = camel_stream_buffer_read_line (istream, cancellable, error);
	d (fprintf (stderr, "[SMTP] received: %s\n", respbuf ? respbuf : "(null)"));
	if (respbuf == NULL) {
		g_prefix_error (error, _("DATA command failed: "));
		camel_service_disconnect_sync (
			CAMEL_SERVICE (transport),
			FALSE, cancellable, NULL);
		return FALSE;
	}
	if (strncmp (respbuf, "354", 3) != 0) {
		 
		smtp_set_error (transport, istream, respbuf, cancellable, error);
		g_prefix_error (error, _("DATA command failed: "));
		g_free (respbuf);
		return FALSE;
	}

	g_free (respbuf);
	respbuf = NULL;

	 
	previous_headers = camel_medium_dup_headers (CAMEL_MEDIUM (message));
	camel_medium_remove_header (CAMEL_MEDIUM (message), "Bcc");

	 
	bytes_written = camel_data_wrapper_calculate_size_sync (CAMEL_DATA_WRAPPER (message), NULL, NULL);

	 
	smtp_maybe_update_socket_timeout (ostream, bytes_written / 512);

	filtered_stream = camel_stream_filter_new (ostream);

	 
	filter = camel_mime_filter_progress_new (cancellable, bytes_written);
	camel_stream_filter_add (
		CAMEL_STREAM_FILTER (filtered_stream), filter);
	g_object_unref (filter);

	 
	filter = camel_mime_filter_crlf_new (
		CAMEL_MIME_FILTER_CRLF_ENCODE,
		CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
	camel_stream_filter_add (
		CAMEL_STREAM_FILTER (filtered_stream), filter);
	g_object_unref (filter);

	 
	ret = camel_data_wrapper_write_to_stream_sync (
		CAMEL_DATA_WRAPPER (message),
		filtered_stream, cancellable, error);

	if (camel_debug ("smtp")) {
		CamelStream *mem_stream, *sec_filtered_stream;
		GError *local_error = NULL;

		mem_stream = camel_stream_mem_new ();
		sec_filtered_stream = camel_stream_filter_new (mem_stream);

		filter = camel_mime_filter_crlf_new (
			CAMEL_MIME_FILTER_CRLF_ENCODE,
			CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
		camel_stream_filter_add (CAMEL_STREAM_FILTER (sec_filtered_stream), filter);
		g_object_unref (filter);

		if (camel_data_wrapper_write_to_stream_sync (CAMEL_DATA_WRAPPER (message), sec_filtered_stream, cancellable, &local_error) == -1) {
			printf ("[SMTP] Failed to write message to memory stream: %s\n", local_error ? local_error->message : "Unknown error");
		} else {
			GByteArray *bytes;

			camel_stream_flush (sec_filtered_stream, cancellable, NULL);

			bytes = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem_stream));

			if (!bytes) {
				printf ("[SMTP] Failed to get filtered stream bytes\n");
			} else {
				printf ("[SMTP] sent data (re-encoded):\n%.*s\n", (gint) bytes->len, (const gchar *) bytes->data);
			}
		}

		g_clear_object (&sec_filtered_stream);
		g_clear_object (&mem_stream);
		g_clear_error (&local_error);
	}

	 
	for (ii = 0; camel_name_value_array_get (previous_headers, ii, &header_name, &header_value); ii++) {
		if (!g_ascii_strcasecmp (header_name, "Bcc")) {
			camel_medium_add_header (CAMEL_MEDIUM (message), header_name, header_value);
		}
	}

	camel_name_value_array_free (previous_headers);

	if (ret == -1) {
		g_prefix_error (error, _("DATA command failed: "));

		g_object_unref (filtered_stream);

		camel_service_disconnect_sync (
			CAMEL_SERVICE (transport),
			FALSE, cancellable, NULL);
		return FALSE;
	}

	camel_stream_flush (filtered_stream, cancellable, NULL);
	g_object_unref (filtered_stream);

	 

	d (fprintf (stderr, "[SMTP] sending: \\r\\n.\\r\\n\n"));

	if (camel_stream_write (ostream, "\r\n.\r\n", 5, cancellable, error) == -1) {
		g_prefix_error (error, _("DATA command failed: "));
		camel_service_disconnect_sync (
			CAMEL_SERVICE (transport),
			FALSE, cancellable, NULL);
		return FALSE;
	}

	do {
		 
		g_free (respbuf);
		respbuf = camel_stream_buffer_read_line (istream, cancellable, error);
		d (fprintf (stderr, "[SMTP] received: %s\n", respbuf ? respbuf : "(null)"));
		if (respbuf == NULL) {
			g_prefix_error (error, _("DATA command failed: "));
			camel_service_disconnect_sync (
				CAMEL_SERVICE (transport),
				FALSE, cancellable, NULL);
			return FALSE;
		}
		if (strncmp (respbuf, "250", 3) != 0) {
			smtp_set_error (transport, istream, respbuf, cancellable, error);
			g_prefix_error (error, _("DATA command failed: "));
			g_free (respbuf);
			return FALSE;
		}
	} while (*(respbuf+3) == '-');  
	g_free (respbuf);

	return TRUE;
}