Mercurial > pidgin.yaz
changeset 20897:8de7c44fd577
Patch from QuLogic to add support for aMSN logs from the log-reader plugin.
This is really awesome!
Closes #3497.
author | Sadrul Habib Chowdhury <imadil@gmail.com> |
---|---|
date | Fri, 12 Oct 2007 11:51:13 +0000 |
parents | ce9579a6bcdd |
children | 70082d0db571 |
files | libpurple/plugins/log_reader.c |
diffstat | 1 files changed, 385 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/libpurple/plugins/log_reader.c Fri Oct 12 11:22:04 2007 +0000 +++ b/libpurple/plugins/log_reader.c Fri Oct 12 11:51:13 2007 +0000 @@ -2098,6 +2098,361 @@ g_free(data); } +/************************************************************************* + * aMSN Logger * + *************************************************************************/ + +/* The aMSN logger doesn't write logs, only reads them. This is to include + * aMSN logs in the log viewer transparently. + */ + +static PurpleLogLogger *amsn_logger; + +struct amsn_logger_data { + char *path; + int offset; + int length; +}; + +#define AMSN_LOG_CONV_START "|\"LRED[Conversation started on " +#define AMSN_LOG_CONV_END "|\"LRED[You have closed the window on " +#define AMSN_LOG_CONV_EXTRA "01 Aug 2001 00:00:00]" + +/* `log_dir`/username@hotmail.com/logs/buddyname@hotmail.com.log */ +/* `log_dir`/username@hotmail.com/logs/Month Year/buddyname@hotmail.com.log */ +static GList *amsn_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account) +{ + GList *list = NULL; + struct amsn_logger_data *data; + const char *logdir; + char *username; + char *log_path; + char *buddy_log; + char *filename; + GDir *dir; + const char *name; + GError *error; + char *contents; + PurpleLog *log; + GList *files = NULL; + GList *f; + + logdir = purple_prefs_get_string("/plugins/core/log_reader/amsn/log_directory"); + + /* By clearing the log directory path, this logger can be (effectively) disabled. */ + if (!logdir || !*logdir) + return NULL; + + /* aMSN only works with MSN/WLM */ + if (strcmp(account->protocol_id, "prpl-msn")) + return NULL; + + username = g_strdup(purple_normalize(account, account->username)); + buddy_log = g_strdup_printf("%s.log", purple_normalize(account, sn)); + log_path = g_build_filename(logdir, username, "logs", NULL); + + /* First check in the top-level */ + filename = g_build_filename(log_path, buddy_log, NULL); + if (g_file_test(filename, G_FILE_TEST_EXISTS)) + files = g_list_prepend(files, filename); + else + g_free(filename); + + /* Check in previous months */ + dir = g_dir_open(log_path, 0, NULL); + if (dir) { + while ((name = g_dir_read_name(dir)) != NULL) { + filename = g_build_filename(log_path, name, buddy_log, NULL); + if (g_file_test(filename, G_FILE_TEST_EXISTS)) + files = g_list_prepend(files, filename); + else + g_free(filename); + } + g_dir_close(dir); + } + + g_free(log_path); + + /* New versions use 'friendlier' directory names */ + purple_util_chrreplace(username, '@', '_'); + purple_util_chrreplace(username, '.', '_'); + + log_path = g_build_filename(logdir, username, "logs", NULL); + + /* First check in the top-level */ + filename = g_build_filename(log_path, buddy_log, NULL); + if (g_file_test(filename, G_FILE_TEST_EXISTS)) + files = g_list_prepend(files, filename); + else + g_free(filename); + + /* Check in previous months */ + dir = g_dir_open(log_path, 0, NULL); + if (dir) { + while ((name = g_dir_read_name(dir)) != NULL) { + filename = g_build_filename(log_path, name, buddy_log, NULL); + if (g_file_test(filename, G_FILE_TEST_EXISTS)) + files = g_list_prepend(files, filename); + else + g_free(filename); + } + g_dir_close(dir); + } + + g_free(log_path); + g_free(username); + g_free(buddy_log); + + /* Loop through files looking for logs */ + for(f = g_list_first(files); f; f = g_list_next(f)) { + filename = f->data; + purple_debug_info("aMSN logger", "Reading %s\n", filename); + error = NULL; + if (!g_file_get_contents(filename, &contents, NULL, &error)) { + purple_debug_error("aMSN logger", + "Couldn't read file %s: %s \n", filename, + (error && error->message) ? + error->message : "Unknown error"); + if (error) + g_error_free(error); + } else { + char *c = contents; + gboolean found_start = FALSE; + char *start_log = c; + int offset = 0; + struct tm tm; + while (c && *c) { + if (purple_str_has_prefix(c, AMSN_LOG_CONV_START)) { + char month[4]; + if (sscanf(c + strlen(AMSN_LOG_CONV_START), + "%u %3s %u %u:%u:%u", + &tm.tm_mday, (char*)&month, &tm.tm_year, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { + found_start = FALSE; + purple_debug_error("aMSN logger", + "Error parsing start date for %s\n", + filename); + } else { + const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL}; + tm.tm_year -= 1900; + + /* Let the C library deal with + * daylight savings time. + */ + tm.tm_isdst = -1; + + /* Ugly hack, in case current locale + * is not English. This code is taken + * from log.c. + */ + for (tm.tm_mon = 0; months[tm.tm_mon]; tm.tm_mon++) { + if (strcmp(month, months[tm.tm_mon]) == 0) + break; + } + found_start = TRUE; + offset = c - contents; + start_log = c; + } + } else if (purple_str_has_prefix(c, AMSN_LOG_CONV_END) && found_start) { + data = g_new0(struct amsn_logger_data, 1); + data->path = g_strdup(filename); + data->offset = offset; + data->length = c - start_log + + strlen(AMSN_LOG_CONV_END) + + strlen(AMSN_LOG_CONV_EXTRA); + log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, mktime(&tm), NULL); + log->logger = amsn_logger; + log->logger_data = data; + list = g_list_prepend(list, log); + found_start = FALSE; + + purple_debug_info("aMSN logger", + "Found log for %s:" + " path = (%s)," + " offset = (%d)," + " length = (%d)\n", + sn, data->path, data->offset, data->length); + } + c = strstr(c, "\n"); + c++; + } + + /* I've seen the file end without the AMSN_LOG_CONV_END bit */ + if (found_start) { + data = g_new0(struct amsn_logger_data, 1); + data->path = g_strdup(filename); + data->offset = offset; + data->length = c - start_log + + strlen(AMSN_LOG_CONV_END) + + strlen(AMSN_LOG_CONV_EXTRA); + log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, mktime(&tm), NULL); + log->logger = amsn_logger; + log->logger_data = data; + list = g_list_prepend(list, log); + found_start = FALSE; + + purple_debug_info("aMSN logger", + "Found log for %s:" + " path = (%s)," + " offset = (%d)," + " length = (%d)\n", + sn, data->path, data->offset, data->length); + } + g_free(contents); + } + g_free(filename); + } + + g_list_free(files); + + return list; +} + +/* Really it's |"L, but the string's been escaped */ +#define AMSN_LOG_FORMAT_TAG "|"L" + +static char *amsn_logger_read(PurpleLog *log, PurpleLogReadFlags *flags) +{ + struct amsn_logger_data *data; + FILE *file; + char *contents; + char *escaped; + GString *formatted; + char *start; + gboolean in_span = FALSE; + + if (flags != NULL) + *flags = PURPLE_LOG_READ_NO_NEWLINE; + + g_return_val_if_fail(log != NULL, g_strdup("")); + + data = log->logger_data; + + g_return_val_if_fail(data->path != NULL, g_strdup("")); + g_return_val_if_fail(data->length > 0, g_strdup("")); + + contents = g_malloc(data->length + 2); + + file = g_fopen(data->path, "rb"); + g_return_val_if_fail(file != NULL, g_strdup("")); + + fseek(file, data->offset, SEEK_SET); + fread(contents, data->length, 1, file); + fclose(file); + + contents[data->length] = '\n'; + contents[data->length + 1] = '\0'; + + escaped = g_markup_escape_text(contents, -1); + g_free(contents); + contents = escaped; + + formatted = g_string_sized_new(data->length + 2); + + start = contents; + while (start && *start) { + char *end; + char *old_tag; + char *tag; + end = strstr(start, "\n"); + if (!end) + break; + *end = '\0'; + if (purple_str_has_prefix(start, AMSN_LOG_FORMAT_TAG) && in_span) { + /* New format for this line */ + g_string_append(formatted, "</span><br>"); + in_span = FALSE; + } else if (start != contents) { + /* Continue format from previous line */ + g_string_append(formatted, "<br>"); + } + old_tag = start; + tag = strstr(start, AMSN_LOG_FORMAT_TAG); + while (tag) { + g_string_append_len(formatted, old_tag, tag - old_tag); + tag += strlen(AMSN_LOG_FORMAT_TAG); + if (in_span) { + g_string_append(formatted, "</span>"); + in_span = FALSE; + } + if (*tag == 'C') { + /* |"LCxxxxxx is a hex colour */ + char colour[7]; + strncpy(colour, tag + 1, 6); + colour[6] = '\0'; + g_string_append_printf(formatted, "<span style=\"color: #%s;\">", colour); + /* This doesn't appear to work? */ + /* g_string_append_printf(formatted, "<span style=\"color: #%6s;\">", tag + 1); */ + in_span = TRUE; + old_tag = tag + 7; /* C + xxxxxx */ + } else { + /* |"Lxxx is a 3-digit colour code */ + if (purple_str_has_prefix(tag, "RED")) { + g_string_append(formatted, "<span style=\"color: red;\">"); + in_span = TRUE; + } else if (purple_str_has_prefix(tag, "GRA")) { + g_string_append(formatted, "<span style=\"color: gray;\">"); + in_span = TRUE; + } else if (purple_str_has_prefix(tag, "NOR")) { + g_string_append(formatted, "<span style=\"color: black;\">"); + in_span = TRUE; + } else if (purple_str_has_prefix(tag, "ITA")) { + g_string_append(formatted, "<span style=\"color: blue;\">"); + in_span = TRUE; + } else if (purple_str_has_prefix(tag, "GRE")) { + g_string_append(formatted, "<span style=\"color: darkgreen;\">"); + in_span = TRUE; + } else { + purple_debug_info("aMSN logger", "Unknown colour format: %3s\n", tag); + } + old_tag = tag + 3; + } + tag = strstr(tag, AMSN_LOG_FORMAT_TAG); + } + g_string_append(formatted, old_tag); + start = end + 1; + } + if (in_span) + g_string_append(formatted, "</span>"); + + g_free(contents); + + return g_string_free(formatted, FALSE); +} + +static int amsn_logger_size(PurpleLog *log) +{ + struct amsn_logger_data *data; + char *text; + int size; + + g_return_val_if_fail(log != NULL, 0); + + data = log->logger_data; + + if (purple_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) { + return data ? data->length : 0; + } + + text = amsn_logger_read(log, NULL); + size = strlen(text); + g_free(text); + + return size; +} + +static void amsn_logger_finalize(PurpleLog *log) +{ + struct amsn_logger_data *data; + + g_return_if_fail(log != NULL); + + data = log->logger_data; + g_free(data->path); + g_free(data); +} + /***************************************************************************** * Plugin Code * *****************************************************************************/ @@ -2347,6 +2702,19 @@ #endif purple_prefs_add_string("/plugins/core/log_reader/qip/log_directory", path ? path : ""); g_free(path); + + /* Add aMSN Messenger log directory preference. */ + purple_prefs_add_none("/plugins/core/log_reader/amsn"); + + /* Calculate default aMSN log directory. */ +#ifdef _WIN32 + folder = wpurple_get_special_folder(CSIDL_PROFILE); /* Silly aMSN, not using CSIDL_APPDATA */ + path = g_build_filename(folder, "amsn", NULL); +#else + path = g_build_filename(purple_home_dir(), ".amsn", NULL); +#endif + purple_prefs_add_string("/plugins/core/log_reader/amsn/log_directory", path); + g_free(path); } static gboolean @@ -2429,6 +2797,18 @@ trillian_logger_size); purple_log_logger_add(trillian_logger); + /* The names of IM clients are marked for translation at the request of + translators who wanted to transliterate them. Many translators + choose to leave them alone. Choose what's best for your language. */ + amsn_logger = purple_log_logger_new("amsn", _("aMSN"), 6, + NULL, + NULL, + amsn_logger_finalize, + amsn_logger_list, + amsn_logger_read, + amsn_logger_size); + purple_log_logger_add(amsn_logger); + return TRUE; } @@ -2445,6 +2825,7 @@ purple_log_logger_remove(msn_logger); purple_log_logger_remove(trillian_logger); purple_log_logger_remove(qip_logger); + purple_log_logger_remove(amsn_logger); return TRUE; } @@ -2505,6 +2886,10 @@ "/plugins/core/log_reader/trillian/log_directory", _("Trillian")); purple_plugin_pref_frame_add(frame, ppref); + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/log_reader/amsn/log_directory", _("aMSN")); + purple_plugin_pref_frame_add(frame, ppref); + return frame; }