# HG changeset patch # User Stu Tomlinson # Date 1132866432 0 # Node ID 029802981b81df9013c45a8ffd52846cb4100090 # Parent 4d3119205a333ea6ace642b1faa7946c48e18a51 [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 diff -r 4d3119205a33 -r 029802981b81 configure.ac --- 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 +#include + ], [], [ + 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) diff -r 4d3119205a33 -r 029802981b81 src/protocols/silc/TODO --- 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 diff -r 4d3119205a33 -r 029802981b81 src/protocols/silc/ops.c --- 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), "", imgid); + + if (channel) + serv_got_chat_in(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(convo)), + sender->nickname ? + sender->nickname : + "", cflags, + tmp, time(NULL)); + else + serv_got_im(gc, sender->nickname ? + sender->nickname : "", + 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; } diff -r 4d3119205a33 -r 029802981b81 src/protocols/silc/silc.c --- 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); diff -r 4d3119205a33 -r 029802981b81 src/protocols/silc/silcgaim.h --- 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; diff -r 4d3119205a33 -r 029802981b81 src/protocols/silc/util.c --- 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 */