# HG changeset patch # User Richard Laager # Date 1180936026 0 # Node ID c588a4a9d287f1262a40ea982c64040a4b2a005d # Parent 4cda1949878cda2a11723bc26cc662c8b624dd6f A patch from David Grohmann (dave1g) to log embedded images. Closes #725 diff -r 4cda1949878c -r c588a4a9d287 COPYRIGHT --- a/COPYRIGHT Mon Jun 04 04:52:41 2007 +0000 +++ b/COPYRIGHT Mon Jun 04 05:47:06 2007 +0000 @@ -145,6 +145,7 @@ Charlie Gordon Ryan C. Gordon Miah Gregory +David Grohmann Christian Hammond Erick Hamness Fred Hampton diff -r 4cda1949878c -r c588a4a9d287 ChangeLog --- a/ChangeLog Mon Jun 04 04:52:41 2007 +0000 +++ b/ChangeLog Mon Jun 04 05:47:06 2007 +0000 @@ -9,6 +9,7 @@ (Arjan van de Ven with Intel Corporation) * No longer linkifies screennames containing @ signs in join/part notifications in chats + * With the HTML logger, images in conversations are now saved. Pidgin: * Ensure only one copy of Pidgin is running with a given configuration diff -r 4cda1949878c -r c588a4a9d287 ChangeLog.API --- a/ChangeLog.API Mon Jun 04 04:52:41 2007 +0000 +++ b/ChangeLog.API Mon Jun 04 05:47:06 2007 +0000 @@ -15,6 +15,7 @@ * purple_core_ensure_single_instance() This is for UIs to use to ensure only one copy is running. * purple_dbus_is_owner() + * purple_image_data_calculate_filename() * purple_timeout_add_seconds() Callers should prefer this to purple_timeout_add() for timers longer than 1 second away. Be aware of the rounding, though. diff -r 4cda1949878c -r c588a4a9d287 libpurple/buddyicon.c --- a/libpurple/buddyicon.c Mon Jun 04 04:52:41 2007 +0000 +++ b/libpurple/buddyicon.c Mon Jun 04 05:47:06 2007 +0000 @@ -24,7 +24,6 @@ */ #include "internal.h" #include "buddyicon.h" -#include "cipher.h" #include "conversation.h" #include "dbus-maybe.h" #include "debug.h" @@ -93,33 +92,6 @@ } } -static char * -purple_buddy_icon_data_calculate_filename(guchar *icon_data, size_t icon_len) -{ - PurpleCipherContext *context; - gchar digest[41]; - - context = purple_cipher_context_new_by_name("sha1", NULL); - if (context == NULL) - { - purple_debug_error("buddyicon", "Could not find sha1 cipher\n"); - g_return_val_if_reached(NULL); - } - - /* Hash the icon data */ - purple_cipher_context_append(context, icon_data, icon_len); - if (!purple_cipher_context_digest_to_str(context, sizeof(digest), digest, NULL)) - { - purple_debug_error("buddyicon", "Failed to get SHA-1 digest.\n"); - g_return_val_if_reached(NULL); - } - purple_cipher_context_destroy(context); - - /* Return the filename */ - return g_strdup_printf("%s.%s", digest, - purple_util_get_image_extension(icon_data, icon_len)); -} - static void purple_buddy_icon_data_cache(PurpleStoredImage *img) { @@ -238,7 +210,7 @@ if (filename == NULL) { - file = purple_buddy_icon_data_calculate_filename(icon_data, icon_len); + file = purple_util_get_image_filename(icon_data, icon_len); if (file == NULL) { g_free(icon_data); @@ -966,7 +938,7 @@ g_free(path); - new_filename = purple_buddy_icon_data_calculate_filename(icon_data, icon_len); + new_filename = purple_util_get_image_filename(icon_data, icon_len); if (new_filename == NULL) { purple_debug_error("buddyicon", diff -r 4cda1949878c -r c588a4a9d287 libpurple/buddyicon.h --- a/libpurple/buddyicon.h Mon Jun 04 04:52:41 2007 +0000 +++ b/libpurple/buddyicon.h Mon Jun 04 05:47:06 2007 +0000 @@ -31,6 +31,7 @@ #include "blist.h" #include "imgstore.h" #include "prpl.h" +#include "util.h" #ifdef __cplusplus extern "C" { diff -r 4cda1949878c -r c588a4a9d287 libpurple/log.c --- a/libpurple/log.c Mon Jun 04 04:52:41 2007 +0000 +++ b/libpurple/log.c Mon Jun 04 05:47:06 2007 +0000 @@ -32,6 +32,7 @@ #include "prefs.h" #include "util.h" #include "stringref.h" +#include "imgstore.h" static GSList *loggers = NULL; @@ -690,6 +691,109 @@ return g_strdup(purple_time_format(&tm)); } +/* NOTE: This can return msg (which you may or may not want to g_free()) + * NOTE: or a newly allocated string which you MUST g_free(). */ +static char * +convert_image_tags(const PurpleLog *log, const char *msg) +{ + const char *tmp; + const char *start; + const char *end; + GData *attributes; + GString *newmsg = NULL; + + tmp = msg; + + newmsg = g_string_new(""); + + while (purple_markup_find_tag("img", tmp, &start, &end, &attributes)) { + int imgid = 0; + char *idstr = NULL; + + if (newmsg == NULL) + newmsg = g_string_new(""); + + /* copy any text before the img tag */ + if (tmp < start) + g_string_append_len(newmsg, tmp, start - tmp); + + idstr = g_datalist_get_data(&attributes, "id"); + + imgid = atoi(idstr); + if (imgid != 0) + { + FILE *image_file; + char *dir; + PurpleStoredImage *image; + gconstpointer image_data; + char *new_filename = NULL; + char *path = NULL; + size_t image_byte_count; + + image = purple_imgstore_find_by_id(imgid); + if (image == NULL) + { + /* This should never happen. */ + g_string_free(newmsg, TRUE); + g_return_val_if_reached((char *)msg); + } + + image_data = purple_imgstore_get_data(image); + image_byte_count = purple_imgstore_get_size(image); + dir = purple_log_get_log_dir(log->type, log->name, log->account); + new_filename = purple_util_get_image_filename(image_data, image_byte_count); + + path = g_build_filename(dir, new_filename, NULL); + + /* Only save unique files. */ + if (!g_file_test(path, G_FILE_TEST_EXISTS)) + { + if ((image_file = g_fopen(path, "wb")) != NULL) + { + if (!fwrite(image_data, image_byte_count, 1, image_file)) + { + purple_debug_error("log", "Error writing %s: %s\n", + path, strerror(errno)); + fclose(image_file); + + /* Attempt to not leave half-written files around. */ + unlink(path); + } + else + { + purple_debug_info("log", "Wrote image file: %s\n", path); + fclose(image_file); + } + } + else + { + purple_debug_error("log", "Unable to create file %s: %s\n", + path, strerror(errno)); + } + } + + /* Write the new image tag */ + g_string_append_printf(newmsg, "", new_filename); + g_free(new_filename); + g_free(path); + } + + /* Continue from the end of the tag */ + tmp = end + 1; + } + + if (newmsg == NULL) + { + /* No images were found to change. */ + return (char *)msg; + } + + /* Append any remaining message data */ + g_string_append(newmsg, tmp); + + return g_string_free(newmsg, FALSE); +} + void purple_log_common_writer(PurpleLog *log, const char *ext) { PurpleLogCommonLoggerData *data = log->logger_data; @@ -1191,6 +1295,7 @@ const char *from, time_t time, const char *message) { char *msg_fixed; + char *image_corrected_msg; char *date; char *header; PurplePlugin *plugin = purple_find_prpl(purple_account_get_protocol_id(log->account)); @@ -1231,7 +1336,14 @@ if(!data->file) return 0; - purple_markup_html_to_xhtml(message, &msg_fixed, NULL); + image_corrected_msg = convert_image_tags(log, message); + purple_markup_html_to_xhtml(image_corrected_msg, &msg_fixed, NULL); + + /* Yes, this breaks encapsulation. But it's a static function and + * this saves a needless strdup(). */ + if (image_corrected_msg != message) + g_free(image_corrected_msg); + date = log_get_timestamp(log, time); if(log->type == PURPLE_LOG_SYSTEM){ diff -r 4cda1949878c -r c588a4a9d287 libpurple/util.c --- a/libpurple/util.c Mon Jun 04 04:52:41 2007 +0000 +++ b/libpurple/util.c Mon Jun 04 05:47:06 2007 +0000 @@ -22,6 +22,7 @@ */ #include "internal.h" +#include "cipher.h" #include "conversation.h" #include "core.h" #include "debug.h" @@ -1403,6 +1404,40 @@ plain = g_string_append_c(plain, '\n'); continue; } + if(!g_ascii_strncasecmp(c, "' || *(c+4) == ' ')) { + const char *p = c; + GString *src = NULL; + struct purple_parse_tag *pt; + while(*p && *p != '>') { + if(!g_ascii_strncasecmp(p, "src=", strlen("src="))) { + const char *q = p + strlen("src="); + src = g_string_new(""); + if(*q == '\'' || *q == '\"') + q++; + while(*q && *q != '\"' && *q != '\'' && *q != ' ') { + src = g_string_append_c(src, *q); + q++; + } + p = q; + } + p++; + } + if ((c = strchr(c, '>')) != NULL) + c++; + else + c = p; + pt = g_new0(struct purple_parse_tag, 1); + pt->src_tag = "img"; + pt->dest_tag = "img"; + tags = g_list_prepend(tags, pt); + if(src && src->len) + g_string_append_printf(xhtml, "", g_strstrip(src->str)); + else + pt->ignore = TRUE; + if (src) + g_string_free(src, TRUE); + continue; + } if(!g_ascii_strncasecmp(c, "", 3) || !g_ascii_strncasecmp(c, "", strlen(""))) { struct purple_parse_tag *pt = g_new0(struct purple_parse_tag, 1); pt->src_tag = *(c+2) == '>' ? "b" : "bold"; @@ -2659,6 +2694,33 @@ return "icon"; } +char * +purple_util_get_image_filename(gconstpointer image_data, size_t image_len) +{ + PurpleCipherContext *context; + gchar digest[41]; + + context = purple_cipher_context_new_by_name("sha1", NULL); + if (context == NULL) + { + purple_debug_error("util", "Could not find sha1 cipher\n"); + g_return_val_if_reached(NULL); + } + + /* Hash the image data */ + purple_cipher_context_append(context, image_data, image_len); + if (!purple_cipher_context_digest_to_str(context, sizeof(digest), digest, NULL)) + { + purple_debug_error("util", "Failed to get SHA-1 digest.\n"); + g_return_val_if_reached(NULL); + } + purple_cipher_context_destroy(context); + + /* Return the filename */ + return g_strdup_printf("%s.%s", digest, + purple_util_get_image_extension(image_data, image_len)); +} + gboolean purple_program_is_valid(const char *program) { diff -r 4cda1949878c -r c588a4a9d287 libpurple/util.h --- a/libpurple/util.h Mon Jun 04 04:52:41 2007 +0000 +++ b/libpurple/util.h Mon Jun 04 05:47:06 2007 +0000 @@ -32,6 +32,7 @@ #include "account.h" #include "xmlnode.h" +#include "notify.h" #ifdef __cplusplus extern "C" { @@ -623,6 +624,12 @@ const char * purple_util_get_image_extension(gconstpointer data, size_t len); +/** + * Returns a SHA-1 hash string of the data passed in with the correct file + * extention appended. + */ +char *purple_util_get_image_filename(gconstpointer image_data, size_t image_len); + /*@}*/