log_reader.c

来自「Linux下的多协议即时通讯程序源代码」· C语言 代码 · 共 2,177 行 · 第 1/4 页

C
2,177
字号
        } else if (hour == 12) {                /* 12 AM = 00 hr */                hour = 0;        }	str = g_strdup_printf("%04i-%02i-%02iT%02i:%02i:%02i", year, month, day, hour, min, sec);	t = purple_str_to_time(str, TRUE, &tm, NULL, NULL);	if (stamp > t)		diff = stamp - t;	else		diff = t - stamp;	if (diff > (14 * 60 * 60))	{		if (day <= 12)		{			/* Swap day & month variables, to see if it's a non-US date. */			g_free(str);			str = g_strdup_printf("%04i-%02i-%02iT%02i:%02i:%02i", year, month, day, hour, min, sec);			t = purple_str_to_time(str, TRUE, &tm, NULL, NULL);			if (stamp > t)				diff = stamp - t;			else				diff = t - stamp;			if (diff > (14 * 60 * 60))			{				/* We got a time, it's not impossible, but				 * the diff is too large.  Display the UTC time. */				g_free(str);				*tm_out = &tm2;				return stamp;			}			else			{				/* Legal time */				/* Fall out */			}		}		else		{			/* We got a time, it's not impossible, but			 * the diff is too large.  Display the UTC time. */			g_free(str);			*tm_out = &tm2;			return stamp;		}	}	/* If we got here, the time is legal with a reasonable offset.	 * Let's find out if it's in our TZ. */	if (purple_str_to_time(str, FALSE, &tm, NULL, NULL) == stamp)	{		g_free(str);		*tm_out = &tm;		return stamp;	}	g_free(str);	/* The time isn't in our TZ, but it's reasonable. */#ifdef HAVE_STRUCT_TM_TM_ZONE	tm.tm_zone = "   ";#endif	*tm_out = &tm;	return stamp;}static GList *msn_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account){	GList *list = NULL;	char *username;	PurpleBuddy *buddy;	const char *logdir;	const char *savedfilename = NULL;	char *logfile;	char *path;	GError *error = NULL;	gchar *contents = NULL;	gsize length;	xmlnode *root;	xmlnode *message;	const char *old_session_id = "";	struct msn_logger_data *data = NULL;	g_return_val_if_fail(sn != NULL, list);	g_return_val_if_fail(account != NULL, list);	if (strcmp(account->protocol_id, "prpl-msn"))		return list;	logdir = purple_prefs_get_string("/plugins/core/log_reader/msn/log_directory");	/* By clearing the log directory path, this logger can be (effectively) disabled. */	if (!*logdir)		return list;	buddy = purple_find_buddy(account, sn);	if ((username = g_strdup(purple_account_get_string(			account, "log_reader_msn_log_folder", NULL)))) {		/* As a special case, we allow the null string to kill the parsing		 * straight away. This would allow the user to deal with the case		 * when two account have the same username at different domains and		 * only one has logs stored.		 */		if (!*username) {			g_free(username);			return list;		}	} else {		username = g_strdup(purple_normalize(account, account->username));	}	if (buddy)		savedfilename = purple_blist_node_get_string(&buddy->node, "log_reader_msn_log_filename");	if (savedfilename) {		/* As a special case, we allow the null string to kill the parsing		 * straight away. This would allow the user to deal with the case		 * when two buddies have the same username at different domains and		 * only one has logs stored.		 */		if (!*savedfilename) {			g_free(username);			return list;		}		logfile = g_strdup(savedfilename);	} else {		logfile = g_strdup_printf("%s.xml", purple_normalize(account, sn));	}	path = g_build_filename(logdir, username, "History", logfile, NULL);	if (!g_file_test(path, G_FILE_TEST_EXISTS)) {		gboolean found = FALSE;		char *at_sign;		GDir *dir;		g_free(path);		if (savedfilename) {			/* We had a saved filename, but it doesn't exist.			 * Returning now is the right course of action because we don't			 * want to detect another file incorrectly.			 */			g_free(username);			g_free(logfile);			return list;		}		/* Perhaps we're using a new version of MSN with the weird numbered folders.		 * I don't know how the numbers are calculated, so I'm going to attempt to		 * find logs by pattern matching...		 */		at_sign = g_strrstr(username, "@");		if (at_sign)			*at_sign = '\0';		dir = g_dir_open(logdir, 0, NULL);		if (dir) {			const gchar *name;			while ((name = g_dir_read_name(dir))) {				const char *c = name;				if (!purple_str_has_prefix(c, username))					continue;				c += strlen(username);				while (*c) {					if (!g_ascii_isdigit(*c))						break;					c++;				}				path = g_build_filename(logdir, name, NULL);				/* The !c makes sure we got to the end of the while loop above. */				if (!*c && g_file_test(path, G_FILE_TEST_IS_DIR)) {					char *history_path = g_build_filename(						path,  "History", NULL);					if (g_file_test(history_path, G_FILE_TEST_IS_DIR)) {						purple_account_set_string(account,							"log_reader_msn_log_folder", name);						g_free(path);						path = history_path;						found = TRUE;						break;					}					g_free(path);					g_free(history_path);				}				else					g_free(path);			}			g_dir_close(dir);		}		g_free(username);		if (!found) {			g_free(logfile);			return list;		}		/* If we've reached this point, we've found a History folder. */		username = g_strdup(purple_normalize(account, sn));		at_sign = g_strrstr(username, "@");		if (at_sign)			*at_sign = '\0';		found = FALSE;		dir = g_dir_open(path, 0, NULL);		if (dir) {			const gchar *name;			while ((name = g_dir_read_name(dir))) {				const char *c = name;				if (!purple_str_has_prefix(c, username))					continue;				c += strlen(username);				while (*c) {					if (!g_ascii_isdigit(*c))						break;					c++;				}				path = g_build_filename(path, name, NULL);				if (!strcmp(c, ".xml") &&				    g_file_test(path, G_FILE_TEST_EXISTS)) {					found = TRUE;					g_free(logfile);					logfile = g_strdup(name);					break;				}				else					g_free(path);			}			g_dir_close(dir);		}		g_free(username);		if (!found) {			g_free(logfile);			return list;		}	} else {		g_free(username);		g_free(logfile);		logfile = NULL; /* No sense saving the obvious buddy@domain.com. */	}	purple_debug(PURPLE_DEBUG_INFO, "MSN log read",				"Reading %s\n", path);	if (!g_file_get_contents(path, &contents, &length, &error)) {		g_free(path);		purple_debug(PURPLE_DEBUG_ERROR, "MSN log read",				"Error reading log\n");		if (error)			g_error_free(error);		return list;	}	g_free(path);	/* Reading the file was successful...	 * Save its name if it involves the crazy numbers. The idea here is that you could	 * then tweak the blist.xml file by hand if need be. This would be the case if two	 * buddies have the same username at different domains. One set of logs would get	 * detected for both buddies.	 */	if (buddy && logfile) {		purple_blist_node_set_string(&buddy->node, "log_reader_msn_log_filename", logfile);		g_free(logfile);	}	root = xmlnode_from_str(contents, length);	g_free(contents);	if (!root)		return list;	for (message = xmlnode_get_child(root, "Message"); message;			message = xmlnode_get_next_twin(message)) {		const char *session_id;		session_id = xmlnode_get_attrib(message, "SessionID");		if (!session_id) {			purple_debug(PURPLE_DEBUG_ERROR, "MSN log parse",					"Error parsing message: %s\n", "SessionID missing");			continue;		}		if (strcmp(session_id, old_session_id)) {			/*			 * The session ID differs from the last message.			 * Thus, this is the start of a new conversation.			 */			struct tm *tm;			time_t stamp;			PurpleLog *log;			data = g_new0(struct msn_logger_data, 1);			data->root = root;			data->message = message;			data->session_id = session_id;			data->text = NULL;			data->last_log = FALSE;			stamp = msn_logger_parse_timestamp(message, &tm);			log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, stamp, tm);			log->logger = msn_logger;			log->logger_data = data;			list = g_list_append(list, log);		}		old_session_id = session_id;	}	if (data)		data->last_log = TRUE;	return list;}static char * msn_logger_read (PurpleLog *log, PurpleLogReadFlags *flags){	struct msn_logger_data *data;	GString *text = NULL;	xmlnode *message;	g_return_val_if_fail(log != NULL, g_strdup(""));	data = log->logger_data;	if (data->text) {		/* The GTK code which displays the logs g_free()s whatever is		 * returned from this function. Thus, we can't reuse the str		 * part of the GString. The only solution is to free it and		 * start over.		 */		g_string_free(data->text, FALSE);	}	text = g_string_new("");	if (!data->root || !data->message || !data->session_id) {		/* Something isn't allocated correctly. */		purple_debug(PURPLE_DEBUG_ERROR, "MSN log parse",				"Error parsing message: %s\n", "Internal variables inconsistent");		data->text = text;		return text->str;	}	for (message = data->message; message;			message = xmlnode_get_next_twin(message)) {		const char *new_session_id;		xmlnode *text_node;		const char *from_name = NULL;		const char *to_name = NULL;		xmlnode *from;		xmlnode *to;		enum name_guesses name_guessed = NAME_GUESS_UNKNOWN;		const char *their_name;		time_t time_unix;		struct tm *tm;		char *timestamp;		char *tmp;		const char *style;		new_session_id = xmlnode_get_attrib(message, "SessionID");		/* If this triggers, something is wrong with the XML. */		if (!new_session_id) {			purple_debug(PURPLE_DEBUG_ERROR, "MSN log parse",					"Error parsing message: %s\n", "New SessionID missing");			break;		}		if (strcmp(new_session_id, data->session_id)) {			/* The session ID differs from the first message.			 * Thus, this is the start of a new conversation.			 */			break;		}		text_node = xmlnode_get_child(message, "Text");		if (!text_node)			continue;		from = xmlnode_get_child(message, "From");		if (from) {			xmlnode *user = xmlnode_get_child(from, "User");			if (user) {				from_name = xmlnode_get_attrib(user, "FriendlyName");				/* This saves a check later. */				if (!*from_name)					from_name = NULL;			}		}		to = xmlnode_get_child(message, "To");		if (to) {			xmlnode *user = xmlnode_get_child(to, "User");			if (user) {				to_name = xmlnode_get_attrib(user, "FriendlyName");				/* This saves a check later. */				if (!*to_name)					to_name = NULL;			}		}		their_name = from_name;		if (from_name && purple_prefs_get_bool("/plugins/core/log_reader/use_name_heuristics")) {			const char *friendly_name = purple_connection_get_display_name(log->account->gc);			if (friendly_name != NULL) {				int friendly_name_length = strlen(friendly_name);				const char *alias;				int alias_length;				PurpleBuddy *buddy = purple_find_buddy(log->account, log->name);				gboolean from_name_matches;				gboolean to_name_matches;				if (buddy && buddy->alias)					their_name = buddy->alias;				if (log->account->alias)				{					alias = log->account->alias;					alias_length = strlen(alias);				}				else				{					alias = "";					alias_length = 0;				}				/* Try to guess which user is me.				 * The first step is to determine if either of the names matches either my				 * friendly name or alias. For this test, "match" is defined as:				 * ^(friendly_name|alias)([^a-zA-Z0-9].*)?$				 */				from_name_matches = (purple_str_has_prefix(from_name, friendly_name) &&				                      !isalnum(*(from_name + friendly_name_length))) ||				                     (purple_str_has_prefix(from_name, alias) &&				                      !isalnum(*(from_name + alias_length)));				to_name_matches = to_name != NULL && (				                   (purple_str_has_prefix(to_name, friendly_name) &&				                    !isalnum(*(to_name + friendly_name_length))) ||				                   (purple_str_has_prefix(to_name, alias) &&				                    !isalnum(*(to_name + alias_length))));				if (from_name_matches) {					if (!to_name_matches) {						name_guessed = NAME_GUESS_ME;					}				} else if (to_name_matches) {					name_guessed = NAME_GUESS_THEM;				} else {					if (buddy && buddy->alias) {						char *alias = g_strdup(buddy->alias);						/* "Truncate" the string at the first non-alphanumeric						 * character. The idea is to relax the comparison.						 */						char *temp;						for (temp = alias; *temp ; temp++) {							if (!isalnum(*temp)) {								*temp = '\0';								break;							}						}						alias_length = strlen(alias);						/* Try to guess which user is them.						 * The first step is to determine if either of the names						 * matches their alias. For this test, "match" is						 * defined as: ^alias([^a-zA-Z0-9].*)?$						 */						from_name_matches = (purple_str_has_prefix(								from_name, alias) &&								!isalnum(*(from_name +								alias_length)));						to_name_matches = to_name && (purple_str_has_prefix(								to_name, alias) &&								!isalnum(*(to_name +								alias_length)));						g_free(alias);						if (from_name_matches) {							if (!to_name_matches) {								name_guessed = NAME_GUESS_THEM;							}						} else if (to_name_matches) {							name_guessed = NAME_GUESS_ME;						} else if (buddy->server_alias) {							friendly_name_length =								strlen(buddy->server_alias);							/* Try to guess which user is them.							 * The first step is to determine if either of							 * the names matches their friendly name. For							 * this test, "match" is defined as:							 * ^friendly_name([^a-zA-Z0-9].*)?$							 */							from_name_matches = (purple_str_has_prefix(									from_name,									buddy->server_alias) &&									!isalnum(*(from_name +									friendly_name_length)));							to_name_matches = to_name && (									(purple_str_has_prefix(									to_name, buddy->server_alias) &&									!isalnum(*(to_name +									friendly_name_length))));							if (from_name_matches) {								if (!to_name_matches) {									name_guessed = NAME_GUESS_THEM;								}							} else if (to_name_matches) {								name_guessed = NAME_GUESS_ME;							}						}					}				}			}		}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?