changeset 18243:a8b19e002cb4

propagate from branch 'im.pidgin.pidgin' (head e9da27c3123d1223162bf9fb76f58822873e18f8) to branch 'im.pidgin.soc.2007.remotelogging' (head da5442aedd302aafb260948b66ce0549a3e0a0e4)
author Richard Laager <rlaager@wiktel.com>
date Sun, 24 Jun 2007 20:01:43 +0000
parents b14dfa2f49a1 (diff) bc4518599c10 (current diff)
children b7c30039b531
files COPYRIGHT libpurple/protocols/qq/group_misc.c libpurple/protocols/qq/group_misc.h
diffstat 2 files changed, 470 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sat Jun 23 06:32:09 2007 +0000
+++ b/COPYRIGHT	Sun Jun 24 20:01:43 2007 +0000
@@ -324,6 +324,7 @@
 Joe Shaw
 Scott Shedden
 Dossy Shiobara
+Michael Shkutkov
 Ettore Simone
 John Silvestri
 Craig Slusher
--- a/libpurple/plugins/log_reader.c	Sat Jun 23 06:32:09 2007 +0000
+++ b/libpurple/plugins/log_reader.c	Sun Jun 24 20:01:43 2007 +0000
@@ -1731,6 +1731,428 @@
 
 }
 
+/*****************************************************************************
+ * QIP Logger                                                           *
+ *****************************************************************************/
+
+/* The QIP logger doesn't write logs, only reads them.  This is to include
+ * QIP logs in the log viewer transparently.
+ */
+#define QIP_LOG_DELIMITER "--------------------------------------"
+#define QIP_LOG_IN_MESSAGE (QIP_LOG_DELIMITER "<-")
+#define QIP_LOG_OUT_MESSAGE (QIP_LOG_DELIMITER ">-")
+#define QIP_LOG_IN_MESSAGE_ESC (QIP_LOG_DELIMITER "&lt;-")
+#define QIP_LOG_OUT_MESSAGE_ESC (QIP_LOG_DELIMITER "&gt;-")
+#define QIP_LOG_TIMEOUT (60*60)
+
+static PurpleLogLogger *qip_logger;
+
+struct qip_logger_data {
+	
+	char *path; /* FIXME: Change this to use PurpleStringref like log.c:old_logger_list  */
+	int offset;
+	int length;
+};
+
+static GList *qip_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account)
+{
+	GList *list = NULL;
+	const char *logdir;
+	PurplePlugin *plugin;
+	PurplePluginProtocolInfo *prpl_info;
+	const char *buddy_name;
+	char *username;
+	char *filename;
+	char *path;
+	gsize length;
+	GError *error = NULL;
+	gchar *contents = NULL;
+
+	g_return_val_if_fail(sn != NULL, list);
+	g_return_val_if_fail(account != NULL, list);
+
+	purple_debug(PURPLE_DEBUG_INFO, "QIP logger list",
+		"arguments not NULL\n");
+
+	/* QIP is ICQ messenger. Should we add prpl-aim? */
+	if (strcmp(account->protocol_id, "prpl-icq"))
+		return list;
+		
+	purple_debug(PURPLE_DEBUG_INFO, "QIP logger list",
+		"protocol is 'prpl-icq'\n");
+
+	logdir = purple_prefs_get_string("/plugins/core/log_reader/qip/log_directory");
+
+	/* By clearing the log directory path, this logger can be (effectively) disabled. */
+	if (!*logdir)
+		return list;
+
+	plugin = purple_find_prpl(purple_account_get_protocol_id(account));
+	if (!plugin)
+		return NULL;
+
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
+	if (!prpl_info->list_icon)
+		return NULL;
+
+	buddy_name = g_strdup(purple_normalize(account, sn));
+	purple_debug(PURPLE_DEBUG_INFO, "QIP logger list",
+		"buddy_name %s\n", buddy_name);
+
+	username = g_strdup(purple_normalize(account, account->username));
+	purple_debug(PURPLE_DEBUG_INFO, "QIP logger list",
+		"username %s\n", username);
+	
+	purple_debug(PURPLE_DEBUG_INFO, "QIP logger list",
+		"sn %s\n", sn);
+	
+	filename = g_strdup_printf("%s.txt", buddy_name);
+	path = g_build_filename(
+		logdir, username, "History", filename, NULL);
+		
+	purple_debug(PURPLE_DEBUG_INFO, "QIP logger list",
+		"Reading %s\n", path);
+	
+	error = NULL;
+	if (!g_file_get_contents(path, &contents, &length, &error)) {
+		if (error) {
+			purple_debug(PURPLE_DEBUG_ERROR, "QIP logger list",
+				"Couldn't read file %s \n", path);
+
+			g_error_free(error);
+		}
+	} else if (contents) {
+		struct qip_logger_data *data = NULL;
+		gchar * utf8_string = NULL;
+
+		purple_debug(PURPLE_DEBUG_INFO, "QIP logger list",
+			"File %s is found\n", filename);
+			
+		/* We should convert file contents from Cp1251 to UTF-8 codeset */
+		error = NULL;
+		if (!(utf8_string = g_convert(contents, length, "UTF-8", "Cp1251", NULL, NULL, &error))) {
+			if (error) {
+				purple_debug(PURPLE_DEBUG_ERROR, "QIP logger list",
+					"Couldn't convert file %s to UTF-8\n", filename);
+				g_error_free(error);
+			}
+		} else {
+			struct tm prev_tm;
+			gboolean prev_tm_init = FALSE;
+			gchar *c;
+			gchar *start_log;
+			gchar *escaped;
+			int offset = 0;
+			
+			purple_debug(PURPLE_DEBUG_INFO, "QIP logger lise",
+				"File %s converted successfully\n", filename);
+			
+			g_free(contents);
+			escaped = g_markup_escape_text(utf8_string, -1);
+			contents = escaped;
+		
+			c = contents;
+			start_log = contents;
+			while (*c) {
+				if (purple_str_has_prefix(c, QIP_LOG_IN_MESSAGE_ESC) || 
+					purple_str_has_prefix(c, QIP_LOG_OUT_MESSAGE_ESC)) {
+					gchar *new_line = c;
+
+					purple_debug(PURPLE_DEBUG_INFO, "QIP logger lise",
+						"Find message\n", filename);
+					
+					/* find EOL */
+					c = strstr(c, "\n");
+					c++;
+					/* searching '(' character from the end of the line */
+					c = strstr(c, "\n");
+					while (*c && *c != '(')
+						--c;
+
+					if (*c == '(') {
+						const char *timestamp = ++c;
+						struct tm tm;
+						purple_debug(PURPLE_DEBUG_INFO, "QIP logger list",
+							"Timestap found\n");
+						
+						/*  Parse the time, day, month and year  */
+						if (sscanf(timestamp, "%u:%u:%u %u/%u/%u",
+							&tm.tm_hour, &tm.tm_min, &tm.tm_sec,
+							&tm.tm_mday, &tm.tm_mon, &tm.tm_year) != 6) {
+								purple_debug(PURPLE_DEBUG_ERROR, "QIP logger list",
+								"Parsing timestamp error\n");
+						} else {
+							/* cos month of year in [0,11] */
+							tm.tm_mon -= 1; 
+							/* cos years since 1900  */
+							tm.tm_year -= 1900;
+
+							/* Let the C library deal with
+							* daylight savings time. */
+							tm.tm_isdst = -1;
+
+							if (!prev_tm_init) {
+								prev_tm = tm;
+								prev_tm_init = TRUE;
+								
+							} else {
+								double time_diff = 0;
+								
+								time_diff = difftime(mktime(&tm), mktime(&prev_tm));
+
+								if (time_diff > QIP_LOG_TIMEOUT) {
+									PurpleLog *log;
+									
+									/* filling data */
+									data = g_new0(
+										struct qip_logger_data, 1);
+									data->path = g_strdup(path);
+									data->length = new_line - start_log;
+									data->offset = offset;
+									offset += data->length;
+									
+									purple_debug(PURPLE_DEBUG_ERROR, "QIP logger list",
+										"Creating log: path = (%s); length = (%d); offset = (%d)\n", data->path, data->length, data->offset);
+
+									/* XXX: Look into this later... Should we pass in a struct tm? */
+									log = purple_log_new(PURPLE_LOG_IM,
+										sn, account, NULL, mktime(&prev_tm), NULL);
+									
+									log->logger = qip_logger;
+									log->logger_data = data;
+									
+									list = g_list_append(list, log);
+									
+									prev_tm = tm;
+									start_log = new_line;
+								}
+							}
+							
+							/* find EOF */
+							c = strstr(c, "\n");
+							c++;
+						}
+					}
+				} else {
+					c = strstr(c, "\n");
+					c++;
+				}
+			}
+			
+			/* adding last log */
+			if (prev_tm_init) {
+				PurpleLog *log;
+			
+				/* filling data */
+				data = g_new0(
+				struct qip_logger_data, 1);
+				data->path = g_strdup(path);
+				data->length = c - start_log;
+				data->offset = offset;
+				offset += data->length;
+				purple_debug(PURPLE_DEBUG_ERROR, "QIP logger list",
+					"Creating log: path = (%s); length = (%d); offset = (%d)\n", data->path, data->length, data->offset);
+
+					/* XXX: Look into this later... Should we pass in a struct tm? */
+				log = purple_log_new(PURPLE_LOG_IM,
+					sn, account, NULL, mktime(&prev_tm), NULL);
+
+					log->logger = qip_logger;
+				log->logger_data = data;
+
+				list = g_list_append(list, log);
+			}
+		}
+		g_free(contents); 
+	} 
+
+	g_free(username);
+	g_free(path);
+	g_free(filename);
+
+	return list;
+}
+	
+static char * qip_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
+{
+	struct qip_logger_data *data;
+	PurpleBuddy *buddy;
+	GString *formatted;
+	char *c;
+	const char *line;
+	GError *error = NULL;
+	gchar *contents = NULL;
+	gsize length;
+	
+	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(""));
+
+	purple_debug(PURPLE_DEBUG_INFO, "QIP logger read",
+				"Reading %s\n", data->path);
+	
+	error = NULL;
+	if (!g_file_get_contents(data->path, &contents, &length, &error)) 
+		if (error) {
+			purple_debug(PURPLE_DEBUG_ERROR, "QIP logger list",
+				"Couldn't read file %s \n", data->path);
+
+			g_error_free(error);
+		}
+	if (contents) {
+		gchar * utf8_string;
+		
+		/* We should convert file contents from Cp1251 to UTF-8 codeset */
+		error = NULL;
+		if (!(utf8_string = g_convert (contents, length, "UTF-8", "Cp1251", NULL, NULL, &error))) {
+			if (error) {
+				purple_debug(PURPLE_DEBUG_ERROR, "QIP logger read",
+					"Couldn't convert file %s to UTF-8\n", data->path);
+				g_error_free(error);
+			}
+		} else {
+			char *escaped;
+			char *selected;
+
+			purple_debug(PURPLE_DEBUG_INFO, "QIP logger read",
+				"File %s converted successfully\n", data->path);
+			
+			g_free(contents);
+			contents = utf8_string;
+		
+			/* Load miscellaneous data. */
+			buddy = purple_find_buddy(log->account, log->name);
+			
+			escaped = g_markup_escape_text(contents, -1);
+			g_free(contents);
+			contents = escaped;
+			
+			selected = g_strndup(contents + data->offset, data->length + 2);
+			selected[data->length] = '\n';
+			selected[data->length + 1] = '\0';
+			
+			g_free(contents);
+			contents = selected;
+			
+			/* Apply formatting... */
+			formatted = g_string_sized_new(strlen(contents));
+			c = contents;
+			line = contents;
+			
+			while (*c) {
+				gboolean is_in_message = FALSE;
+				
+				if (purple_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC) || 
+					purple_str_has_prefix(line, QIP_LOG_OUT_MESSAGE_ESC)) {
+					const char *buddy_name;
+					is_in_message = purple_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC);
+					
+					/* find EOL */
+					c = strstr(c, "\n");
+
+					/* XXX: Do we need buddy_name when we have buddy->alias? */
+					buddy_name = ++c;
+					
+					/* searching '(' character from the end of the line */
+					c = strstr(c, "\n");
+					while (*c && *c != '(')
+						--c;
+
+					if (*c == '(') {
+						const char *timestamp = c;
+						int hour;
+						int min;
+						int sec;
+						
+						timestamp++;
+							
+						/*  Parse the time, day, month and year */
+						if (sscanf(timestamp, "%u:%u:%u",
+								&hour, &min, &sec) != 3) 
+							purple_debug(PURPLE_DEBUG_ERROR, "QIP logger read",
+								"Parsing timestamp error\n");
+						else {
+							g_string_append(formatted, "<font size=\"2\">");
+							g_string_append_printf(formatted, 
+								"(%u:%02u:%02u) %cM ", hour % 12, 
+								min, sec, (hour >= 12) ? 'P': 'A');
+							g_string_append(formatted, "</font> ");
+							
+							if (is_in_message) {
+								if (buddy_name != NULL && buddy->alias) {
+									g_string_append_printf(formatted, 
+										"<span style=\"color: #A82F2F;\">"
+										"<b>%s</b></span>: ", buddy->alias);
+								}
+							} else {
+								const char *acct_name;
+								acct_name = purple_account_get_alias(log->account);
+								if (!acct_name)
+									acct_name = purple_account_get_username(log->account);
+
+								g_string_append_printf(formatted,
+									"<span style=\"color: #16569E;\">"
+									"<b>%s</b></span>: ", acct_name);
+							}
+							
+							/* find EOF */
+							c = strstr(c, "\n");
+							line = ++c;
+						}
+					}
+				} else {
+					if ((c = strstr(c, "\n")))
+						*c = '\0';
+				
+					if (line[0] != '\n' && line[0] != '\r') {
+						
+						g_string_append(formatted, line);
+						g_string_append_c(formatted, '\n');
+					}
+					line = ++c;
+				}
+			}
+		}
+	}
+	g_free(contents);
+	/* XXX: TODO: Avoid this g_strchomp() */
+	return g_strchomp(g_string_free(formatted, FALSE));
+}
+
+static int qip_logger_size (PurpleLog *log)
+{
+	struct qip_logger_data *data;
+	char *text;
+	size_t 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 = qip_logger_read(log, NULL); 
+	size = strlen(text);
+	g_free(text);
+
+	return size;
+}
+
+static void qip_logger_finalize(PurpleLog *log)
+{
+	struct qip_logger_data *data;
+
+	g_return_if_fail(log != NULL);
+
+	data = log->logger_data;
+
+	g_free(data->path);
+}
 
 /*****************************************************************************
  * Plugin Code                                                               *
@@ -1896,6 +2318,8 @@
 
 		purple_debug(PURPLE_DEBUG_INFO, "Trillian talk.ini read",
 				"Reading %s\n", path);
+				
+		error = NULL;
 		if (!g_key_file_load_from_file(key_file, path, G_KEY_FILE_NONE, GError &error)) {
 			purple_debug(PURPLE_DEBUG_ERROR, "Trillian talk.ini read",
 					"Error reading talk.ini\n");
@@ -1977,12 +2401,35 @@
 		path = g_strdup("");
 #endif
 
-	purple_prefs_add_string("/plugins/core/log_reader/trillian/log_directory", path);
-	g_free(path);
-
 #ifdef _WIN32
 	} /* !found */
 #endif
+
+	/* Add QIP log directory preference. */
+	purple_prefs_add_none("/plugins/core/log_reader/qip");
+
+#ifdef _WIN32 
+	/* Calculate default Messenger Plus! log directory. */
+	folder = wpurple_get_special_folder(CSIDL_PROGRAM_FILES);
+	if (folder) {
+#endif
+	path = g_build_filename(
+#ifdef _WIN32 
+		folder,
+#else
+		PURPLE_LOG_READER_WINDOWS_MOUNT_POINT, "Program Files",
+#endif
+		"QIP", "Users", NULL);
+#ifdef _WIN32
+	g_free(folder);
+	} else /* !folder */
+		path = g_strdup("");
+#endif
+
+	purple_debug(PURPLE_DEBUG_INFO, "QIP log reader", "QIP log directory %s\n", path);
+
+	purple_prefs_add_string("/plugins/core/log_reader/qip/log_directory", path);
+	g_free(path);
 }
 
 static gboolean
@@ -2026,11 +2473,24 @@
 												messenger_plus_logger_read,
 												messenger_plus_logger_size);
 	purple_log_logger_add(messenger_plus_logger);
+	
 #endif
 
 	/* 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. */
+	qip_logger = purple_log_logger_new("qip", _("QIP"), 6,
+											NULL,
+											NULL,
+											qip_logger_finalize,
+											qip_logger_list,
+											qip_logger_read,
+											qip_logger_size);
+	purple_log_logger_add(qip_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. */
 	msn_logger = purple_log_logger_new("msn", _("MSN Messenger"), 6,
 									 NULL,
 									 NULL,
@@ -2067,6 +2527,7 @@
 #endif
 	purple_log_logger_remove(msn_logger);
 	purple_log_logger_remove(trillian_logger);
+	purple_log_logger_remove(qip_logger);
 
 	return TRUE;
 }
@@ -2116,6 +2577,11 @@
 #endif
 
 	ppref = purple_plugin_pref_new_with_name_and_label(
+		"/plugins/core/log_reader/qip/log_directory", _("QIP"));
+	purple_plugin_pref_frame_add(frame, ppref);
+	purple_debug(PURPLE_DEBUG_INFO, "QIP log reader", "QIP creating directory\n");
+
+	ppref = purple_plugin_pref_new_with_name_and_label(
 		"/plugins/core/log_reader/msn/log_directory", _("MSN Messenger"));
 	purple_plugin_pref_frame_add(frame, ppref);