changeset 12217:029802981b81

[gaim-migrate @ 14519] SILC IM Image and cipher selection support from Pekka Riikonen. Modified by me so the SILC prpl continues to work with SILC Toolkit < 1.0.1, but without images. Any breakage is my fault. committer: Tailor Script <tailor@pidgin.im>
author Stu Tomlinson <stu@nosnilmot.com>
date Thu, 24 Nov 2005 21:07:12 +0000
parents 4d3119205a33
children 9cbc5967fbfd
files configure.ac src/protocols/silc/TODO src/protocols/silc/ops.c src/protocols/silc/silc.c src/protocols/silc/silcgaim.h src/protocols/silc/util.c
diffstat 6 files changed, 471 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Thu Nov 24 20:47:46 2005 +0000
+++ b/configure.ac	Thu Nov 24 21:07:12 2005 +0000
@@ -229,7 +229,22 @@
 fi
 AC_SUBST(SILC_LIBS)
 AC_SUBST(SILC_CFLAGS)
-
+dnl SILC Toolkit >= 1.0.1 has a new MIME API
+if test "x$silcclient" = "xyes"; then
+	CPPFLAGS_save="$CPPFLAGS"
+	CPPFLAGS="$CPPFLAGS $SILC_CFLAGS"
+		AC_MSG_CHECKING(for silcmime.h)
+		AC_TRY_COMPILE([
+#include <silcincludes.h>
+#include <silcmime.h>
+		], [], [
+		AC_MSG_RESULT(yes)
+		AC_DEFINE(HAVE_SILCMIME_H, 1, [define if we have silcmime.h])
+		], [
+		AC_MSG_RESULT(no)
+		])
+	CPPFLAGS="$CPPFLAGS_save"
+fi
 
 
 AC_ARG_ENABLE(distrib,,,enable_distrib=no)
--- a/src/protocols/silc/TODO	Thu Nov 24 20:47:46 2005 +0000
+++ b/src/protocols/silc/TODO	Thu Nov 24 21:07:12 2005 +0000
@@ -2,8 +2,6 @@
 =====================
 
 Sending images
-	- PROTO_IM_IMAGES flag support.  We need easy way to map the
-	  image filenames to their MIME types to do this.
 	- Sending images to channel too, if Gaim allows it.
 
 Preferences
--- a/src/protocols/silc/ops.c	Thu Nov 24 20:47:46 2005 +0000
+++ b/src/protocols/silc/ops.c	Thu Nov 24 21:07:12 2005 +0000
@@ -20,8 +20,21 @@
 #include "silcincludes.h"
 #include "silcclient.h"
 #include "silcgaim.h"
+#include "imgstore.h"
 #include "wb.h"
 
+static void
+silc_channel_message(SilcClient client, SilcClientConnection conn,
+		     SilcClientEntry sender, SilcChannelEntry channel,
+		     SilcMessagePayload payload, SilcChannelPrivateKey key,
+		     SilcMessageFlags flags, const unsigned char *message,
+		     SilcUInt32 message_len);
+static void
+silc_private_message(SilcClient client, SilcClientConnection conn,
+		     SilcClientEntry sender, SilcMessagePayload payload,
+		     SilcMessageFlags flags, const unsigned char *message,
+		     SilcUInt32 message_len);
+
 /* Message sent to the application by library. `conn' associates the
    message to a specific connection.  `conn', however, may be NULL.
    The `type' indicates the type of the message sent by the library.
@@ -35,6 +48,158 @@
 	/* Nothing */
 }
 
+#ifdef HAVE_SILCMIME_H
+/* Processes incoming MIME message.  Can be private message or channel
+   message. */
+
+static void
+silcgaim_mime_message(SilcClient client, SilcClientConnection conn,
+		      SilcClientEntry sender, SilcChannelEntry channel,
+		      SilcMessagePayload payload, SilcChannelPrivateKey key,
+		      SilcMessageFlags flags, SilcMime mime,
+		      gboolean recursive)
+{
+	GaimConnection *gc = client->application;
+	SilcGaim sg = gc->proto_data;
+	const char *type;
+	const unsigned char *data;
+	SilcUInt32 data_len;
+	GaimMessageFlags cflags = 0;
+	GaimConversation *convo = NULL;
+
+	if (!mime)
+		return;
+
+	/* Check for fragmented MIME message */
+	if (silc_mime_is_partial(mime)) {
+		if (!sg->mimeass)
+			sg->mimeass = silc_mime_assembler_alloc();
+
+		/* Defragment */
+		mime = silc_mime_assemble(sg->mimeass, mime);
+		if (!mime)
+			/* More fragments to come */
+			return;
+
+		/* Process the complete message */
+		silcgaim_mime_message(client, conn, sender, channel,
+				      payload, key, flags, mime, FALSE);
+		return;
+	}
+
+	/* Check for multipart message */
+	if (silc_mime_is_multipart(mime)) {
+		SilcMime p;
+		const char *mtype;
+		SilcDList parts = silc_mime_get_multiparts(mime, &mtype);
+
+		/* Only "mixed" type supported */
+		if (strcmp(mtype, "mixed"))
+			goto out;
+
+		silc_dlist_start(parts);
+		while ((p = silc_dlist_get(parts)) != SILC_LIST_END) {
+			/* Recursively process parts */
+			silcgaim_mime_message(client, conn, sender, channel,
+					      payload, key, flags, p, TRUE);
+		}
+		goto out;
+	}
+
+	/* Get content type and MIME data */
+	type = silc_mime_get_field(mime, "Content-Type");
+	if (!type)
+		goto out;
+	data = silc_mime_get_data(mime, &data_len);
+	if (!data)
+		goto out;
+
+	/* Process according to content type */
+
+	/* Plain text */
+	if (strstr(type, "text/plain")) {
+		/* Default is UTF-8, don't check for other charsets */
+		if (!strstr(type, "utf-8"))
+			goto out;
+
+		if (channel)
+			silc_channel_message(client, conn, sender, channel,
+					     payload, key, 
+					     SILC_MESSAGE_FLAG_UTF8, data,
+					     data_len);
+		else
+			silc_private_message(client, conn, sender, payload,
+					     SILC_MESSAGE_FLAG_UTF8, data,
+					     data_len);
+		goto out;
+	}
+
+	/* Image */
+	if (strstr(type, "image/png") ||
+	    strstr(type, "image/jpeg") ||
+	    strstr(type, "image/gif") ||
+	    strstr(type, "image/tiff")) {
+		char tmp[32];
+		int imgid;
+
+		/* Get channel convo (if message is for channel) */
+		if (key && channel) {
+			GList *l;
+			SilcGaimPrvgrp prv;
+
+			for (l = sg->grps; l; l = l->next)
+				if (((SilcGaimPrvgrp)l->data)->key == key) {
+					prv = l->data;
+					convo = gaim_find_conversation_with_account(GAIM_CONV_TYPE_CHAT,
+							prv->channel, sg->account);
+					break;
+				}
+		}
+		if (channel && !convo)
+			convo = gaim_find_conversation_with_account(GAIM_CONV_TYPE_CHAT,
+								    channel->channel_name, sg->account);
+		if (channel && !convo)
+			goto out;
+
+		imgid = gaim_imgstore_add(data, data_len, "");
+		if (imgid) {
+			cflags |= GAIM_MESSAGE_IMAGES | GAIM_MESSAGE_RECV;
+			g_snprintf(tmp, sizeof(tmp), "<IMG ID=\"%d\">", imgid);
+		  
+			if (channel)
+				serv_got_chat_in(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(convo)),
+				 		 sender->nickname ?
+				 		  sender->nickname : 
+						 "<unknown>", cflags,
+						 tmp, time(NULL));
+			else
+				serv_got_im(gc, sender->nickname ?
+					    sender->nickname : "<unknown>",
+					    tmp, cflags, time(NULL));
+
+			gaim_imgstore_unref(imgid);
+			cflags = 0;
+		}
+		goto out;
+	}
+
+	/* Whiteboard message */
+	if (strstr(type, "application/x-wb") &&
+	    !gaim_account_get_bool(sg->account, "block-wb", FALSE)) {
+		if (channel)
+			silcgaim_wb_receive_ch(client, conn, sender, channel,
+					       payload, flags, data, data_len);
+		else
+			silcgaim_wb_receive(client, conn, sender, payload,
+					    flags, data, data_len);
+		goto out;
+	}
+
+ out:
+	if (!recursive)
+		silc_mime_free(mime);
+}
+#endif /* HAVE_SILCMIME_H */
 
 /* Message for a channel. The `sender' is the sender of the message
    The `channel' is the channel. The `message' is the message.  Note
@@ -81,6 +246,13 @@
 	}
 
 	if (flags & SILC_MESSAGE_FLAG_DATA) {
+		/* Process MIME message */
+#ifdef HAVE_SILCMIME_H
+		SilcMime mime;
+		mime = silc_mime_decode(message, message_len);
+		silcgaim_mime_message(client, conn, sender, channel, payload,
+				      key, flags, mime, FALSE);
+#else
 		char type[128], enc[128];
 		unsigned char *data;
 		SilcUInt32 data_len;
@@ -89,7 +261,7 @@
 		memset(enc, 0, sizeof(enc));
 
 		if (!silc_mime_parse(message, message_len, NULL, 0,
-		    type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data, 
+		    type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data,
 		    &data_len))
 			return;
 
@@ -98,7 +270,7 @@
 		    !gaim_account_get_bool(sg->account, "block-wb", FALSE))
 			silcgaim_wb_receive_ch(client, conn, sender, channel,
 					       payload, flags, data, data_len);
-
+#endif
 		return;
 	}
 
@@ -177,6 +349,13 @@
 	}
 
 	if (flags & SILC_MESSAGE_FLAG_DATA) {
+#ifdef HAVE_SILCMIME_H
+		/* Process MIME message */
+		SilcMime mime;
+		mime = silc_mime_decode(message, message_len);
+		silcgaim_mime_message(client, conn, sender, NULL, payload,
+				      NULL, flags, mime, FALSE);
+#else
 		char type[128], enc[128];
 		unsigned char *data;
 		SilcUInt32 data_len;
@@ -194,7 +373,7 @@
 		    !gaim_account_get_bool(sg->account, "block-wb", FALSE))
 			silcgaim_wb_receive(client, conn, sender, payload,
 					    flags, data, data_len);
-
+#endif
 		return;
 	}
 
--- a/src/protocols/silc/silc.c	Thu Nov 24 20:47:46 2005 +0000
+++ b/src/protocols/silc/silc.c	Thu Nov 24 21:07:12 2005 +0000
@@ -253,6 +253,8 @@
 	SilcClientParams params;
 	GaimConnection *gc;
 	char pkd[256], prd[256];
+	const char *cipher, *hmac;
+	int i;
 
 	gc = account->gc;
 	if (!gc)
@@ -292,6 +294,20 @@
 
 	gaim_connection_set_display_name(gc, client->username);
 
+	/* Register requested cipher and HMAC */
+	cipher = gaim_account_get_string(account, "cipher", SILC_DEFAULT_CIPHER);
+	for (i = 0; silc_default_ciphers[i].name; i++)
+		if (!strcmp(silc_default_ciphers[i].name, cipher)) {
+			silc_cipher_register(&(silc_default_ciphers[i]));
+			break;
+		}
+	hmac = gaim_account_get_string(account, "hmac", SILC_DEFAULT_HMAC);
+	for (i = 0; silc_default_hmacs[i].name; i++)
+		if (!strcmp(silc_default_hmacs[i].name, hmac)) {
+			silc_hmac_register(&(silc_default_hmacs[i]));
+			break;
+		}
+
 	/* Init SILC client */
 	if (!silc_client_init(client)) {
 		gc->wants_to_die = TRUE;
@@ -354,6 +370,10 @@
 	SilcGaim sg = (SilcGaim)context;
 	silc_client_stop(sg->client);
 	silc_client_free(sg->client);
+#ifdef HAVE_SILCMIME_H
+	if (sg->mimeass)
+		silc_mime_assembler_free(sg->mimeass);
+#endif
 	silc_free(sg);
 	return 0;
 }
@@ -1016,6 +1036,9 @@
 	GaimConversation *convo;
 	char tmp[256], *nickname = NULL;
 	SilcClientEntry client_entry;
+#ifdef HAVE_SILCMIME_H
+	SilcDList list;
+#endif
 
 	convo = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, im->nick,
 							sg->account);
@@ -1041,12 +1064,31 @@
 		client_entry = clients[0];
 	}
 
-	/* Send the message */
-	silc_client_send_private_message(client, conn, client_entry, im->flags,
-					 (unsigned char *)im->message, im->message_len, TRUE);
+#ifdef HAVE_SILCMIME_H
+	/* Check for images */
+	list = silcgaim_image_message(im->message, (SilcUInt32 *)&im->flags);
+	if (list) {
+		/* Send one or more MIME message.  If more than one, they
+		   are MIME fragments due to over large message */
+		SilcBuffer buf;
+
+		silc_dlist_start(list);
+		while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
+			silc_client_send_private_message(client, conn, 
+							 client_entry, im->flags,
+							 buf->data, buf->len,
+							 TRUE);
+		silc_mime_partial_free(list);
+	} else
+#endif
+	{
+		/* Send the message */
+		silc_client_send_private_message(client, conn, client_entry, im->flags,
+						 (unsigned char *)im->message, im->message_len, TRUE);
+	}
+
 	gaim_conv_im_write(GAIM_CONV_IM(convo), conn->local_entry->nickname,
 			   im->message, 0, time(NULL));
-
 	goto out;
 
  err:
@@ -1071,8 +1113,11 @@
 	SilcClientEntry *clients;
 	SilcUInt32 clients_count, mflags;
 	char *nickname, *msg, *tmp;
-	int ret;
+	int ret = 0;
 	gboolean sign = gaim_account_get_bool(sg->account, "sign-verify", FALSE);
+#ifdef HAVE_SILCMIME_H
+	SilcDList list;
+#endif
 
 	if (!who || !message)
 		return 0;
@@ -1116,7 +1161,7 @@
 			return 0;
 		}
 		im->nick = g_strdup(who);
-		im->message = g_strdup(msg);
+		im->message = g_strdup(message);
 		im->message_len = strlen(im->message);
 		im->flags = mflags;
 		silc_client_get_clients(client, conn, nickname, NULL,
@@ -1126,10 +1171,31 @@
 		return 0;
 	}
 
-	/* Send private message directly */
-	ret = silc_client_send_private_message(client, conn, clients[0],
-					       mflags, (unsigned char *)msg,
-					       strlen(msg), TRUE);
+#ifdef HAVE_SILCMIME_H
+	/* Check for images */
+	list = silcgaim_image_message(message, &mflags);
+	if (list) {
+		/* Send one or more MIME message.  If more than one, they
+		   are MIME fragments due to over large message */
+		SilcBuffer buf;
+
+		silc_dlist_start(list);
+		while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
+			ret =
+			 silc_client_send_private_message(client, conn, 
+							  clients[0], mflags,
+							  buf->data, buf->len,
+							  TRUE);
+		silc_mime_partial_free(list);
+	} else
+#endif
+	{
+		/* Send private message directly */
+		ret = silc_client_send_private_message(client, conn, clients[0],
+						       mflags,
+						       (unsigned char *)msg,
+						       strlen(msg), TRUE);
+	}
 
 	g_free(tmp);
 	silc_free(nickname);
@@ -1639,8 +1705,13 @@
 
 static GaimPluginProtocolInfo prpl_info =
 {
+#ifdef HAVE_SILCMIME_H
+	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
+	OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE,
+#else
 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
 	OPT_PROTO_PASSWORD_OPTIONAL,
+#endif
 	NULL,						/* user_splits */
 	NULL,						/* protocol_options */
 	NO_BUDDY_ICONS,				/* icon_spec */
@@ -1738,6 +1809,9 @@
 	GaimAccountOption *option;
 	GaimAccountUserSplit *split;
 	char tmp[256];
+	int i;
+	GaimKeyValuePair *kvp;
+	GList *list = NULL;
 
 	silc_plugin = plugin;
 
@@ -1759,10 +1833,29 @@
 	option = gaim_account_option_string_new(_("Private Key file"),
 						"private-key", tmp);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	for (i = 0; silc_default_ciphers[i].name; i++) {
+		kvp = silc_calloc(1, sizeof(*kvp));
+		kvp->key = strdup(silc_default_ciphers[i].name);
+		kvp->value = strdup(silc_default_ciphers[i].name);
+		list = g_list_append(list, kvp);
+	}
+	option = gaim_account_option_list_new(_("Cipher"), "cipher", list);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	list = NULL;
+	for (i = 0; silc_default_hmacs[i].name; i++) {
+		kvp = silc_calloc(1, sizeof(*kvp));
+		kvp->key = strdup(silc_default_hmacs[i].name);
+		kvp->value = strdup(silc_default_hmacs[i].name);
+		list = g_list_append(list, kvp);
+	}
+	option = gaim_account_option_list_new(_("HMAC"), "hmac", list);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
 	option = gaim_account_option_bool_new(_("Public key authentication"),
 					      "pubkey-auth", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
 	option = gaim_account_option_bool_new(_("Reject watching by other users"),
 					      "reject-watch", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
--- a/src/protocols/silc/silcgaim.h	Thu Nov 24 20:47:46 2005 +0000
+++ b/src/protocols/silc/silcgaim.h	Thu Nov 24 21:07:12 2005 +0000
@@ -75,7 +75,9 @@
 
 	char *motd;
 	GaimRoomlist *roomlist;
-
+#ifdef HAVE_SILCMIME_H
+	SilcMimeAssembler mimeass;
+#endif
 	unsigned int detaching            : 1;
 	unsigned int resuming             : 1;
 	unsigned int roomlist_canceled    : 1;
@@ -133,8 +135,7 @@
 void silcgaim_chat_invite(GaimConnection *gc, int id, const char *msg,
 			  const char *name);
 void silcgaim_chat_leave(GaimConnection *gc, int id);
-int silcgaim_chat_send(GaimConnection *gc, int id, const char *msg,
-					GaimMessageFlags flags);
+int silcgaim_chat_send(GaimConnection *gc, int id, const char *msg, GaimMessageFlags flags);
 void silcgaim_chat_set_topic(GaimConnection *gc, int id, const char *topic);
 GaimRoomlist *silcgaim_roomlist_get_list(GaimConnection *gc);
 void silcgaim_roomlist_cancel(GaimRoomlist *list);
@@ -143,6 +144,10 @@
 void silcgaim_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
 					 char **contactstr, char **langstr, char **devicestr,
 					 char **tzstr, char **geostr);
+#ifdef HAVE_SILCMIME_H
+char *silcgaim_file2mime(const char *filename);
+SilcDList silcgaim_image_message(const char *msg, SilcUInt32 *mflags);
+#endif
 
 #ifdef _WIN32
 typedef int uid_t;
--- a/src/protocols/silc/util.c	Thu Nov 24 20:47:46 2005 +0000
+++ b/src/protocols/silc/util.c	Thu Nov 24 21:07:12 2005 +0000
@@ -20,6 +20,7 @@
 #include "silcincludes.h"
 #include "silcclient.h"
 #include "silcgaim.h"
+#include "imgstore.h"
 
 /**************************** Utility Routines *******************************/
 
@@ -569,3 +570,163 @@
 				geo.altitude ? geo.altitude : "",
 				geo.accuracy ? geo.accuracy : "");
 }
+
+#ifdef HAVE_SILCMIME_H
+/* Returns MIME type of filetype */
+
+char *silcgaim_file2mime(const char *filename)
+{
+	const char *ct;
+
+	ct = strrchr(filename, '.');
+	if (!ct)
+		return NULL;
+	else if (!strcasecmp(".png", ct))
+		return strdup("image/png");
+	else if (!strcasecmp(".jpg", ct))
+		return strdup("image/jpeg");
+	else if (!strcasecmp(".jpeg", ct))
+		return strdup("image/jpeg");
+	else if (!strcasecmp(".gif", ct))
+		return strdup("image/gif");
+	else if (!strcasecmp(".tiff", ct))
+		return strdup("image/tiff");
+	
+	return NULL;
+}
+
+/* Checks if message has images, and assembles MIME message if it has. 
+   If only one image is present, creates simple MIME image message.  If 
+   there are multiple images and/or text with images multipart MIME 
+   message is created. */
+
+SilcDList silcgaim_image_message(const char *msg, SilcUInt32 *mflags)
+{
+	SilcMime mime = NULL, p;
+	SilcDList list, parts = NULL;
+	const char *start, *end, *last;
+	GData *attribs;
+	char *type;
+	gboolean images = FALSE;
+
+	last = msg;
+	while (last && *last && gaim_markup_find_tag("img", last, &start,
+						     &end, &attribs)) {
+		GaimStoredImage *image = NULL;
+		const char *id;
+
+		/* Check if there is text before image */
+		if (start - last) {
+			char *text, *tmp;
+			p = silc_mime_alloc();
+
+			/* Add content type */
+			silc_mime_add_field(p, "Content-Type",
+					    "text/plain; charset=utf-8");
+
+			tmp = g_strndup(last, start - last);
+			text = gaim_unescape_html(tmp);
+			g_free(tmp);
+			/* Add text */
+			silc_mime_add_data(p, text, strlen(text));
+			g_free(text);
+
+			if (!parts)
+				parts = silc_dlist_init();
+			silc_dlist_add(parts, p);
+		}
+
+		id = g_datalist_get_data(&attribs, "id");
+		if (id && (image = gaim_imgstore_get(atoi(id)))) {
+			unsigned long imglen = gaim_imgstore_get_size(image);
+			gpointer img = gaim_imgstore_get_data(image);
+
+			p = silc_mime_alloc();
+
+			/* Add content type */
+			type = silcgaim_file2mime(gaim_imgstore_get_filename(image));
+			if (!type) {
+				g_datalist_clear(&attribs);
+				last = end + 1;
+				continue;
+			}
+			silc_mime_add_field(p, "Content-Type", type);
+			silc_free(type);
+
+			/* Add content transfer encoding */
+			silc_mime_add_field(p, "Content-Transfer-Encoding", "binary");
+
+			/* Add image data */
+			silc_mime_add_data(p, img, imglen);
+
+			if (!parts)
+				parts = silc_dlist_init();
+			silc_dlist_add(parts, p);
+			images = TRUE;
+		}
+
+		g_datalist_clear(&attribs);
+
+		/* Continue after tag */
+		last = end + 1;
+	}
+
+	/* Check for text after the image(s) */
+	if (images && last && *last) {
+		char *tmp = gaim_unescape_html(last);
+		p = silc_mime_alloc();
+
+		/* Add content type */
+		silc_mime_add_field(p, "Content-Type",
+				    "text/plain; charset=utf-8");
+
+		/* Add text */
+		silc_mime_add_data(p, tmp, strlen(tmp));
+		g_free(tmp);
+
+		if (!parts)
+			parts = silc_dlist_init();
+		silc_dlist_add(parts, p);
+	}
+
+	/* If there weren't any images, don't return anything. */
+	if (!images) {
+		if (parts)
+			silc_dlist_uninit(parts);
+		return NULL;
+	}
+
+	if (silc_dlist_count(parts) > 1) {
+		/* Multipart MIME message */
+		char b[32];
+		mime = silc_mime_alloc();
+		silc_mime_add_field(mime, "MIME-Version", "1.0");
+		g_snprintf(b, sizeof(b), "b%4X%4X",
+			   (unsigned int)time(NULL),
+			   silc_dlist_count(parts)); 
+		silc_mime_set_multipart(mime, "mixed", b);
+		silc_dlist_start(parts);
+		while ((p = silc_dlist_get(parts)) != SILC_LIST_END)
+			silc_mime_add_multipart(mime, p);
+	} else {
+		/* Simple MIME message */
+		silc_dlist_start(parts);
+		mime = silc_dlist_get(parts);
+		silc_mime_add_field(mime, "MIME-Version", "1.0");
+	}
+
+	*mflags &= ~SILC_MESSAGE_FLAG_UTF8;
+	*mflags |= SILC_MESSAGE_FLAG_DATA;
+
+	/* Encode message. Fragment if it is too large */
+	list = silc_mime_encode_partial(mime, 0xfc00);
+
+	silc_dlist_uninit(parts);
+
+	/* Added multiparts gets freed here */
+	silc_mime_free(mime);
+
+	return list;
+}
+
+#endif /* HAVE_SILCMIME_H */