im.pidgin.pidgin: 68d63cab97f8861a7a080c3103d5e24a31587473
sadrul at pidgin.im
sadrul at pidgin.im
Fri Oct 12 07:50:32 EDT 2007
-----------------------------------------------------------------
Revision: 68d63cab97f8861a7a080c3103d5e24a31587473
Ancestor: 4d9fac4aab275ee133ce860edc469e0e5c4734ff
Author: sadrul at pidgin.im
Date: 2007-10-12T11:51:13
Branch: im.pidgin.pidgin
Modified files:
libpurple/plugins/log_reader.c
ChangeLog:
Patch from QuLogic to add support for aMSN logs from the log-reader plugin.
This is really awesome!
Closes #3497.
-------------- next part --------------
============================================================
--- libpurple/plugins/log_reader.c b9efb34aa6dd084cdb8a93deb9554017c3347ce4
+++ libpurple/plugins/log_reader.c 59931ccc492d9f2d432e0a39a538005c8d679d34
@@ -2098,6 +2098,361 @@ static void qip_logger_finalize(PurpleLo
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 at hotmail.com/logs/buddyname at hotmail.com.log */
+/* `log_dir`/username at hotmail.com/logs/Month Year/buddyname at 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 @@ init_plugin(PurplePlugin *plugin)
#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 @@ plugin_load(PurplePlugin *plugin)
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 @@ plugin_unload(PurplePlugin *plugin)
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 @@ get_plugin_pref_frame(PurplePlugin *plug
"/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;
}
More information about the Commits
mailing list