cpw.nader.asynclogging-3: 2ea7966c: Lots of changes!

morshed.nader at gmail.com morshed.nader at gmail.com
Sat Jan 7 22:01:55 EST 2012


----------------------------------------------------------------------
Revision: 2ea7966cb2b0c4f1dd422bcf3d5645237e29bc1d
Parent:   1b132d7543287542e1eb64783c43e88267c73c16
Author:   morshed.nader at gmail.com
Date:     01/07/12 21:25:15
Branch:   im.pidgin.cpw.nader.asynclogging-3
URL: http://d.pidgin.im/viewmtn/revision/info/2ea7966cb2b0c4f1dd422bcf3d5645237e29bc1d

Changelog: 

Lots of changes!
Added a search_async option to the logging code, allowing us to have loggers that can be queried with search terms, rather than always having to read/search entire files ourself
Removed the locking code from gntlog.c, not sure why it was still there
Updated gtklog.c and gntlog.c to use the new search code, as well as other API changes
Added a write_async function to commonlog.c, not yet tested, however, don't think it works on windows yet
Moved the list code that defined the number of files to query/enumerate at a time into a macro
Changed the list/total-size functions in commonlog to take the class, rather than the extension
Moved the extension as a property into the PurpleCommonLogClass public structure
Made all the purple_common_log_create* function internal to commonlog.c and have them be called when necessary from the write functions
Commented out the failed_write code, still need to figure out where to place all that
Changed when the common logs' paths are set to make more sense
The purple_common_log_create code now returns the socket, rather than setting the property on the log
The common_log_write functions now take write flags (And have been filled in, see above)
Few other miscellaneous changes to the common log API
purple_log_write_async no longer takes a callback function, updated the rest of libpurple to reflect that
Got rid of a lot of the html write and txt write code, now that they share the common log writer
Did away with the list_syslog code, as we can just call purple_logs_list*(PURPLE_LOG_SYSTEM, NULL, ...) instead
Fixed an issue that was causing some of the logging modules to crash on exit
Cleaned up/standardized a lot more code in log.c
Renamed purple_logs_list_logs* to just purple_logs_list*
Removed the write_finish functions as they are no longer needed

Changes against parent 1b132d7543287542e1eb64783c43e88267c73c16

  patched  finch/gntlog.c
  patched  libpurple/commonlog.c
  patched  libpurple/commonlog.h
  patched  libpurple/htmllog.c
  patched  libpurple/log.c
  patched  libpurple/log.h
  patched  libpurple/oldlog.c
  patched  libpurple/txtlog.c
  patched  pidgin/gtklog.c

-------------- next part --------------
============================================================
--- libpurple/log.c	de7cf900f53c7a435cca9cdae197c13f80ea590e
+++ libpurple/log.c	12cd8559d11a399037040a7ea750d0c7ccfca762
@@ -40,8 +40,10 @@
 
 
 /* Helpful macros */
-#define PURPLE_IS_ACCOUNT(account) TRUE
-#define PURPLE_IS_CONVERSATION(account) TRUE
+#define FILES_PER_SEARCH 100
+
+#define PURPLE_IS_ACCOUNT(account) (account != NULL)
+#define PURPLE_IS_CONVERSATION(conv) (conv != NULL)
 #define PURPLE_ACCOUNT(account) ((PurpleAccount *) account)
 
 #if ! GLIB_CHECK_VERSION(2, 19, 8)
@@ -146,10 +148,23 @@ typedef struct {
 	GAsyncReadyCallback cb;
 	gpointer userdata;
 	guint counter;
-} _list_logs_callback_data;
+} list_callback_data;
 
 typedef struct {
+	gint io_priority;
+	GCancellable *cancel;
 	GAsyncReadyCallback cb;
+	gpointer userdata;
+	guint searches;
+	guint lists;
+	guint reads;
+	gchar *search_term;
+	GList *logs;
+	GList *logs_with_string;
+} search_callback_data;
+
+typedef struct {
+	GAsyncReadyCallback cb;
 	GHashTable *sets;
 	gpointer userdata;
 	guint counter;
@@ -158,7 +173,6 @@ static GList *purple_log_real_list_logs_
 static gssize purple_log_real_size_finish(PurpleLog *, GAsyncResult *,
 	GError **);
 static GList *purple_log_real_list_logs_finish(GAsyncResult *, GError **);
-static GList *purple_log_real_list_syslog_finish(GAsyncResult *, GError **);
 static gssize purple_log_real_total_size_finish(GAsyncResult *, GError **);
 static GHashTable *purple_log_real_get_log_sets_finish(GAsyncResult *,
 	GError **);
@@ -171,8 +185,10 @@ static void log_list_cb(GObject *, GAsyn
 	GError **);
 
 static void log_list_cb(GObject *, GAsyncResult *, gpointer);
-static void log_system_list_cb(GObject *, GAsyncResult *, gpointer);
 static void log_total_size_cb(GObject *, GAsyncResult *, gpointer);
+static void log_search_cb(GObject *, GAsyncResult *, gpointer);
+static void log_search_read_cb(GObject *, GAsyncResult *, gpointer);
+static void log_search_list_cb(GObject *, GAsyncResult *, gpointer);
 static void log_hash_cb(GObject *, GAsyncResult *, gpointer);
 
 static GArray *purple_log_logger_get_all(void);
@@ -239,7 +255,20 @@ static void
 #endif
 
 static void
-_notify_not_supported(GObject *obj, GAsyncReadyCallback cb, gpointer userdata) {
+search_callback_data_free(gpointer userdata)
+{
+	search_callback_data *data = userdata;
+
+	if (data->cancel != NULL)
+		g_object_unref(data->cancel);
+
+	g_free(data->search_term);
+	g_free(data);
+}
+
+static void
+_notify_not_supported(GObject *obj, GAsyncReadyCallback cb, gpointer userdata)
+{
 	if (cb != NULL) {
 		GSimpleAsyncResult *simple;
 
@@ -269,7 +298,6 @@ purple_log_class_init(PurpleLogClass *cl
 	class->size_finish = purple_log_real_size_finish;
 	class->total_size_finish = purple_log_real_total_size_finish;
 	class->list_finish = purple_log_real_list_logs_finish;
-	class->list_syslog_finish = purple_log_real_list_syslog_finish;
 	class->get_log_sets_finish = purple_log_real_get_log_sets_finish;
 	class->remove_finish = purple_log_real_remove_finish;
 
@@ -617,7 +645,7 @@ gssize
 }
 
 gssize
-purple_log_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from,
+purple_log_write(PurpleLog *log, PurpleMessageFlags flags, const gchar *from,
 	time_t time, const gchar *message, GCancellable *cancellable,
 	GError **error)
 {
@@ -627,6 +655,15 @@ purple_log_write(PurpleLog *log, PurpleM
 	g_return_val_if_fail(from != NULL, -1);
 	g_return_val_if_fail(message != NULL, -1);
 
+	if (flags & PURPLE_MESSAGE_NO_LOG) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_FAILED,
+			_("Trying to log when flagged not to"));
+
+		return -1;
+	}
+
 	class = PURPLE_LOG_GET_CLASS(log);
 
 	if (class->write_fn == NULL) {
@@ -638,42 +675,46 @@ purple_log_write(PurpleLog *log, PurpleM
 		return -1;
 	}
 
-	return class->write_fn(log, type, from, time, message, cancellable, error);
+	return class->write_fn(log, flags, from, time, message, cancellable, error);
 }
 
 void
-purple_log_write_async(PurpleLog *log, PurpleMessageFlags type,
+purple_log_write_async(PurpleLog *log, PurpleMessageFlags flags,
 	const gchar *from, time_t time, const gchar *message, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+	GCancellable *cancellable)
 {
-	PurpleLogClass *class;
+	PurpleLogClass *klass;
 
 	g_return_if_fail(PURPLE_IS_LOG(log));
 	g_return_if_fail(message != NULL);
 	g_return_if_fail(from != NULL);
-	g_return_if_fail(cb == NULL);
-	g_return_if_fail(userdata == NULL);
 
-	class = PURPLE_LOG_GET_CLASS(log);
+	if (flags & PURPLE_MESSAGE_NO_LOG) {
+		purple_debug_warning("log", _("Trying to log when flagged not to"));
+		return;
+	}
 
-	if (class->write_async == NULL)
-		_notify_not_supported(G_OBJECT(log), cb, userdata);
+	klass = PURPLE_LOG_GET_CLASS(log);
+
+	if (klass->write_async == NULL)
+		/* XXX: Signal error */
+		_notify_not_supported(G_OBJECT(log), NULL, NULL);
 	else
-		PURPLE_LOG_GET_CLASS(log)->write_async(log, type, from, time, message,
-			io_priority, cancellable, cb, userdata);
+		klass->write_async(log, flags, from, time, message,
+			io_priority, cancellable);
 }
 
 gchar *
 purple_log_read(PurpleLog *log, PurpleLogReadFlags *flags,
 	GCancellable *cancellable, GError **error)
 {
-	PurpleLogClass *class;
+	PurpleLogClass *klass;
 
 	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
 
-	class = PURPLE_LOG_GET_CLASS(log);
+	klass = PURPLE_LOG_GET_CLASS(log);
 
-	if (class->read_fn == NULL) {
+	if (klass->read_fn == NULL) {
 		g_set_error_literal(error,
 			G_IO_ERROR,
 			G_IO_ERROR_NOT_SUPPORTED,
@@ -682,36 +723,24 @@ purple_log_read(PurpleLog *log, PurpleLo
 		return NULL;
 	}
 
-	return class->read_fn(log, flags, cancellable, error);
+	return klass->read_fn(log, flags, cancellable, error);
 }
 
 void
 purple_log_read_async(PurpleLog *log, gint io_priority,
 	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
 {
-	g_return_if_fail(PURPLE_IS_LOG(log));
+	PurpleLogClass *klass;
 
-	PURPLE_LOG_GET_CLASS(log)->read_async(log,
-		io_priority, cancellable, cb, userdata);
-}
+	g_return_if_fail(PURPLE_IS_LOG(log));
 
-#if 0
-static gchar *
-purple_log_real_read_finish(PurpleLog *log, GAsyncResult *res,
-	PurpleLogReadFlags *flags, GError **error)
-{
-	read_res_callback_data *res_data;
-	GSimpleAsyncResult *simple;
+	klass = PURPLE_LOG_GET_CLASS(log);
 
-	simple = G_SIMPLE_ASYNC_RESULT(res);
-	res_data = g_simple_async_result_get_op_res_gpointer(simple);
-
-	if (flags != NULL)
-		*flags = res_data->flags;
-
-	return res_data->text;
+	if (klass->read_async == NULL)
+		_notify_not_supported(G_OBJECT(log), cb, userdata);
+	else
+		klass->read_async(log, io_priority, cancellable, cb, userdata);
 }
-#endif
 
 gchar *
 purple_log_read_finish(PurpleLog *log, GAsyncResult *res,
@@ -771,7 +800,12 @@ purple_log_real_size_finish(PurpleLog *l
 static gssize
 purple_log_real_size_finish(PurpleLog *log, GAsyncResult *res, GError **error)
 {
-	return g_simple_async_result_get_op_res_gssize(G_SIMPLE_ASYNC_RESULT(res));
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail(G_IS_SIMPLE_ASYNC_RESULT(res), -1);
+
+	simple = G_SIMPLE_ASYNC_RESULT(res);
+	return g_simple_async_result_get_op_res_gssize(simple);
 }
 
 gssize
@@ -793,21 +827,25 @@ purple_log_real_list_logs_finish(GAsyncR
 static GList *
 purple_log_real_list_logs_finish(GAsyncResult *res, GError **error)
 {
-	GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
-	GList *list = g_simple_async_result_get_op_res_gpointer(simple);
+	GSimpleAsyncResult *simple;
+	GList *list;
 
+	g_return_val_if_fail(G_IS_SIMPLE_ASYNC_RESULT(res), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT(res);
+	list = g_simple_async_result_get_op_res_gpointer(simple);
 	return purple_log_list_copy(list);
 }
 
 GList *
-purple_logs_list_logs(PurpleLogChatType chat_type, const gchar *name,
+purple_logs_list(PurpleLogChatType chat_type, const gchar *name,
 	PurpleAccount *account, GCancellable *cancellable, GError **error)
 {
 	GArray *array;
 	GList *logs = NULL;
 	guint i;
 
-	g_return_val_if_fail(name != NULL, NULL);
+	g_return_val_if_fail(chat_type == PURPLE_LOG_SYSTEM || name != NULL, NULL);
 	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
 
 	array = purple_log_logger_get_all();
@@ -836,21 +874,21 @@ void
 }
 
 void
-purple_logs_list_logs_async(PurpleLogChatType chat_type, const gchar *name,
+purple_logs_list_async(PurpleLogChatType chat_type, const gchar *name,
 	PurpleAccount *account, gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata)
 {
-	_list_logs_callback_data *callback_data;
+	list_callback_data *callback_data;
 	GArray *array;
 	guint i;
 	gboolean any;
 
-	g_return_if_fail(name != NULL);
-	g_return_if_fail(account != NULL); /* XXX: PURPLE_IS_ACCOUNT(account) */
+	g_return_if_fail(chat_type == PURPLE_LOG_SYSTEM || name != NULL);
+	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
 
 	array = purple_log_logger_get_all();
 
-	callback_data = g_new0(_list_logs_callback_data, 1);
+	callback_data = g_new0(list_callback_data, 1);
 	callback_data->cb = cb;
 	callback_data->userdata = userdata;
 	callback_data->counter = array->len;
@@ -875,9 +913,10 @@ GList *
 }
 
 GList *
-purple_logs_list_logs_finish(GAsyncResult *res, GError **error)
+purple_logs_list_finish(GAsyncResult *res, GError **error)
 {
 	GSimpleAsyncResult *simple;
+	GList *list;
 
 	g_return_val_if_fail(G_IS_SIMPLE_ASYNC_RESULT(res), NULL);
 
@@ -886,105 +925,9 @@ purple_logs_list_logs_finish(GAsyncResul
 	if (g_simple_async_result_propagate_error(simple, error))
 		return NULL;
 
-	return purple_log_list_copy(g_simple_async_result_get_op_res_gpointer(simple));
-}
-
-GList *
-purple_logs_list_system_logs(PurpleAccount *account, GCancellable *cancellable,
-	GError **error)
-{
-	GArray *array;
-	GList *logs = NULL;
-	guint i;
-
-	g_return_val_if_fail(account != NULL, NULL); /* XXX: PURPLE_IS_ACCOUNT(account) */
-
-	array = purple_log_logger_get_all();
-
-	for (i = 0; i < array->len; i++) {
-		PurpleLogClass *klass = g_array_index(array, PurpleLogClass *, i);
-		GList *list;
-
-		if (klass->list_syslog_fn == NULL)
-			continue;
-
-		list = klass->list_syslog_fn(account, cancellable, error);
-
-		if (list == NULL) {
-			purple_log_list_free(logs);
-			g_object_unref(log);
-
-			return NULL;
-		}
-
-		logs = g_list_concat(list, logs);
-		g_object_unref(log);
-	}
-
-	return g_list_sort(logs, purple_log_compare);
-}
-
-void
-purple_logs_list_system_logs_async(PurpleAccount *account, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
-{
-	_list_logs_callback_data *callback_data;
-	GArray *array;
-	guint i;
-	gboolean any;
-
-	g_return_if_fail(account != NULL); /* XXX: PURPLE_IS_ACCOUNT(account) */
-
-	array = purple_log_logger_get_all();
-
-	callback_data = g_new0(_list_logs_callback_data, 1);
-	callback_data->userdata = userdata;
-	callback_data->cb = cb;
-	callback_data->counter = array->len;
-
-	any = FALSE;
-
-	for (i = 0; i < array->len; i++) {
-		PurpleLogClass *klass = g_array_index(array, PurpleLogClass *, i);
-
-		if (klass->list_syslog_async == NULL) {
-			callback_data->counter--;
-			continue;
-		}
-
-		any = TRUE;
-		klass->list_syslog_async(account, io_priority, cancellable,
-			log_system_list_cb, callback_data);
-	}
-
-	if (!any)
-		log_system_list_cb(NULL, NULL, callback_data);
-}
-
-static GList *
-purple_log_real_list_syslog_finish(GAsyncResult *res, GError **error)
-{
-	GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
-	GList *list = g_simple_async_result_get_op_res_gpointer(simple);
-
+	list = g_simple_async_result_get_op_res_gpointer(simple);
 	return purple_log_list_copy(list);
 }
-
-GList *
-purple_logs_list_system_logs_finish(GAsyncResult *res, GError **error)
-{
-	GSimpleAsyncResult *simple;
-
-	g_return_val_if_fail(G_IS_SIMPLE_ASYNC_RESULT(res), NULL);
-
-	simple = G_SIMPLE_ASYNC_RESULT(res);
-
-	if (g_simple_async_result_propagate_error(simple, error))
-		return NULL;
-
-	return purple_log_list_copy(g_simple_async_result_get_op_res_gpointer(simple));
-}
-
 static guint
 _purple_logsize_user_hash(_purple_logsize_user *lu)
 {
@@ -1008,11 +951,16 @@ purple_log_real_total_size_finish(GAsync
 static gssize
 purple_log_real_total_size_finish(GAsyncResult *res, GError **error)
 {
-	return g_simple_async_result_get_op_res_gssize(G_SIMPLE_ASYNC_RESULT(res));
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail(G_IS_SIMPLE_ASYNC_RESULT(res), -1);
+
+	simple = G_SIMPLE_ASYNC_RESULT(res);
+	return g_simple_async_result_get_op_res_gssize(simple);
 }
 
 gssize
-purple_logs_get_total_size(PurpleLogChatType chat_type, const gchar *name,
+purple_logs_total_size(PurpleLogChatType chat_type, const gchar *name,
 	PurpleAccount *account, GCancellable *cancellable, GError **error)
 {
 	_purple_logsize_user *lu;
@@ -1021,7 +969,7 @@ purple_logs_get_total_size(PurpleLogChat
 	gssize total = 0;
 
 	g_return_val_if_fail(name != NULL, -1);
-	g_return_val_if_fail(account != NULL, -1); /* XXX: PURPLE_IS_ACCOUNT(account) */
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), -1);
 
 	lu = g_new(_purple_logsize_user, 1);
 	lu->name = g_strdup(purple_normalize(account, name));
@@ -1062,7 +1010,7 @@ purple_logs_get_total_size(PurpleLogChat
 
 /**
  * XXX: Would it make sense to allow the caller to pass in a list of logs, if
- * it just got them from purple_logs_list_logs_async()?  Pidgin asks
+ * it just got them from purple_logs_list_async()?  Pidgin asks
  * for the total size, which means that for some loggers, we end up
  * calling list *again* needlessly (to loop over them and size them).
  * If we had a list of logs, we could loop over them and find those
@@ -1075,7 +1023,7 @@ void
  * }
  */
 void
-purple_logs_get_total_size_async(PurpleLogChatType chat_type, const gchar *name,
+purple_logs_total_size_async(PurpleLogChatType chat_type, const gchar *name,
 	PurpleAccount *account, gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata)
 {
@@ -1089,7 +1037,7 @@ purple_logs_get_total_size_async(PurpleL
 	guint i;
 
 	g_return_if_fail(name != NULL);
-	g_return_if_fail(account != NULL); /* XXX: PURPLE_IS_ACCOUNT(account) */
+	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
 
 	lu = g_new(_purple_logsize_user, 1);
 	lu->name = g_strdup(purple_normalize(account, name));
@@ -1111,7 +1059,7 @@ purple_logs_get_total_size_async(PurpleL
 		callback_data->counter = 1;
 
 		simple = g_simple_async_result_new(NULL, log_total_size_cb,
-			callback_data, purple_logs_get_total_size_async);
+			callback_data, purple_logs_total_size_async);
 
 		g_simple_async_result_set_op_res_gssize(simple, size);
 		g_simple_async_result_complete_in_idle(simple);
@@ -1148,7 +1096,7 @@ gssize
 }
 
 gssize
-purple_logs_get_total_size_finish(GAsyncResult *res, GError **error)
+purple_logs_total_size_finish(GAsyncResult *res, GError **error)
 {
 	GSimpleAsyncResult *simple;
 
@@ -1162,6 +1110,71 @@ purple_logs_get_total_size_finish(GAsync
 	return g_simple_async_result_get_op_res_gssize(simple);
 }
 
+void
+purple_logs_search_async(PurpleLogChatType type, const gchar *name,
+	PurpleAccount *account, const gchar *search_term, gint io_priority,
+	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	search_callback_data *data;
+	GArray *array;
+	guint i;
+
+	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+	array = purple_log_logger_get_all();
+
+	data = g_new0(search_callback_data, 1);
+	data->io_priority = io_priority;
+	data->cancel = cancellable != NULL ? g_object_ref(cancellable) : NULL;
+	data->userdata = userdata;
+	data->cb = cb;
+	data->searches = 0;
+	data->lists = 0;
+	data->reads = 0;
+	data->logs = NULL;
+	data->logs_with_string = NULL;
+	data->search_term = g_strdup(search_term);
+
+	for (i = 0; i < array->len; i++) {
+		PurpleLogClass *klass = g_array_index(array, PurpleLogClass *, i);
+
+		if (klass->search_async == NULL) {
+			/* Search using the list/read commands */
+			if (klass->list_async == NULL || klass->read_async == NULL)
+				continue;
+
+			data->lists++;
+			klass->list_async(type, name, account, io_priority, cancellable,
+				log_search_list_cb, data);
+		} else {
+			klass->search_async(type, name, account, search_term, io_priority,
+				cancellable, log_search_cb, data);
+		}
+
+		data->searches++;
+	}
+
+	if (data->searches == 0)
+		log_search_cb(NULL, NULL, data);
+}
+
+GList *
+purple_logs_search_finish(GAsyncResult *res, GError **error)
+{
+	GSimpleAsyncResult *simple;
+	GList *list;
+
+	g_return_val_if_fail(G_IS_SIMPLE_ASYNC_RESULT(res), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT(res);
+
+	if (g_simple_async_result_propagate_error(simple, error))
+		return NULL;
+
+	list = g_simple_async_result_get_op_res_gpointer(simple);
+	return purple_log_list_copy(list);
+}
+
 static gboolean
 steal_log_sets(gpointer key, gpointer value, gpointer sets)
 {
@@ -1172,7 +1185,14 @@ purple_log_real_get_log_sets_finish(GAsy
 static GHashTable *
 purple_log_real_get_log_sets_finish(GAsyncResult *res, GError **error)
 {
-	return g_hash_table_ref(g_simple_async_result_get_op_res_gpointer(G_SIMPLE_ASYNC_RESULT(res)));
+	GSimpleAsyncResult *simple;
+	GHashTable *table;
+
+	g_return_val_if_fail(G_IS_SIMPLE_ASYNC_RESULT(res), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT(res);
+	table = g_simple_async_result_get_op_res_gpointer(simple);
+	return g_hash_table_ref(table);
 }
 
 GHashTable *
@@ -1281,7 +1301,7 @@ purple_log_get_activity_score(PurpleLogC
 	gssize score;
 
 	g_return_val_if_fail(name != NULL, -1);
-	g_return_val_if_fail(account != NULL, -1); /* XXX: PURPLE_IS_ACCOUNT(account) */
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), -1);
 
 	time(&now);
 
@@ -1304,7 +1324,7 @@ purple_log_get_activity_score(PurpleLogC
 		for (i = 0; i < array->len; i++) {
 			GList *logs;
 
-			logs = purple_logs_list_logs(chat_type, name, account, cancellable,
+			logs = purple_logs_list(chat_type, name, account, cancellable,
 				error);
 
 			if (logs == NULL && error != NULL)
@@ -1350,12 +1370,17 @@ purple_log_get_activity_score_finish(GAs
 gssize
 purple_log_get_activity_score_finish(GAsyncResult *res, GError **error)
 {
-	GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
+	GSimpleAsyncResult *simple;
 
+	g_return_val_if_fail(G_IS_SIMPLE_ASYNC_RESULT(res), -1);
+
+	simple= G_SIMPLE_ASYNC_RESULT(res);
+
 	if (g_simple_async_result_propagate_error(simple, error))
-		return FALSE;
+		return -1;
 
-	g_return_val_if_fail(g_simple_async_result_get_source_tag(simple) != purple_log_get_activity_score_async, -1);
+	g_return_val_if_fail(g_simple_async_result_get_source_tag(simple) !=
+		purple_log_get_activity_score_async, -1);
 
 	return g_simple_async_result_get_op_res_gssize(simple);
 }
@@ -1411,11 +1436,11 @@ purple_log_real_remove_finish(PurpleLog 
 static gboolean
 purple_log_real_remove_finish(PurpleLog *log, GAsyncResult *res, GError **error)
 {
-	GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
+	GSimpleAsyncResult *simple;
 
-	if (g_simple_async_result_propagate_error(simple, error))
-		return FALSE;
+	g_return_val_if_fail(G_IS_SIMPLE_ASYNC_RESULT(res), FALSE);
 
+	simple = G_SIMPLE_ASYNC_RESULT(res);
 	return g_simple_async_result_get_op_res_gboolean(simple);
 }
 
@@ -1425,6 +1450,13 @@ purple_log_remove_finish(PurpleLog *log,
 	g_return_val_if_fail(PURPLE_IS_LOG(log), FALSE);
 	g_return_val_if_fail(G_IS_ASYNC_RESULT(res), FALSE);
 
+	if (G_IS_SIMPLE_ASYNC_RESULT(res)) {
+		GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
+
+		if (g_simple_async_result_propagate_error(simple, error))
+			return FALSE;
+	}
+
 	return PURPLE_LOG_GET_CLASS(log)->remove_finish(log, res, error);
 }
 
@@ -1687,7 +1719,7 @@ purple_log_system_init(void)
 
 	purple_html_log_system_init();
 	purple_txt_log_system_init();
-//	purple_old_log_system_init();
+	// purple_old_log_system_init();
 
 	purple_signal_register(handle, "log-timestamp",
 #if SIZEOF_TIME_T == 4
@@ -1736,7 +1768,7 @@ purple_log_system_uninit(void)
 
 	purple_html_log_system_uninit();
 	purple_txt_log_system_uninit();
-	purple_old_log_system_uninit();
+	// purple_old_log_system_uninit();
 
 	g_array_free(loggers, TRUE);
 	loggers = NULL;
@@ -2010,13 +2042,13 @@ log_list_cb(GObject *object, GAsyncResul
 static void
 log_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
-	_list_logs_callback_data *data = userdata;
+	list_callback_data *data = userdata;
 
 	if (res != NULL) {
 		GError *error = NULL;
 		GList *list;
 
-		list = purple_logs_list_logs_finish(res, &error);
+		list = purple_logs_list_finish(res, &error);
 
 		if (list == NULL) {
 			/* It's hard to pass all the errors up, so just log them :( */
@@ -2027,7 +2059,7 @@ log_list_cb(GObject *object, GAsyncResul
 			data->counter--;
 		} else {
 			SIMPLE_ASYNC_RESULT_NEW_GOOD(NULL, data->cb, data->userdata,
-				purple_logs_list_logs_async,
+				purple_logs_list_async,
 				g_simple_async_result_set_op_res_gpointer,
 				list,
 				(GDestroyNotify) purple_log_list_free);
@@ -2040,7 +2072,7 @@ log_list_cb(GObject *object, GAsyncResul
 
 	if (data->counter < 1) {
 		SIMPLE_ASYNC_RESULT_NEW_GOOD(NULL, data->cb, data->userdata,
-			purple_logs_list_logs_async,
+			purple_logs_list_async,
 			g_simple_async_result_set_op_res_gpointer,
 			NULL,
 			NULL);
@@ -2050,80 +2082,157 @@ static void
 }
 
 static void
-log_system_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+log_total_size_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
-	_list_logs_callback_data *data = userdata;
+	_purple_log_total_size_callback_data *data = userdata;
+	GError *error = NULL;
+	gssize size;
 
+	if (res != NULL)
+		size = purple_logs_total_size_finish(res, &error);
+	else
+		size = 0;
+
+	/* It's hard to pass all the errors up, so just log them :( */
+	if (size < 0 && error != NULL)
+		purple_debug_error("log", "Error getting total size for logs: %s\n",
+			error->message);
+	else
+		data->total += size;
+
+	g_clear_error(&error);
+	data->counter--;
+
+	if (data->counter < 1) {
+		SIMPLE_ASYNC_RESULT_NEW_GOOD(NULL, data->cb, data->userdata,
+			purple_logs_total_size_async,
+			g_simple_async_result_set_op_res_gssize,
+			data->total);
+
+		g_free(data);
+	}
+}
+
+static void
+log_search_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+	search_callback_data *data = userdata;
+
+	data->searches--;
+
 	if (res != NULL) {
 		GError *error = NULL;
-		GList *list;
+		GList *logs;
 
-		list = purple_logs_list_system_logs_finish(res, &error);
+		logs = purple_logs_search_finish(res, &error);
 
-		if (list == NULL) {
-			/* It's hard to pass all the errors up, so just log them :( */
+		if (logs == NULL) {
 			if (error != NULL)
-				purple_debug_error("log", "Error listing logs: %s\n",
+				purple_debug_error("log", "Error searchin logs: %s\n",
 					error->message);
+		} else
+			data->logs_with_string = g_list_concat(logs,
+				data->logs_with_string);
 
-			data->counter--;
-		} else {
-			SIMPLE_ASYNC_RESULT_NEW_GOOD(NULL, data->cb, data->userdata,
-				purple_logs_list_system_logs_async,
-				g_simple_async_result_set_op_res_gpointer,
-				list,
-				(GDestroyNotify) purple_log_list_free);
-		}
-
 		g_clear_error(&error);
 	}
 
-	/* If res is NULL, data->counter should already be set to 0 */
-
-	if (data->counter < 1) {
+	if (data->searches < 1) {
 		SIMPLE_ASYNC_RESULT_NEW_GOOD(NULL, data->cb, data->userdata,
-			purple_logs_list_system_logs_async,
+			purple_logs_search_async,
 			g_simple_async_result_set_op_res_gpointer,
-			NULL,
-			NULL);
+			data->logs_with_string,
+			(GDestroyNotify) purple_log_list_free);
 
-		g_free(data);
+		data->logs_with_string = NULL;
+		search_callback_data_free(data);
 	}
 }
 
 static void
-log_total_size_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+log_search_read_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
-	_purple_log_total_size_callback_data *data = userdata;
+	PurpleLog *log = PURPLE_LOG(object);
 	GError *error = NULL;
-	gssize size;
+	gchar *text;
+	search_callback_data *data = userdata;
 
-	if (res != NULL)
-		size = purple_logs_get_total_size_finish(res, &error);
-	else
-		size = 0;
+	data->reads--;
+	text = purple_log_read_finish(log, res, NULL, &error);
 
-	/* It's hard to pass all the errors up, so just log them :( */
-	if (size < 0 && error != NULL)
-		purple_debug_error("log", "Error getting total size for logs: %s\n",
+	if (text == NULL) {
+		purple_debug_error("log", "Error reading log: %s\n",
 			error->message);
-	else
-		data->total += size;
+	} else if (*text && purple_strcasestr(text, data->search_term))
+		data->logs_with_string = g_list_prepend(g_object_ref(log),
+			data->logs_with_string);
 
-	g_clear_error(&error);
-	data->counter--;
+	g_free(text);
 
-	if (data->counter < 1) {
-		SIMPLE_ASYNC_RESULT_NEW_GOOD(NULL, data->cb, data->userdata,
-			purple_logs_list_system_logs_async,
-			g_simple_async_result_set_op_res_gssize,
-			data->total);
+	if (data->reads == 0) {
+		if (data->logs != NULL) {
+			GList *logs;
+			guint i;
 
-		g_free(data);
+			for (i = 0, logs = data->logs;
+				i < FILES_PER_SEARCH && logs != NULL;
+				i++, logs = g_list_delete_link(logs, logs))
+			{
+				PurpleLog *log = logs->data;
+
+				data->reads++;
+				purple_log_read_async(log, data->io_priority, data->cancel,
+					log_search_read_cb, data);
+
+				g_object_unref(log);
+			}
+		} else
+			log_search_cb(NULL, NULL, data);
 	}
 }
 
 static void
+log_search_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+	GError *error = NULL;
+	GList *logs;
+	search_callback_data *data = userdata;
+
+	logs = purple_logs_list_finish(res, &error);
+
+	if (logs == NULL) {
+		if (error != NULL)
+			purple_debug_error("log", "Error listing logs: %s\n",
+				error->message);
+
+		data->lists--;
+
+		if (data->lists) {
+			guint i;
+
+			if (data->logs == NULL) {
+				log_search_cb(NULL, NULL, data);
+				return;
+			}
+
+			for (i = 0, logs = data->logs;
+				i < FILES_PER_SEARCH && logs != NULL;
+				i++, logs = g_list_delete_link(logs, logs))
+			{
+				PurpleLog *log = logs->data;
+
+				data->reads++;
+				purple_log_read_async(log, data->io_priority, data->cancel,
+					log_search_read_cb, data);
+
+				g_object_unref(log);
+			}
+		}
+	} else
+		data->logs = g_list_concat(logs, data->logs);
+}
+
+static void
 log_hash_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
 	_purple_log_sets_callback_data *data = userdata;
============================================================
--- libpurple/log.h	6d97fe48d8538e5e81b2022dfbd265cdd61e8d2b
+++ libpurple/log.h	862b2a3dba889c19f1dacab9238533b7d4580b42
@@ -120,13 +120,8 @@ struct _PurpleLogClass {
 	/** Asynchronously writes a message to a log */
 	void (* write_async) (PurpleLog *log, PurpleMessageFlags type,
 		const gchar *from, time_t time, const gchar *message,
-		gint io_priority, GCancellable *cancellable,
-		GAsyncReadyCallback cb, gpointer userdata);
+		gint io_priority, GCancellable *cancellable);
 
-	/** Finishes writing a message to a log (Unused currently) */
-	/*gssize (* write_finish) (PurpleLog *log, GCancellable *cancellable,
-		GError **error);*/
-
 	/** Reads all messages from a log */
 	gchar * (* read_fn) (PurpleLog *log, PurpleLogReadFlags *flags,
 		GCancellable *cancellable, GError **error);
@@ -171,23 +166,6 @@ struct _PurpleLogClass {
 	GList * (* list_finish) (GAsyncResult *res, GError **error);
 
 	/**
-	 * Lists all available system logs
-	 */
-	GList * (* list_syslog_fn) (PurpleAccount *account,
-		GCancellable *cancellable, GError **error);
-
-	/**
-	 * Asynchronously lists all available system logs
-	 */
-	void (* list_syslog_async) (PurpleAccount *account, gint io_priority, 
-		GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
-
-	/**
-	 * Finishes an asynchronous list system logs operation
-	 */
-	GList * (* list_syslog_finish) (GAsyncResult *res, GError **error);
-
-	/**
 	 * Gets the total size for all available logs
 	 *
 	 * If this is undefined a default implementation is used
@@ -208,6 +186,18 @@ struct _PurpleLogClass {
 	gssize (* total_size_finish) (GAsyncResult *res, GError **error);
 
 	/**
+	 * Asyncronously searchs all the logs for a given user
+	 */
+	void (* search_async) (PurpleLogChatType type, const gchar *name,
+		PurpleAccount *account, const gchar *search_term, gint io_priority,
+		GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+
+	/**
+	 * Finishes an asynchronous search operation
+	 */
+	GList * (* search_finish) (GAsyncResult *res, GError **error);
+
+	/**
 	 * Adds log sets to a hash table. By passing the data in the
 	 * log sets to list, the caller can get every available log from
 	 * the logger. Loggers using purple_log_common_writer()
@@ -367,7 +357,7 @@ G_CONST_RETURN struct tm *purple_log_get
  * Writes to a log file. Assumes you have checked preferences already.
  *
  * @param log          The log to write to
- * @param type         The type of message being logged
+ * @param flags        The flags for writing the message
  * @param from         Whom this message is coming from, or @c NULL for
  *                     system messages
  * @param time         A timestamp in UNIX time
@@ -377,7 +367,7 @@ G_CONST_RETURN struct tm *purple_log_get
  *
  * @return             Number of bytes written, -1 on error.
  */
-gssize purple_log_write(PurpleLog *log, PurpleMessageFlags type,
+gssize purple_log_write(PurpleLog *log, PurpleMessageFlags flags,
 	const gchar *from, time_t time, const gchar *message,
 	GCancellable *cancellable, GError **error);
 
@@ -388,7 +378,7 @@ gssize purple_log_write(PurpleLog *log, 
  * the API to be extended further in the future.
  *
  * @param log          The log to write to
- * @param type         The type of message being logged
+ * @param flags        The flags for writing the message
  * @param from         Whom this message is coming from, or @c %NULL for system messages
  * @param time         A timestamp in UNIX time
  * @param message      The message to log (will be removed in function)
@@ -400,9 +390,9 @@ gssize purple_log_write(PurpleLog *log, 
  *
  * @since 3.0.0
  */
-void purple_log_write_async(PurpleLog *log, PurpleMessageFlags type,
+void purple_log_write_async(PurpleLog *log, PurpleMessageFlags flags,
 	const gchar *from, time_t time, const gchar *message, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+	GCancellable *cancellable);
 
 /**
  * Reads from a log
@@ -505,7 +495,7 @@ gssize purple_log_get_size_finish(Purple
  *
  * @since 3.0.0
  */
-GList *purple_logs_list_logs(PurpleLogChatType chat_type, const gchar *name,
+GList *purple_logs_list(PurpleLogChatType chat_type, const gchar *name,
 	PurpleAccount *account, GCancellable *cancellable, GError **error);
 
 /**
@@ -514,7 +504,7 @@ GList *purple_logs_list_logs(PurpleLogCh
  *
  * Will continously call cb with more logs until all have been listed, allowing
  * for continous display of the data. When done, the cb receive NULL from
- * purple_logs_list_logs_finish() and the error pointer will remain untouched.
+ * purple_logs_list_finish() and the error pointer will remain untouched.
  *
  * @param type         The type of the log
  * @param name         The name of the log
@@ -529,7 +519,7 @@ GList *purple_logs_list_logs(PurpleLogCh
  *
  * @since 3.0.0
  */
-void purple_logs_list_logs_async(PurpleLogChatType type, const gchar *name,
+void purple_logs_list_async(PurpleLogChatType type, const gchar *name,
 	PurpleAccount *account, gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata);
 
@@ -546,60 +536,9 @@ void purple_logs_list_logs_async(PurpleL
  *
  * @since 3.0.0
  */
-GList *purple_logs_list_logs_finish(GAsyncResult *res, GError **error);
+GList *purple_logs_list_finish(GAsyncResult *res, GError **error);
 
 /**
- * Gets a list of all available system logs for all added loggers.
- *
- * @param account      The account
- * @param cancellable  (allow-none): GCancellable object
- * @param error        (out) (allow-none): a GError location to store the error
- *
- * @return             pointer to log list or %NULL on error
- *
- * @since 3.0.0
- */
-GList *purple_logs_list_system_logs(PurpleAccount *account,
-	GCancellable *cancellable, GError **error);
-
-/**
- * Asynchronously gets a list of all available system logs for all added
- * loggers.
- *
- * Will continously call cb with more logs until all have been listed, allowing
- * for continous display of the data. When done, the cb receive NULL from
- * purple_logs_list_logs_finish() and the error pointer will remain untouched.
- *
- * @param account      The account
- * @param io_priority  The io priority of the request
- * @param cancellable  (allow-none): GCancellable object
- * @param cb           (allow-none): A GAsyncReadyCallback to call when the
-                       request is satisfied
- * @param userdata     (allow-none): The data to pass to callback function
- *
- * @since 3.0.0
- */
-void purple_logs_list_system_logs_async(PurpleAccount *account,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb,
-	gpointer userdata);
-
-/**
- * Finishes asynchronously getting all available system logs for all added
- * loggers.
- *
- * Note: Make sure to examine that error is not NULL as this function will also
- * return NULL when all logs have been listed.
- *
- * @param res          A GAsyncResult
- * @param error        (out) (allow-none): a GError location to store the error
- *
- * @return             pointer to log list or %NULL on error
- *
- * @since 3.0.0
- */
-GList *purple_logs_list_system_logs_finish(GAsyncResult *res, GError **error);
-
-/**
  * Gets the total size of all stored conversations.
  *
  * @param type         The type of the log
@@ -612,7 +551,7 @@ GList *purple_logs_list_system_logs_fini
  *
  * @since 3.0.0
  */
-gssize purple_logs_get_total_size(PurpleLogChatType type, const gchar *name,
+gssize purple_logs_total_size(PurpleLogChatType type, const gchar *name,
 	PurpleAccount *account, GCancellable *cancellable, GError **error);
 
 /**
@@ -629,7 +568,7 @@ gssize purple_logs_get_total_size(Purple
  *
  * @since 3.0.0
  */
-void purple_logs_get_total_size_async(PurpleLogChatType type,
+void purple_logs_total_size_async(PurpleLogChatType type,
 	const gchar *name, PurpleAccount *account,  gint io_priority,
 	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
@@ -643,8 +582,14 @@ void purple_logs_get_total_size_async(Pu
  *
  * @since 3.0.0
  */
-gssize purple_logs_get_total_size_finish(GAsyncResult *res, GError **error);
+gssize purple_logs_total_size_finish(GAsyncResult *res, GError **error);
 
+void purple_logs_search_async(PurpleLogChatType type, const gchar *name,
+	PurpleAccount *account, const gchar *search_term, gint io_priority,
+	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+
+GList *purple_logs_search_finish(GAsyncResult *res, GError **error);
+
 /**
  * Gets a hash table for the combined log sets of all accounts.
  *
============================================================
--- pidgin/gtklog.c	2072929b53dec73ea5fcc213e201c4c06f446733
+++ pidgin/gtklog.c	ecef243cdae5e1956614fbfba5a9f63bd3f7cc2e
@@ -41,6 +41,8 @@
 
 
 /* Helpful macros */
+#define PURPLE_IS_ACCOUNT(account) (account != NULL)
+
 #define G_OBJECT_CHECK_UNREF(obj) \
 	{ \
 		if ((obj) != NULL) { \
@@ -124,6 +126,10 @@ struct _PidginLogViewerPrivate {
 #if GTK_CHECK_VERSION(2, 20, 0)
 	GtkWidget          *spinner;       /**< A spinner to indicate a read is in progress */
 #endif
+
+	PurpleLogChatType  chat_type;
+	GList              *names;
+	GList              *accounts;
 };
 
 typedef struct {
@@ -159,14 +165,11 @@ typedef struct {
 
 
 /* Prototypes */
-static void pidgin_log_data_free(_pidgin_log_data *);
 static void pidgin_window_destroy_cb(_pidgin_log_data *);
 static guint log_viewer_hash(gconstpointer);
 static gboolean log_viewer_equal(gconstpointer, gconstpointer);
 static void select_first_log(PidginLogViewer *);
 static const gchar *log_get_date(PurpleLog *);
-static void pidgin_log_search_done_cb(_pidgin_log_data *);
-static void pidgin_log_search_cb(GObject *, GAsyncResult *, gpointer);
 static void search_cb(GtkWidget *, PidginLogViewer *);
 static void destroy_cb(PidginLogViewer *, gint, log_viewer_hash_t *);
 static void log_row_activated_cb(GtkTreeView *, GtkTreePath *,
@@ -319,53 +322,46 @@ pidgin_log_search_cb(GObject *object, GA
 static void
 pidgin_log_search_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
-	_pidgin_log_data *pidgin_log_data = userdata;
-	PurpleLog *log = PURPLE_LOG(object);
+	_pidgin_log_data *data = userdata;
 	GError *error = NULL;
-	gchar *text;
+	GList *logs;
 
-	text = purple_log_read_finish(log, res, NULL, &error);
-	pidgin_log_data->count--;
+	logs = purple_logs_search_finish(res, &error);
 
-	if (text == NULL) {
-		if (error->code != G_IO_ERROR_CANCELLED)
-			purple_debug_error("gtklog",
-				"Error reading file during search: %s\n", error->message);
-	} else if (pidgin_log_data->is_window_open) {
-		PidginLogViewer *lv = pidgin_log_data->log_viewer;
-		GtkTreeStore *treestore = pidgin_log_viewer_get_tree_store(lv);
-		GtkTreeIter iter;
+	if (logs == NULL) {
+		if (error != NULL) {
+			if (error->code != G_IO_ERROR_CANCELLED)
+				purple_debug_error("gtklog",
+					"Error searching logs: %s\n", error->message);
+		}
 
-		if (*text && purple_strcasestr(text, pidgin_log_data->string)) {
-			gtk_tree_store_append(treestore, &iter, NULL);
-			gtk_tree_store_set(treestore, &iter, 0, log_get_date(log),
-				1, log, -1);
-		}
-	}
+		data->count--;
+	} else if (data->is_window_open)
+		pidgin_log_viewer_add_logs(data->log_viewer, logs);
 
 	g_clear_error(&error);
 
-	if (pidgin_log_data->is_window_open) {
-		const gchar *cur_search =
-			pidgin_log_viewer_get_search_string(pidgin_log_data->log_viewer);
+	// if (data->is_window_open) {
+		// const gchar *cur_search =
+			// pidgin_log_viewer_get_search_string(data->log_viewer);
 
-		if (purple_strequal(pidgin_log_data->string, cur_search))
-			pidgin_log_viewer_update_search_bar(pidgin_log_data);
-	}
+		// if (purple_strequal(data->string, cur_search))
+			// pidgin_log_viewer_update_search_bar(data);
+	// }
 
-	if (pidgin_log_data->count < 1)
-		pidgin_log_search_done_cb(pidgin_log_data);
+	if (data->count < 1)
+		pidgin_log_search_done_cb(data);
 }
 
 static void
 search_cb(GtkWidget *button, PidginLogViewer *lv)
 {
-	_pidgin_log_data *pidgin_log_data;
+	_pidgin_log_data *data;
 	PidginLogViewerPrivate *priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
 	WebKitWebView *wv = WEBKIT_WEB_VIEW(pidgin_log_viewer_get_text_area(lv));
 	GtkWidget *bar;
 	GCancellable *cancel;
-	GList *logs;
+	GList *logs, *name, *account;
 	const gchar *search_term, *old_search;
 	guint length;
 
@@ -403,13 +399,13 @@ search_cb(GtkWidget *button, PidginLogVi
 	webkit_web_view_open(wv, "about:blank"); /* clear the view */
 	pidgin_log_viewer_set_selected(lv, FALSE);
 
-	pidgin_log_data = g_new0(_pidgin_log_data, 1);
-	pidgin_log_data->is_window_open = TRUE;
-	pidgin_log_data->log_viewer = lv;
-	pidgin_log_data->string = g_strdup(search_term);
-	pidgin_log_data->count = pidgin_log_data->total = length;
-	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv,
-		"destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
+	data = g_new0(_pidgin_log_data, 1);
+	data->is_window_open = TRUE;
+	data->log_viewer = lv;
+	data->string = g_strdup(search_term);
+	data->count = data->total = length;
+	data->destroy_handler_id = g_signal_connect_swapped(lv,
+		"destroy", G_CALLBACK(pidgin_window_destroy_cb), data);
 
 	bar = pidgin_log_viewer_get_search_bar(lv);
 	gtk_widget_show(bar);
@@ -418,10 +414,22 @@ search_cb(GtkWidget *button, PidginLogVi
 	cancel = g_cancellable_new();
 	pidgin_log_viewer_set_search_cancel(lv, cancel);
 
-	for ( ; logs != NULL; logs = g_list_next(logs))
-		purple_log_read_async(logs->data, G_PRIORITY_DEFAULT_IDLE, cancel,
-			pidgin_log_search_cb, pidgin_log_data);
+	for (account = priv->accounts, name = priv->names;
+		account != NULL;
+		account = g_list_next(account))
+	{
+		const gchar *name_str = NULL;
 
+		if (priv->chat_type != PURPLE_LOG_SYSTEM) {
+			name_str = name->data;
+			name = g_list_next(name);
+		}
+
+		purple_logs_search_async(priv->chat_type, name_str, account->data,
+			search_term, G_PRIORITY_DEFAULT_IDLE, cancel, pidgin_log_search_cb,
+			data);
+	}
+
 	g_object_unref(cancel);
 }
 
@@ -461,9 +469,8 @@ destroy_cb(PidginLogViewer *lv, gint res
 
 		g_free(ht->buddyname);
 		g_free(ht);
-	} else {
+	} else
 		syslog_viewer = NULL;
-	}
 
 	gtk_widget_destroy(GTK_WIDGET(lv));
 }
@@ -758,12 +765,12 @@ pidgin_log_read_cb(GObject *object, GAsy
 static void
 pidgin_log_read_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
-	_pidgin_log_data *pidgin_log_data = userdata;
+	_pidgin_log_data *data = userdata;
 
-	if (pidgin_log_data->is_window_open) {
-		PurpleLog *log = pidgin_log_data->log;
+	if (data->is_window_open) {
+		PurpleLog *log = data->log;
 		PurpleLogReadFlags flags;
-		PidginLogViewer *lv = pidgin_log_data->log_viewer;
+		PidginLogViewer *lv = data->log_viewer;
 		WebKitWebView *wv = WEBKIT_WEB_VIEW(pidgin_log_viewer_get_text_area(lv));
 		GError *error = NULL;
 		gchar *text;
@@ -773,7 +780,7 @@ pidgin_log_read_cb(GObject *object, GAsy
 
 		if (text == NULL) {
 			if (error->code == G_IO_ERROR_CANCELLED) {
-				pidgin_log_data_free(pidgin_log_data);
+				pidgin_log_data_free(data);
 				g_clear_error(&error);
 
 				return;
@@ -811,19 +818,19 @@ pidgin_log_read_cb(GObject *object, GAsy
 		 */
 		if (pidgin_log_viewer_get_search_string(lv) != NULL) {
 			webkit_web_view_unmark_text_matches(wv);
-			g_idle_add(search_find_cb, pidgin_log_data);
+			g_idle_add(search_find_cb, data);
 		} else
-			pidgin_log_data_free(pidgin_log_data);
+			pidgin_log_data_free(data);
 
 		g_clear_error(&error);
 	} else
-		pidgin_log_data_free(pidgin_log_data);
+		pidgin_log_data_free(data);
 }
 
 static void
 log_select_cb(GtkTreeSelection *sel, PidginLogViewer *lv)
 {
-	_pidgin_log_data *pidgin_log_data;
+	_pidgin_log_data *data;
 	PurpleLog *log = NULL;
 	PurpleLogChatType chat_type;
 	GtkTreeModel *model = GTK_TREE_MODEL(pidgin_log_viewer_get_tree_store(lv));
@@ -863,14 +870,14 @@ log_select_cb(GtkTreeSelection *sel, Pid
 	}
 
 	wv = WEBKIT_WEB_VIEW(pidgin_log_viewer_get_text_area(lv));
-	pidgin_log_data = g_new0(_pidgin_log_data, 1);
-	pidgin_log_data->is_window_open = TRUE;
-	pidgin_log_data->total = pidgin_log_data->count = 1;
+	data = g_new0(_pidgin_log_data, 1);
+	data->is_window_open = TRUE;
+	data->total = data->count = 1;
 	webkit_web_view_open(wv, "about:blank");
-	pidgin_log_data->log_viewer = lv;
-	pidgin_log_data->log = log;
-	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv,
-		"destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
+	data->log_viewer = lv;
+	data->log = log;
+	data->destroy_handler_id = g_signal_connect_swapped(lv,
+		"destroy", G_CALLBACK(pidgin_window_destroy_cb), data);
 
 	cancel = g_cancellable_new();
 	pidgin_log_viewer_set_read_cancel(lv, cancel);
@@ -884,7 +891,7 @@ log_select_cb(GtkTreeSelection *sel, Pid
 #endif
 
 	purple_log_read_async(log, G_PRIORITY_DEFAULT, cancel,
-		pidgin_log_read_cb, pidgin_log_data);
+		pidgin_log_read_cb, data);
 
 	g_object_unref(cancel);
 }
@@ -1133,7 +1140,7 @@ pidgin_log_size_cb(GObject *object, GAsy
 	GError *error = NULL;
 	gssize log_size;
 
-	log_size = purple_logs_get_total_size_finish(res, &error);
+	log_size = purple_logs_total_size_finish(res, &error);
 	pidgin_log_data->count--;
 
 	if (log_size < 0) {
@@ -1165,7 +1172,7 @@ pidgin_log_list_cb(GObject *object, GAsy
 	GError *error = NULL;
 	GList *list;
 
-	list = purple_logs_list_logs_finish(res, &error);
+	list = purple_logs_list_finish(res, &error);
 	if (list == NULL) {
 		pidgin_log_data->count--;
 
@@ -1191,7 +1198,7 @@ pidgin_log_system_list_cb(GObject *objec
 	GError *error = NULL;
 	GList *list;
 
-	list = purple_logs_list_system_logs_finish(res, &error);
+	list = purple_logs_list_finish(res, &error);
 
 	if (list == NULL) {
 		pidgin_log_data->count--;
@@ -1216,16 +1223,17 @@ pidgin_log_show(PurpleLogChatType chat_t
 	PurpleAccount *account)
 {
 	log_viewer_hash_t *ht;
-	_pidgin_log_data *pidgin_log_data;
+	_pidgin_log_data *data;
 	PidginLogViewer *lv;
+	PidginLogViewerPrivate *priv;
 	GdkPixbuf *prpl_icon;
 	GtkWidget *image;
 	GCancellable *cancel;
-	const gchar *name = buddyname;
+	const gchar *name;
 	gchar *title;
 
 	if (chat_type != PURPLE_LOG_IM) {
-		g_return_if_fail(account != NULL); /* XXX: PURPLE_IS_ACCOUNT */
+		g_return_if_fail(PURPLE_IS_ACCOUNT(account));
 		g_return_if_fail(buddyname != NULL);
 	}
 
@@ -1245,6 +1253,8 @@ pidgin_log_show(PurpleLogChatType chat_t
 		return;
 	}
 
+	name = buddyname;
+
 	if (chat_type == PURPLE_LOG_CHAT) {
 		PurpleChat *chat = purple_blist_find_chat(account, buddyname);
 
@@ -1261,31 +1271,36 @@ pidgin_log_show(PurpleLogChatType chat_t
 		title = g_strdup_printf(_("Conversations with %s"), name);
 	}
 
-	pidgin_log_data = g_new0(_pidgin_log_data, 1);
-	pidgin_log_data->is_window_open = TRUE;
-	pidgin_log_data->total = pidgin_log_data->count = 2;
+	data = g_new0(_pidgin_log_data, 1);
+	data->is_window_open = TRUE;
+	data->total = data->count = 2;
 
 	prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
 	image = gtk_image_new_from_pixbuf(prpl_icon);
-	lv = pidgin_log_data->log_viewer = pidgin_log_viewer_new(ht, title, image,
-		TRUE);
+	data->log_viewer = pidgin_log_viewer_new(ht, title, image, TRUE);
 	g_free(title);
-	gtk_widget_show(GTK_WIDGET(lv));
+	gtk_widget_show(GTK_WIDGET(data->log_viewer));
 
 	if (prpl_icon != NULL)
 		g_object_unref(prpl_icon);
 
-	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv,
-		"destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
+	data->destroy_handler_id = g_signal_connect_swapped(data->log_viewer,
+		"destroy", G_CALLBACK(pidgin_window_destroy_cb), data);
 
 	cancel = g_cancellable_new();
-	pidgin_log_viewer_set_list_cancel(lv, cancel);
+	pidgin_log_viewer_set_list_cancel(data->log_viewer, cancel);
 
-	purple_logs_list_logs_async(chat_type, buddyname, account,
-		G_PRIORITY_DEFAULT, cancel, pidgin_log_list_cb, pidgin_log_data);
-	purple_logs_get_total_size_async(chat_type, buddyname, account,
-		G_PRIORITY_DEFAULT, cancel, pidgin_log_size_cb, pidgin_log_data);
+	priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(data->log_viewer);
+	priv->chat_type = chat_type;
+	priv->names = g_list_prepend(priv->names, g_strdup(buddyname));
+	/* XXX: g_object_ref(account) */
+	priv->accounts = g_list_prepend(priv->accounts, account);
 
+	purple_logs_list_async(chat_type, buddyname, account,
+		G_PRIORITY_DEFAULT, cancel, pidgin_log_list_cb, data);
+	purple_logs_total_size_async(chat_type, buddyname, account,
+		G_PRIORITY_DEFAULT, cancel, pidgin_log_size_cb, data);
+
 	g_object_unref(cancel);
 }
 
@@ -1293,9 +1308,10 @@ pidgin_log_show_contact(PurpleContact *c
 pidgin_log_show_contact(PurpleContact *contact)
 {
 	log_viewer_hash_t *ht;
-	_pidgin_log_data *pidgin_log_data;
+	_pidgin_log_data *data;
 	PurpleBlistNode *child;
 	PidginLogViewer *lv;
+	PidginLogViewerPrivate *priv;
 	GCancellable *cancel;
 	GdkPixbuf *pixbuf;
 	GtkWidget *image;
@@ -1349,25 +1365,28 @@ pidgin_log_show_contact(PurpleContact *c
 			name = "";
 	}
 
-	pidgin_log_data = g_new0(_pidgin_log_data, 1);
-	pidgin_log_data->is_window_open = TRUE;
-	pidgin_log_data->count = pidgin_log_data->total = 0;
+	data = g_new0(_pidgin_log_data, 1);
+	data->is_window_open = TRUE;
+	data->count = data->total = 0;
 
 	title = g_strdup_printf(_("Conversations with %s"), name);
-	lv = pidgin_log_data->log_viewer =
-		pidgin_log_viewer_new(ht, title, image, TRUE);
+	lv = data->log_viewer = pidgin_log_viewer_new(ht, title, image, TRUE);
 
 	g_free(title);
 	gtk_widget_show(GTK_WIDGET(lv));
 
-	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv,
-		"destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
+	data->destroy_handler_id = g_signal_connect_swapped(lv,
+		"destroy", G_CALLBACK(pidgin_window_destroy_cb), data);
 
 	cancel = g_cancellable_new();
 	pidgin_log_viewer_set_list_cancel(lv, cancel);
 
+	priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(data->log_viewer);
+	priv->chat_type = PURPLE_LOG_IM;
+
 	for (child = purple_blist_node_get_first_child(PURPLE_BLIST_NODE(contact));
-		child != NULL; child = purple_blist_node_get_sibling_next(child))
+		child != NULL;
+		child = purple_blist_node_get_sibling_next(child))
 	{
 		const gchar *name;
 		PurpleAccount *account;
@@ -1376,28 +1395,33 @@ pidgin_log_show_contact(PurpleContact *c
 			continue;
 
 		found_account = TRUE;
-		pidgin_log_data->count = (pidgin_log_data->total += 2);
+		data->count = (data->total += 2);
 
 		name = purple_buddy_get_name(PURPLE_BUDDY(child));
 		account = purple_buddy_get_account(PURPLE_BUDDY(child));
 
-		purple_logs_list_logs_async(PURPLE_LOG_IM, name, account,
-			G_PRIORITY_DEFAULT, cancel, pidgin_log_list_cb, pidgin_log_data);
-		purple_logs_get_total_size_async(PURPLE_LOG_IM, name, account,
-			G_PRIORITY_DEFAULT, cancel, pidgin_log_size_cb, pidgin_log_data);
+		priv->names = g_list_prepend(priv->names, g_strdup(name));
+		 /* XXX: g_object_ref(account) */
+		priv->accounts = g_list_prepend(priv->accounts, account);
+
+		purple_logs_list_async(PURPLE_LOG_IM, name, account,
+			G_PRIORITY_DEFAULT, cancel, pidgin_log_list_cb, data);
+		purple_logs_total_size_async(PURPLE_LOG_IM, name, account,
+			G_PRIORITY_DEFAULT, cancel, pidgin_log_size_cb, data);
 	}
 
 	g_object_unref(cancel);
 
 	if (!found_account)
-		pidgin_log_done_cb(pidgin_log_data);
+		pidgin_log_done_cb(data);
 }
 
 void
 pidgin_syslog_show(void)
 {
-	_pidgin_log_data *pidgin_log_data;
+	_pidgin_log_data *data;
 	PidginLogViewer *lv;
+	PidginLogViewerPrivate *priv;
 	GCancellable *cancel;
 	GList *accounts;
 	gboolean found_account = FALSE;
@@ -1409,21 +1433,25 @@ pidgin_syslog_show(void)
 		return;
 	}
 
-	pidgin_log_data = g_new0(_pidgin_log_data, 1);
-	pidgin_log_data->is_window_open = TRUE;
-	pidgin_log_data->count = pidgin_log_data->total = 0;
+	data = g_new0(_pidgin_log_data, 1);
+	data->is_window_open = TRUE;
+	data->count = data->total = 0;
 
-	lv = pidgin_log_data->log_viewer = pidgin_log_viewer_new(NULL,
+	lv = data->log_viewer = pidgin_log_viewer_new(NULL,
 		_("System Log"), NULL, FALSE);
 	gtk_widget_show(GTK_WIDGET(lv));
 
-	pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv,
-		"destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
+	data->destroy_handler_id = g_signal_connect_swapped(lv,
+		"destroy", G_CALLBACK(pidgin_window_destroy_cb), data);
 
 	cancel = g_cancellable_new();
 	pidgin_log_viewer_set_list_cancel(lv, cancel);
 
-	for(accounts = purple_accounts_get_all(); accounts != NULL;
+	priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(lv);
+	priv->chat_type = PURPLE_LOG_SYSTEM;
+
+	for(accounts = purple_accounts_get_all();
+		accounts != NULL;
 		accounts = g_list_next(accounts))
 	{
 		PurpleAccount *account = accounts->data;
@@ -1432,19 +1460,21 @@ pidgin_syslog_show(void)
 			continue;
 
 		found_account = TRUE;
-		pidgin_log_data->count++;
-		pidgin_log_data->total++;
+		data->count++;
+		data->total++;
 
-		purple_logs_list_system_logs_async(account, G_PRIORITY_DEFAULT,
-			cancel, pidgin_log_system_list_cb, pidgin_log_data);
+		priv->accounts = g_list_prepend(priv->accounts, account);
+
+		purple_logs_list_async(PURPLE_LOG_SYSTEM, NULL, account,
+			G_PRIORITY_DEFAULT, cancel, pidgin_log_system_list_cb, data);
 	}
 
 	g_object_unref(cancel);
 
-	if (found_account) {
+	if (found_account)
 		syslog_viewer = lv;
-	} else
-		pidgin_log_done_cb(pidgin_log_data);
+	else
+		pidgin_log_done_cb(data);
 }
 
 GList *
@@ -1864,6 +1894,16 @@ pidgin_log_viewer_finalize(GObject *obje
 	if (priv->treestore != NULL)
 		g_object_unref(priv->treestore);
 
+	if (priv->names != NULL) {
+		g_list_foreach(priv->names, (GFunc) g_free, NULL);
+		g_list_free(priv->names);
+	}
+
+	if (priv->accounts != NULL) {
+		// g_list_foreach(priv->accounts, (GFunc) g_object_unref, NULL);
+		g_list_free(priv->accounts);
+	}
+
 	G_OBJECT_CLASS(pidgin_log_viewer_parent_class)->finalize(object);
 }
 
@@ -2149,7 +2189,7 @@ pidgin_log_viewer_init(PidginLogViewer *
 	GtkDialog *dialog = GTK_DIALOG(lv);
 	GtkWindow *window = GTK_WINDOW(lv);
 	GtkWidget *content_area = gtk_dialog_get_content_area(dialog);
-	GtkWidget *pane, *sw, *frame, *find_button, *vbox, *hbox;
+	GtkWidget *pane, *sw, *find_button, *vbox, *hbox;
 
 	/* Window */
 	gtk_dialog_add_buttons(dialog, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
@@ -2269,6 +2309,10 @@ pidgin_log_viewer_init(PidginLogViewer *
 	priv->list_cancel = NULL;
 	priv->selected = FALSE;
 	priv->icon = NULL;
+
+	priv->chat_type = PURPLE_LOG_IM;
+	priv->names = NULL;
+	priv->accounts = NULL;
 }
 
 /****************************************************************************
============================================================
--- finch/gntlog.c	667560b25653cbeee9ef8cdb85a1e620e87fd9df
+++ finch/gntlog.c	d6ac085d088991402f216517f281c9df84c1cbee
@@ -91,6 +91,10 @@ struct _FinchLogViewerPrivate {
 	gboolean           need_log_size;
 	GntWidget          *view_box;
 	gchar              *title;
+
+	PurpleLogChatType  chat_type;
+	GList              *names;
+	GList              *accounts;
 };
 
 typedef struct {
@@ -138,10 +142,6 @@ static void finch_log_viewer_set_total_s
 static void finch_log_viewer_set_total_size(FinchLogViewer *, gsize);
 
 
-G_LOCK_DEFINE(log_viewers);
-G_LOCK_DEFINE(syslog_viewer);
-
-
 static GHashTable *log_viewers = NULL;
 static FinchLogViewer *syslog_viewer = NULL;
 
@@ -236,45 +236,41 @@ finch_log_search_cb(GObject *object, GAs
 static void
 finch_log_search_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
-	_finch_log_data *finch_log_data = userdata;
-	PurpleLog *log = PURPLE_LOG(object);
+	_finch_log_data *data = userdata;
 	GError *error = NULL;
-	gchar *text;
+	GList *logs;
 
-	text = purple_log_read_finish(log, res, NULL, &error);
-	finch_log_data->count--;
+	logs = purple_logs_search_finish(res, &error);
 
-	if (text == NULL) {
-		if (error->code != G_IO_ERROR_CANCELLED)
-			purple_debug_error("gntlog", "Error searching logs: %s\n", error->message);
-	} else if (finch_log_data->is_window_open) {
-		FinchLogViewer *lv = finch_log_data->log_viewer;
-		GntTree *tree = GNT_TREE(finch_log_viewer_get_tree(lv));
+	if (logs == NULL) {
+		if (error != NULL) {
+			if (error->code != G_IO_ERROR_CANCELLED)
+				purple_debug_error("gntlog",
+					"Error searching logs: %s\n", error->message);
+		}
 
-		if (*text && purple_strcasestr(text, finch_log_data->string))
-			gnt_tree_add_row_last(tree, log,
-				gnt_tree_create_row(tree, log_get_date(log)),
-				NULL);
-	}
+		data->count--;
+	} else if (finch_log_data->is_window_open)
+		finch_log_viewer_add_logs(data->log_viewer, logs);
 
 	g_clear_error(&error);
 
-	if (finch_log_data->is_window_open) {
-		const gchar *cur_search =
-			finch_log_viewer_get_search_string(finch_log_data->log_viewer);
+	// if (data->is_window_open) {
+		// const gchar *cur_search =
+			// finch_log_viewer_get_search_string(data->log_viewer);
 
-		if (purple_strequal(finch_log_data->string, cur_search))
-			finch_log_viewer_update_search_bar(finch_log_data);
-	}
+		// if (purple_strequal(data->string, cur_search))
+			// finch_log_viewer_update_search_bar(data);
+	// }
 
-	if (finch_log_data->count < 1)
-		finch_log_search_done_cb(finch_log_data);
+	if (data->count < 1)
+		finch_log_search_done_cb(data);
 }
 
 static void
 search_cb(GntWidget *button, FinchLogViewer *lv)
 {
-	_finch_log_data *finch_log_data;
+	_finch_log_data *data;
 	FinchLogViewerPrivate *priv = FINCH_LOG_VIEWER_GET_PRIVATE(lv);
 	GntTree *tree = GNT_TREE(finch_log_viewer_get_tree(lv));
 	GntTextView *view = GNT_TEXT_VIEW(finch_log_viewer_get_text_area(lv));
@@ -313,13 +309,13 @@ search_cb(GntWidget *button, FinchLogVie
 	gnt_tree_remove_all(tree);
 	gnt_text_view_clear(view);
 
-	finch_log_data = g_new0(_finch_log_data, 1);
-	finch_log_data->is_window_open = TRUE;
-	finch_log_data->log_viewer = lv;
-	finch_log_data->string = g_strdup(search_term);
-	finch_log_data->count = finch_log_data->total = length;
-	finch_log_data->destroy_handler_id = g_signal_connect_swapped(lv,
-		"destroy", G_CALLBACK(finch_window_destroy_cb), finch_log_data);
+	data = g_new0(_finch_log_data, 1);
+	data->is_window_open = TRUE;
+	data->log_viewer = lv;
+	data->string = g_strdup(search_term);
+	data->count = data->total = length;
+	data->destroy_handler_id = g_signal_connect_swapped(lv,
+		"destroy", G_CALLBACK(finch_window_destroy_cb), data);
 
 	bar = finch_log_viewer_get_search_bar(lv);
 	gnt_widget_show(bar);
@@ -328,10 +324,22 @@ search_cb(GntWidget *button, FinchLogVie
 	cancel = g_cancellable_new();
 	finch_log_viewer_set_search_cancel(lv, cancel);
 
-	for ( ; logs != NULL; logs = g_list_next(logs))
-		purple_log_read_async(logs->data, G_PRIORITY_DEFAULT_IDLE, cancel,
-			finch_log_search_cb, finch_log_data);
+	for (account = priv->accounts, name = priv->names;
+		account != NULL;
+		account = g_list_next(account))
+	{
+		const gchar *name_str = NULL;
 
+		if (priv->chat_type != PURPLE_LOG_SYSTEM) {
+			name_str = name->data;
+			name = g_list_next(name);
+		}
+
+		purple_logs_search_async(priv->chat_type, name_str, account->data,
+			search_term, G_PRIORITY_DEFAULT_IDLE, cancel, finch_log_search_cb,
+			data);
+	}
+
 	g_object_unref(cancel);
 }
 
@@ -339,28 +347,23 @@ destroy_cb(FinchLogViewer *lv, log_viewe
 destroy_cb(FinchLogViewer *lv, log_viewer_hash_t *ht)
 {
 	if (ht != NULL) {
-		G_LOCK(log_viewers);
 		g_hash_table_remove(log_viewers, ht);
-		G_UNLOCK(log_viewers);
 
 		g_free(ht->username);
 		g_free(ht);
-	} else {
-		G_LOCK(syslog_viewer);
+	} else
 		syslog_viewer = NULL;
-		G_UNLOCK(syslog_viewer);
-	}
 }
 
 static void
 finch_log_read_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
-	_finch_log_data *finch_log_data = userdata;
+	_finch_log_data *data = userdata;
 
-	if (finch_log_data->is_window_open) {
-		PurpleLog *log = finch_log_data->log;
+	if (data->is_window_open) {
+		PurpleLog *log = data->log;
 		PurpleLogReadFlags flags;
-		FinchLogViewer *lv = finch_log_data->log_viewer;
+		FinchLogViewer *lv = data->log_viewer;
 		GntTextView *view = GNT_TEXT_VIEW(finch_log_viewer_get_text_area(lv));
 		GError *error = NULL;
 		gchar *text, *strip;
@@ -369,7 +372,7 @@ finch_log_read_cb(GObject *object, GAsyn
 
 		if (text == NULL) {
 			if (error->code == G_IO_ERROR_CANCELLED) {
-				finch_log_data_free(finch_log_data);
+				finch_log_data_free(data);
 				g_clear_error(&error);
 
 				return;
@@ -395,14 +398,14 @@ finch_log_read_cb(GObject *object, GAsyn
 		g_clear_error(&error);
 	}
 
-	finch_log_data_free(finch_log_data);
+	finch_log_data_free(data);
 }
 
 static void
 log_select_cb(GntWidget *w, gpointer old_row, gpointer new_row,
 	FinchLogViewer *lv)
 {
-	_finch_log_data *finch_log_data;
+	_finch_log_data *data;
 	PurpleLog *log;
 	PurpleLogChatType chat_type;
 	GntTree *tree = GNT_TREE(w);
@@ -436,19 +439,19 @@ log_select_cb(GntWidget *w, gpointer old
 		g_free(title);
 	}
 
-	finch_log_data = g_new0(_finch_log_data, 1);
-	finch_log_data->is_window_open = TRUE;
-	finch_log_data->total = finch_log_data->count = 1;
-	finch_log_data->log_viewer = lv;
-	finch_log_data->log = log;
-	finch_log_data->destroy_handler_id = g_signal_connect_swapped(lv,
-        "destroy", G_CALLBACK(finch_window_destroy_cb), finch_log_data);
+	data = g_new0(_finch_log_data, 1);
+	data->is_window_open = TRUE;
+	data->total = data->count = 1;
+	data->log_viewer = lv;
+	data->log = log;
+	data->destroy_handler_id = g_signal_connect_swapped(lv,
+        "destroy", G_CALLBACK(finch_window_destroy_cb), data);
 
 	cancel = g_cancellable_new();
 	finch_log_viewer_set_read_cancel(lv, cancel);
 
 	purple_log_read_async(log, G_PRIORITY_DEFAULT, cancel,
-		finch_log_read_cb, finch_log_data);
+		finch_log_read_cb, data);
 
 	g_object_unref(cancel);
 }
@@ -521,11 +524,8 @@ finch_log_viewer_new(log_viewer_hash_t *
 	/**
 	 * Store the information to later prevent duplicate windows from popping up.
 	 */
-	if (ht != NULL) {
-		G_LOCK(log_viewers);
+	if (ht != NULL)
 		g_hash_table_insert(log_viewers, ht, g_object_ref(lv));
-		G_UNLOCK(log_viewers);
-	}
 
 	g_signal_connect(lv, "destroy", G_CALLBACK(destroy_cb), ht);
 
@@ -665,7 +665,7 @@ finch_log_system_list_cb(GObject *object
 	GError *error = NULL;
 	GList *list;
 
-	list = purple_logs_list_system_logs_finish(res, &error);
+	list = purple_logs_list_finish(res, &error);
 
 	if (list == NULL) {
 		finch_log_data->count--;
@@ -740,8 +740,9 @@ finch_log_show(PurpleLogChatType chat_ty
 	PurpleAccount *account)
 {
 	log_viewer_hash_t *ht;
-	_finch_log_data *finch_log_data;
+	_finch_log_data *data;
 	FinchLogViewer *lv;
+	FinchLogViewerPrivate *priv;
 	GCancellable *cancel;
 	const gchar *name = username;
 	gchar *title;
@@ -756,9 +757,7 @@ finch_log_show(PurpleLogChatType chat_ty
 	ht->username = g_strdup(username);
 	ht->account = account;
 
-	G_LOCK(log_viewers);
 	lv = g_hash_table_lookup(log_viewers, ht);
-	G_UNLOCK(log_viewers);
 
 	if (lv != NULL) {
 		gnt_window_present(GNT_WIDGET(lv));
@@ -788,34 +787,40 @@ finch_log_show(PurpleLogChatType chat_ty
 			title = g_strdup(_("All Conversations"));
 	}
 
-	finch_log_data = g_new0(_finch_log_data, 1);
-	finch_log_data->is_window_open = TRUE;
+	data = g_new0(_finch_log_data, 1);
+	data->is_window_open = TRUE;
 
-	lv = finch_log_data->log_viewer = finch_log_viewer_new(ht,
+	lv = data->log_viewer = finch_log_viewer_new(ht,
 		title, TRUE);
 	gnt_widget_show(GNT_WIDGET(lv));
 
-	finch_log_data->destroy_handler_id = g_signal_connect_swapped(lv,
-		"destroy", G_CALLBACK(finch_window_destroy_cb), finch_log_data);
+	data->destroy_handler_id = g_signal_connect_swapped(lv,
+		"destroy", G_CALLBACK(finch_window_destroy_cb), data);
 
 	g_free(title);
 
 	cancel = g_cancellable_new();
 	finch_log_viewer_set_list_cancel(lv, cancel);
 
+	priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(data->log_viewer);
+	priv->chat_type = chat_type;
+	priv->names = g_list_prepend(priv->names, g_strdup(buddyname));
+	/* XXX: g_object_ref(account) */
+	priv->accounts = g_list_prepend(priv->accounts, account);
+
 	if (username != NULL) {
-		finch_log_data->count = finch_log_data->total = 2;
+		data->count = data->total = 2;
 
 		purple_logs_list_logs_async(chat_type, username, account,
-			G_PRIORITY_DEFAULT, cancel, finch_log_list_cb, finch_log_data);
+			G_PRIORITY_DEFAULT, cancel, finch_log_list_cb, data);
 		purple_logs_get_total_size_async(chat_type, username, account,
-			G_PRIORITY_DEFAULT, cancel, finch_log_size_cb, finch_log_data);
+			G_PRIORITY_DEFAULT, cancel, finch_log_size_cb, data);
 	} else {
 		/* This will happen only for IMs */
-		finch_log_data->count = finch_log_data->total = 1;
+		data->count = data->total = 1;
 
 		purple_logs_get_log_sets_async(G_PRIORITY_DEFAULT, cancel,
-			finch_log_sets_cb, finch_log_data);
+			finch_log_sets_cb, data);
 	}
 
 	g_object_unref(cancel);
@@ -824,7 +829,7 @@ finch_log_show_contact(PurpleContact *co
 void
 finch_log_show_contact(PurpleContact *contact)
 {
-	_finch_log_data *finch_log_data;
+	_finch_log_data *data;
 	log_viewer_hash_t *ht;
 	PurpleBlistNode *child;
 	FinchLogViewer *lv;
@@ -839,9 +844,7 @@ finch_log_show_contact(PurpleContact *co
 	ht->chat_type = PURPLE_LOG_IM;
 	ht->contact = contact;
 
-	G_LOCK(log_viewers);
 	lv = g_hash_table_lookup(log_viewers, ht);
-	G_UNLOCK(log_viewers);
 
 	if (lv != NULL) {
 		gnt_window_present(GNT_WIDGET(lv));
@@ -869,23 +872,27 @@ finch_log_show_contact(PurpleContact *co
 			name = "";
 	}
 
-	finch_log_data = g_new0(_finch_log_data, 1);
-	finch_log_data->is_window_open = TRUE;
-	finch_log_data->count = finch_log_data->total = 0;
+	data = g_new0(_finch_log_data, 1);
+	data->is_window_open = TRUE;
+	data->count = data->total = 0;
 
 	title = g_strdup_printf(_("Conversations with %s"), name);
-	lv = finch_log_data->log_viewer = finch_log_viewer_new(ht, title, TRUE);
+	lv = data->log_viewer = finch_log_viewer_new(ht, title, TRUE);
 	g_free(title);
 	gnt_widget_show(GNT_WIDGET(lv));
 
-	finch_log_data->destroy_handler_id = g_signal_connect_swapped(lv,
-		"destroy", G_CALLBACK(finch_window_destroy_cb), finch_log_data);
+	data->destroy_handler_id = g_signal_connect_swapped(lv,
+		"destroy", G_CALLBACK(finch_window_destroy_cb), data);
 
 	cancel = g_cancellable_new();
 	finch_log_viewer_set_list_cancel(lv, cancel);
 
+	priv = PIDGIN_LOG_VIEWER_GET_PRIVATE(data->log_viewer);
+	priv->chat_type = PURPLE_LOG_IM;
+
 	for (child = purple_blist_node_get_first_child(PURPLE_BLIST_NODE(contact));
-		child != NULL; child = purple_blist_node_get_sibling_next(child))
+		child != NULL;
+		child = purple_blist_node_get_sibling_next(child))
 	{
 		const gchar *name;
 		PurpleAccount *account;
@@ -894,21 +901,25 @@ finch_log_show_contact(PurpleContact *co
 			continue;
 
 		found_account = TRUE;
-		finch_log_data->count = (finch_log_data->total += 2);
+		data->count = (data->total += 2);
 
 		name = purple_buddy_get_name(PURPLE_BUDDY(child));
 		account = purple_buddy_get_account(PURPLE_BUDDY(child));
 
+		priv->names = g_list_prepend(priv->names, g_strdup(name));
+		 /* XXX: g_object_ref(account) */
+		priv->accounts = g_list_prepend(priv->accounts, account);
+
 		purple_logs_list_logs_async(PURPLE_LOG_IM, name, account,
-			G_PRIORITY_DEFAULT, cancel, finch_log_list_cb, finch_log_data);
+			G_PRIORITY_DEFAULT, cancel, finch_log_list_cb, data);
 		purple_logs_get_total_size_async(PURPLE_LOG_IM, name, account,
-			G_PRIORITY_DEFAULT, cancel, finch_log_size_cb, finch_log_data);
+			G_PRIORITY_DEFAULT, cancel, finch_log_size_cb, data);
 	}
 
 	g_object_unref(cancel);
 
 	if (!found_account)
-		finch_log_done_cb(finch_log_data);
+		finch_log_done_cb(data);
 }
 
 void
@@ -916,13 +927,12 @@ finch_syslog_show(void)
 {
 	_finch_log_data *finch_log_data;
 	FinchLogViewer *lv;
+	FinchLogViewerPrivate *priv;
 	GCancellable *cancel;
 	GList *accounts = NULL;
 	gboolean found_account = FALSE;
 
-	G_LOCK(syslog_viewer);
 	lv = syslog_viewer;
-	G_UNLOCK(syslog_viewer);
 
 	if (lv != NULL) {
 		gnt_window_present(GNT_WIDGET(lv));
@@ -943,7 +953,11 @@ finch_syslog_show(void)
 	cancel = g_cancellable_new();
 	finch_log_viewer_set_list_cancel(lv, cancel);
 
-	for(accounts = purple_accounts_get_all(); accounts != NULL;
+	priv = FINCH_LOG_VIEWER_GET_PRIVATE(lv);
+	priv->chat_type = PURPLE_LOG_SYSTEM;
+
+	for(accounts = purple_accounts_get_all();
+		accounts != NULL;
 		accounts = g_list_next(accounts))
 	{
 		PurpleAccount *account = accounts->data;
@@ -955,17 +969,18 @@ finch_syslog_show(void)
 		finch_log_data->count++;
 		finch_log_data->total++;
 
-		purple_logs_list_system_logs_async(account, G_PRIORITY_DEFAULT,
-			cancel, finch_log_system_list_cb, finch_log_data);
+		priv->accounts = g_list_prepend(priv->accounts, account);
+
+		purple_logs_list_async(PURPLE_LOG_SYSTEM, NULL, account,
+			G_PRIORITY_DEFAULT, cancel, finch_log_system_list_cb,
+			finch_log_data);
 	}
 
 	g_object_unref(cancel);
 
-	if (found_account) {
-		G_LOCK(syslog_viewer);
+	if (found_account)
 		syslog_viewer = lv;
-		G_UNLOCK(syslog_viewer);
-	} else
+	else
 		finch_log_done_cb(finch_log_data);
 }
 
@@ -1296,6 +1311,16 @@ finch_log_viewer_finalize(GObject *objec
 		g_object_unref(priv->read_cancel);
 	}
 
+	if (priv->names != NULL) {
+		g_list_foreach(priv->names, (GFunc) g_free, NULL);
+		g_list_free(priv->names);
+	}
+
+	if (priv->accounts != NULL) {
+		// g_list_foreach(priv->accounts, (GFunc) g_object_unref, NULL);
+		g_list_free(priv->accounts);
+	}
+
 	G_OBJECT_CLASS(finch_log_viewer_parent_class)->finalize(object);
 }
 
@@ -1620,6 +1645,10 @@ finch_log_viewer_init(FinchLogViewer *lv
 	priv->read_cancel = NULL;
 	priv->search_cancel = NULL;
 	priv->list_cancel = NULL;
+
+	priv->chat_type = PURPLE_LOG_IM;
+	priv->names = NULL;
+	priv->accounts = NULL;
 }
 
 /****************************************************************************
@@ -1639,15 +1668,11 @@ finch_log_init(void)
 {
 	void *handle = finch_log_get_handle();
 
-	G_LOCK(log_viewers);
 	/* Is the ht stuff leaking? */
 	log_viewers = g_hash_table_new_full(log_viewer_hash, log_viewer_equal,
 		NULL, g_object_unref);
-	G_UNLOCK(log_viewers);
 
-	G_LOCK(syslog_viewer);
 	syslog_viewer = NULL;
-	G_UNLOCK(syslog_viewer);
 
 	purple_signal_register(handle, "log-displaying",
 		purple_marshal_VOID__POINTER_POINTER,
@@ -1661,17 +1686,13 @@ finch_log_uninit(void)
 {
 	purple_signals_unregister_by_instance(finch_log_get_handle());
 
-	G_LOCK(log_viewers);
 	if (log_viewers != NULL) {
 		g_hash_table_destroy(log_viewers);
 		log_viewers = NULL;
 	}
-	G_UNLOCK(log_viewers);
 
-	G_LOCK(syslog_viewer);
 	if (syslog_viewer != NULL) {
 		g_object_unref(syslog_viewer);
 		syslog_viewer = NULL;
 	}
-	G_UNLOCK(syslog_viewer);
 }
============================================================
--- libpurple/htmllog.c	cc91227e59a5416773ee3227b8ab09190c0585d9
+++ libpurple/htmllog.c	b79fe1a7d2361ed9e76635d4b5d5aea40e6c3a69
@@ -61,67 +61,18 @@ typedef struct {
 
 /* Local structures */
 typedef struct {
-	PurpleLog *log;
-	PurpleMessageFlags type;
-	gchar *from;
-	time_t time;
-	gchar *message;
-	gint io_priority;
-	GCancellable *cancellable;
 	GAsyncReadyCallback cb;
 	gpointer userdata;
-	gboolean write_header;
-	GOutputStream *out_stream;
-} purple_html_write_callback_data;
-
-typedef struct {
-	GAsyncReadyCallback cb;
-	gpointer userdata;
 } read_callback_data;
 
 
 /* Function prototypes */
 static void purple_html_log_read_async_2(GObject *, GAsyncResult *, gpointer);
-static void purple_html_log_write_async_2(PurpleLog *, GAsyncResult *,
-	purple_html_write_callback_data *);
-static void purple_html_log_write_async_3(GFile *, GAsyncResult *,
-	purple_html_write_callback_data *);
 
 G_DEFINE_TYPE (PurpleHtmlLog, purple_html_log, PURPLE_TYPE_COMMON_LOG)
 
-#define PURPLE_HTML_LOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
-	PURPLE_TYPE_HTML_LOG, PurpleHtmlLogPrivate))
-typedef struct _PurpleHtmlLogPrivate PurpleHtmlLogPrivate;
+PurpleCommonLogClass *html_klass = NULL;
 
-struct _PurpleHtmlLogPrivate {
-	gboolean created;
-};
-
-PurpleLogClass *html_klass = NULL;
-
-/* XXX: Move the extension in CommonLog somehow to reduce duplicate code here/txt
- * and make things more configurable for future loggers? */
-static gboolean
-purple_html_log_create(PurpleLog *log, GCancellable *cancellable,
-	GError **error)
-{
-	return purple_common_log_create(log, ".html", cancellable, error);
-}
-
-static void
-purple_html_log_create_async(PurpleLog *log, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
-{
-	purple_common_log_create_async(log, ".html", io_priority, cancellable, cb,
-		userdata);
-}
-
-static gboolean
-purple_html_log_create_finish(PurpleLog *log, GAsyncResult *res, GError **error)
-{
-	return purple_common_log_create_finish(log, res, error);
-}
-
 /* 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 gchar *
@@ -213,57 +164,20 @@ static gssize
 }
 
 static gssize
-purple_html_log_write(PurpleLog *log, PurpleMessageFlags type,
+purple_html_log_write(PurpleLog *log, PurpleMessageFlags flags,
 	const gchar *from, time_t time, const gchar *message,
 	GCancellable *cancellable, GError **error)
 {
-	GFile *file;
-	GFileOutputStream *stream;
-	GOutputStream *out_stream;
-	const gchar *path;
+	PurpleCommonLog *common_log;
 	gchar *date, *escaped_from;
 	gchar *image_corrected_msg, *msg_fixed, *line;
-	gsize written, size = 0;
-	gboolean write_header, success;
+	gssize written;
+	gsize total = 0;
+	gboolean write_header;
 
-	path = purple_common_log_get_path(PURPLE_COMMON_LOG(log));
+	common_log = PURPLE_COMMON_LOG(log);
+	write_header = purple_common_log_get_socket(common_log) < 0 ? TRUE : FALSE;
 
-	if (path == NULL) {
-		/* This log is new.  We could use the loggers 'new' function, but
-		 * creating a new file there would result in empty files in the case
-		 * that you open a convo with someone, but don't say anything.
-		 */
-		PurpleHtmlLogPrivate *priv = PURPLE_HTML_LOG_GET_PRIVATE(log);
-
-		/* XXX: Should we be doing this? */
-		if (!purple_html_log_create(log, cancellable, error))
-			return -1;
-
-		path = purple_common_log_get_path(PURPLE_COMMON_LOG(log));
-		write_header = TRUE;
-		priv->created = TRUE;
-	} else
-		write_header = FALSE;
-
-	/* If we can't write to the file, give up before we hurt ourselves */
-	if (path == NULL) {
-		g_set_error_literal(error,
-			G_IO_ERROR,
-			G_IO_ERROR_FAILED,
-			_("Unable to find log path"));
-
-		return -1;
-	}
-
-	file = g_file_new_for_path(path);
-	stream = g_file_append_to(file, G_FILE_CREATE_NONE, cancellable, error);
-	g_object_unref(file);
-
-	if (stream == NULL)
-		return -1;
-
-	out_stream = G_OUTPUT_STREAM(stream);
-
 	if (write_header) {
 		PurpleAccount *account = purple_log_get_account(log);
 		PurplePlugin *plugin;
@@ -285,28 +199,23 @@ purple_html_log_write(PurpleLog *log, Pu
 				purple_log_get_name(log), date_full,
 				purple_account_get_username(account), prpl);
 
-		line = g_strdup_printf("<html><head>"
-			"<meta http-equiv=\"content-type\" "
-			"content=\"text/html; charset=UTF-8\">"
-			"<title>"
-			"%s"
-			"</title></head><body>"
-			"<h3>%s</h3>\n",
+		line = g_strdup_printf("<html>\n<head>\n"
+			"<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"
+			"<title>\n%s\n</title>\n</head>\n"
+			"<body>\n<h3>%s</h3>\n",
 			header, header);
 
 		g_free(header);
 
-		success = g_output_stream_write_all(out_stream, line,
-			strlen(line), &written, cancellable, error);
-		g_free(line);
+		written = purple_common_log_write(common_log, flags, line, cancellable,
+			error);
 
-		if (!success) {
-			g_object_unref(stream);
+		g_free(line);
 
+		if (written < 0)
 			return -1;
-		}
 
-		size += written;
+		total += written;
 	}
 
 	escaped_from = g_markup_escape_text(from, -1);
@@ -323,22 +232,22 @@ purple_html_log_write(PurpleLog *log, Pu
 	if (purple_log_get_chat_type(log) == PURPLE_LOG_SYSTEM){
 		line = g_strdup_printf("---- %s @ %s ----<br/>\n", msg_fixed, date);
 	} else {
-		if (type & PURPLE_MESSAGE_SYSTEM)
+		if (flags & PURPLE_MESSAGE_SYSTEM)
 			line = g_strdup_printf("<font size=\"2\">(%s)</font><b> %s"
 				"</b><br/>\n", date, msg_fixed);
-		else if (type & PURPLE_MESSAGE_RAW)
+		else if (flags & PURPLE_MESSAGE_RAW)
 			line = g_strdup_printf("<font size=\"2\">(%s)</font> %s<br/>\n",
 				date, msg_fixed);
-		else if (type & PURPLE_MESSAGE_ERROR)
+		else if (flags & PURPLE_MESSAGE_ERROR)
 			line = g_strdup_printf("<font color=\"#FF0000\"><font "
 				"size=\"2\">(%s)</font><b> %s</b></font><br/>\n",
 				date, msg_fixed);
-		else if (type & PURPLE_MESSAGE_WHISPER)
+		else if (flags & PURPLE_MESSAGE_WHISPER)
 			line = g_strdup_printf("<font color=\"#6C2585\"><font "
 				"size=\"2\">(%s)</font><b> %s:</b></font> %s<br/>\n",
 				date, escaped_from, msg_fixed);
-		else if (type & PURPLE_MESSAGE_AUTO_RESP) {
-			if (type & PURPLE_MESSAGE_SEND)
+		else if (flags & PURPLE_MESSAGE_AUTO_RESP) {
+			if (flags & PURPLE_MESSAGE_SEND)
 				line = g_strdup_printf("<font color=\"#16569E\"><font "
 					"size=\"2\">(%s)</font> <b>%s <%s>:</b>"
 					"</font> %s<br/>\n", date, escaped_from, _("AUTO-REPLY"),
@@ -348,7 +257,7 @@ purple_html_log_write(PurpleLog *log, Pu
 					"size=\"2\">(%s)</font> <b>%s <%s>:</b>"
 					"</font> %s<br/>\n"), date, escaped_from, _("AUTO-REPLY"),
 					msg_fixed);
-		} else if (type & PURPLE_MESSAGE_RECV) {
+		} else if (flags & PURPLE_MESSAGE_RECV) {
 			if (purple_message_meify(msg_fixed, -1))
 				line = g_strdup_printf("<font color=\"#062585\"><font "
 					"size=\"2\">(%s)</font> <b>***%s</b></font> %s<br/>\n",
@@ -357,7 +266,7 @@ purple_html_log_write(PurpleLog *log, Pu
 				line = g_strdup_printf("<font color=\"#A82F2F\"><font "
 					"size=\"2\">(%s)</font> <b>%s:</b></font> %s<br/>\n",
 					date, escaped_from, msg_fixed);
-		} else if (type & PURPLE_MESSAGE_SEND) {
+		} else if (flags & PURPLE_MESSAGE_SEND) {
 			if (purple_message_meify(msg_fixed, -1))
 				line = g_strdup_printf("<font color=\"#062585\"><font "
 					"size=\"2\">(%s)</font> <b>***%s</b></font> %s<br/>\n",
@@ -373,153 +282,36 @@ purple_html_log_write(PurpleLog *log, Pu
 		}
 	}
 
-	success = g_output_stream_write_all(out_stream, line, strlen(line),
-		&written, cancellable, error);
+	written = purple_common_log_write(common_log, flags, line, cancellable,
+		error);
 
-	if (success)
-		size += written;
+	if (written < 0)
+		total = -1;
 	else
-		size = -1;
+		total += written;
 
 	g_free(date);
 	g_free(msg_fixed);
 	g_free(escaped_from);
-	g_object_unref(stream);
 
-	return size;
+	return total;
 }
 
-static void
-purple_html_write_callback_data_free(purple_html_write_callback_data *data)
-{
-	g_object_unref(log);
-	g_free(data->from);
-	g_free(data->message);
-
-	if (data->cancellable != NULL)
-		g_object_unref(data->cancellable);
-
-	if (data->out_stream != NULL)
-		g_object_unref(data->out_stream);
-
-	g_free(data);
-}
-
 /* Create the file on disk if necessary */
 static void
-purple_html_log_write_async(PurpleLog *log, PurpleMessageFlags type,
+purple_html_log_write_async(PurpleLog *log, PurpleMessageFlags flags,
 	const gchar *from, time_t time, const gchar *message, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+	GCancellable *cancellable)
 {
-	purple_html_write_callback_data *callback_data;
-
-	/* Set up the callback data and create the file if needed */
-	callback_data = g_new(purple_html_write_callback_data, 1);
-	callback_data->log = g_object_ref(log);
-	callback_data->type = type;
-	callback_data->from = g_strdup(from);
-	callback_data->time = time;
-	callback_data->message = g_strdup(message);
-	callback_data->io_priority = io_priority;
-	callback_data->cancellable = cancellable != NULL ?
-		g_object_ref(cancellable) : NULL;
-	callback_data->cb = cb;
-	callback_data->userdata = userdata;
-	callback_data->write_header = FALSE;
-	callback_data->out_stream = NULL;
-
-	if (purple_common_log_get_path(PURPLE_COMMON_LOG(log)) == NULL)
-		/* This log is new.  We could use a logger 'new' function, but
-		 * creating a new file there would result in empty files in the case
-		 * that you open a convo with someone, but don't say anything.
-		 */
-		purple_html_log_create_async(log, io_priority, cancellable,
-			(GAsyncReadyCallback) purple_html_log_write_async_2, callback_data);
-	else
-		purple_html_log_write_async_2(log, NULL, callback_data);
-}
-
-/* Open the file up for writing */
-static void
-purple_html_log_write_async_2(PurpleLog *log, GAsyncResult *res,
-	purple_html_write_callback_data *data)
-{
-	GFile *file;
-	GSimpleAsyncResult *simple;
-	const gchar *path;
-
-	if (res != NULL) {
-		GError *error = NULL;
-		PurpleHtmlLogPrivate *priv = PURPLE_HTML_LOG_GET_PRIVATE(log);
-
-		if (!purple_html_log_create_finish(log, res, &error)) {
-			simple = g_simple_async_result_new_from_error(G_OBJECT(log),
-				data->cb, data->userdata, error);
-
-			g_simple_async_result_complete_in_idle(simple);
-			g_object_unref(simple);
-			g_clear_error(&error);
-			purple_html_write_callback_data_free(data);
-			return;
-		}
-
-		g_clear_error(&error);
-		data->write_header = TRUE;
-		priv->created = TRUE;
-	}
-
-	path = purple_common_log_get_path(PURPLE_COMMON_LOG(log));
-
-	/* If we can't write to the file, give up before we hurt ourselves */
-	if (path == NULL) {
-		simple = g_simple_async_result_new_error(G_OBJECT(log), data->cb,
-			data->userdata,
-			G_IO_ERROR,
-			G_IO_ERROR_FAILED,
-			_("Unable to find log path"));
-
-		g_simple_async_result_complete_in_idle(simple);
-		g_object_unref(simple);
-		purple_html_write_callback_data_free(data);
-		return;
-	}
-
-	file = g_file_new_for_path(path);
-	g_file_append_to_async(file, G_FILE_CREATE_NONE,
-		data->io_priority, data->cancellable,
-		(GAsyncReadyCallback) purple_html_log_write_async_3, data);
-	g_object_unref(file);
-}
-
-/* Create the header and write it to the file */
-static void
-purple_html_log_write_async_3(GFile *file, GAsyncResult *res,
-	purple_html_write_callback_data *data)
-{
-	PurpleLog *log = data->log;
-	GError *error;
-	GFileOutputStream *stream;
-	GSimpleAsyncResult *simple;
+	PurpleCommonLog *common_log;
 	gchar *date, *escaped_from;
 	gchar *image_corrected_msg, *msg_fixed, *line;
-	gsize written, size = 0;
-	gboolean success;
+	gboolean write_header;
 
-	stream = g_file_append_to_finish(file, res, &error);
-	if (stream == NULL) {
-		simple = g_simple_async_result_new_from_error(G_OBJECT(log),
-			data->cb, data->userdata, error);
+	common_log = PURPLE_COMMON_LOG(log);
+	write_header = purple_common_log_get_socket(common_log) < 0 ? TRUE : FALSE;
 
-		g_simple_async_result_complete_in_idle(simple);
-		g_object_unref(simple);
-		g_clear_error(&error);
-		purple_html_write_callback_data_free(data);
-		return;
-	}
-
-	data->out_stream = G_OUTPUT_STREAM(stream);
-
-	if (data->write_header) {
+	if (write_header) {
 		PurpleAccount *account = purple_log_get_account(log);
 		PurplePlugin *plugin;
 		const gchar *prpl;
@@ -540,66 +332,49 @@ purple_html_log_write_async_3(GFile *fil
 				purple_log_get_name(log), date_full,
 				purple_account_get_username(account), prpl);
 
-		line = g_strdup_printf("<html><head>"
-			"<meta http-equiv=\"content-type\" "
-			"content=\"text/html; charset=UTF-8\">"
-			"<title>"
-			"%s"
-			"</title></head><body>"
-			"<h3>%s</h3>\n",
+		line = g_strdup_printf("<html>\n<head>\n"
+			"<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"
+			"<title>\n%s\n</title>\n</head>\n"
+			"<body>\n<h3>%s</h3>\n",
 			header, header);
 
-		g_free(header);
+		purple_common_log_write_async(common_log, flags, line, io_priority,
+			cancellable);
 
-		success = g_output_stream_write_all(data->out_stream, line,
-			strlen(line), &written, data->cancellable, &error);
+		g_free(header);
 		g_free(line);
-
-		if (!success) {
-			simple = g_simple_async_result_new_from_error(G_OBJECT(log),
-				data->cb, data->userdata, error);
-
-			g_simple_async_result_complete_in_idle(simple);
-			g_object_unref(simple);
-			g_clear_error(&error);
-			purple_html_write_callback_data_free(data);
-
-			return;
-		}
-
-		size += written;
 	}
 
-	escaped_from = g_markup_escape_text(data->from, -1);
-	image_corrected_msg = convert_image_tags(log, data->message);
+	escaped_from = g_markup_escape_text(from, -1);
+	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 != data->message)
+	if (image_corrected_msg != message)
 		g_free(image_corrected_msg);
 
-	date = _log_get_timestamp(log, data->time);
+	date = _log_get_timestamp(log, time);
 
 	if (purple_log_get_chat_type(log) == PURPLE_LOG_SYSTEM){
 		line = g_strdup_printf("---- %s @ %s ----<br/>\n", msg_fixed, date);
 	} else {
-		if (data->type & PURPLE_MESSAGE_SYSTEM)
+		if (flags & PURPLE_MESSAGE_SYSTEM)
 			line = g_strdup_printf("<font size=\"2\">(%s)</font><b> %s"
 				"</b><br/>\n", date, msg_fixed);
-		else if (data->type & PURPLE_MESSAGE_RAW)
+		else if (flags & PURPLE_MESSAGE_RAW)
 			line = g_strdup_printf("<font size=\"2\">(%s)</font> %s<br/>\n",
 				date, msg_fixed);
-		else if (data->type & PURPLE_MESSAGE_ERROR)
+		else if (flags & PURPLE_MESSAGE_ERROR)
 			line = g_strdup_printf("<font color=\"#FF0000\"><font "
 				"size=\"2\">(%s)</font><b> %s</b></font><br/>\n",
 				date, msg_fixed);
-		else if (data->type & PURPLE_MESSAGE_WHISPER)
+		else if (flags & PURPLE_MESSAGE_WHISPER)
 			line = g_strdup_printf("<font color=\"#6C2585\"><font "
 				"size=\"2\">(%s)</font><b> %s:</b></font> %s<br/>\n",
 				date, escaped_from, msg_fixed);
-		else if (data->type & PURPLE_MESSAGE_AUTO_RESP) {
-			if (data->type & PURPLE_MESSAGE_SEND)
+		else if (flags & PURPLE_MESSAGE_AUTO_RESP) {
+			if (flags & PURPLE_MESSAGE_SEND)
 				line = g_strdup_printf("<font color=\"#16569E\"><font "
 					"size=\"2\">(%s)</font> <b>%s <%s>:</b>"
 					"</font> %s<br/>\n", date, escaped_from, _("AUTO-REPLY"),
@@ -609,7 +384,7 @@ purple_html_log_write_async_3(GFile *fil
 					"size=\"2\">(%s)</font> <b>%s <%s>:</b>"
 					"</font> %s<br/>\n"), date, escaped_from, _("AUTO-REPLY"),
 					msg_fixed);
-		} else if (data->type & PURPLE_MESSAGE_RECV) {
+		} else if (flags & PURPLE_MESSAGE_RECV) {
 			if (purple_message_meify(msg_fixed, -1))
 				line = g_strdup_printf("<font color=\"#062585\"><font "
 					"size=\"2\">(%s)</font> <b>***%s</b></font> %s<br/>\n",
@@ -618,7 +393,7 @@ purple_html_log_write_async_3(GFile *fil
 				line = g_strdup_printf("<font color=\"#A82F2F\"><font "
 					"size=\"2\">(%s)</font> <b>%s:</b></font> %s<br/>\n",
 					date, escaped_from, msg_fixed);
-		} else if (data->type & PURPLE_MESSAGE_SEND) {
+		} else if (flags & PURPLE_MESSAGE_SEND) {
 			if (purple_message_meify(msg_fixed, -1))
 				line = g_strdup_printf("<font color=\"#062585\"><font "
 					"size=\"2\">(%s)</font> <b>***%s</b></font> %s<br/>\n",
@@ -634,28 +409,23 @@ purple_html_log_write_async_3(GFile *fil
 		}
 	}
 
-	success = g_output_stream_write_all(data->out_stream, line, strlen(line),
-		&written, data->cancellable, &error);
+	purple_common_log_write_async(common_log, flags, line, io_priority,
+		cancellable);
 
-	if (success)
-		size += written;
-	else
-		size = -1;
-
 	g_free(date);
 	g_free(msg_fixed);
 	g_free(escaped_from);
-	g_object_unref(stream);
-
-//	return size;
 }
 
 static GList *
 purple_html_log_list(PurpleLogChatType type, const gchar *sn,
 	PurpleAccount *account, GCancellable *cancellable, GError **error)
 {
-	return purple_common_log_list(type, sn, account, ".html",
-		html_klass, cancellable, error);
+	if (type == PURPLE_LOG_SYSTEM)
+		sn = ".system";
+
+	return purple_common_log_list(type, sn, account, html_klass, cancellable,
+		error);
 }
 
 static void
@@ -663,33 +433,21 @@ purple_html_log_list_async(PurpleLogChat
 	PurpleAccount *account, gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata)
 {
-	purple_common_log_list_async(type, sn, account, ".html",
-		html_klass, io_priority, cancellable, cb, userdata);
-}
+	if (type == PURPLE_LOG_SYSTEM)
+		sn = ".system";
 
-static GList *
-purple_html_log_list_syslog(PurpleAccount *account, GCancellable *cancellable,
-	GError **error)
-{
-	return purple_common_log_list(PURPLE_LOG_SYSTEM, ".system", account,
-		".html", html_klass, cancellable, error);
+	purple_common_log_list_async(type, sn, account, html_klass, io_priority,
+		cancellable, cb, userdata);
 }
 
-static void
-purple_html_log_list_syslog_async(PurpleAccount *account, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
-{
-	purple_common_log_list_async(PURPLE_LOG_SYSTEM, ".system", account,
-		".html", html_klass, io_priority, cancellable, cb, userdata);
-}
-
 static gchar *
 purple_html_log_read(PurpleLog *log, PurpleLogReadFlags *flags,
 	GCancellable *cancellable, GError **error)
 {
 	gchar *read, *minus_header;
 
-	read = purple_common_log_read(log, flags, cancellable, error);
+	read = purple_common_log_read(PURPLE_COMMON_LOG(log), flags, cancellable,
+		error);
 
 	if (read == NULL)
 		return NULL;
@@ -721,7 +479,7 @@ purple_html_log_read_async(PurpleLog *lo
 	data->cb = cb;
 	data->userdata = userdata;
 
-	purple_common_log_read_async(log, io_priority,
+	purple_common_log_read_async(PURPLE_COMMON_LOG(log), io_priority,
 		cancellable, purple_html_log_read_async_2, data);
 }
 
@@ -770,7 +528,7 @@ purple_html_log_total_size(PurpleLogChat
 	PurpleAccount *account, GCancellable *cancellable,
 	GError **error)
 {
-	return purple_common_log_total_size(type, name, account, ".html",
+	return purple_common_log_total_size(type, name, account, html_klass,
 		cancellable, error);
 }
 
@@ -779,51 +537,19 @@ purple_html_log_total_size_async(PurpleL
 	PurpleAccount *account, gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata)
 {
-	purple_common_log_total_size_async(type, name, account, ".html",
+	purple_common_log_total_size_async(type, name, account, html_klass,
 		io_priority, cancellable, cb, userdata);
 }
 
 static void
-write_footer(PurpleCommonLog *common_log, const gchar *footer)
-{
-	GError *error = NULL;
-	GFile *file;
-	GFileOutputStream *file_stream;
-	GOutputStream *stream;
-	const gchar *path;
-	gboolean success;
-
-	path = purple_common_log_get_path(common_log);
-
-	if (path == NULL)
-		return;
-
-	file = g_file_new_for_path(path);
-	file_stream = g_file_append_to(file, G_FILE_CREATE_PRIVATE, NULL, NULL);
-	g_object_unref(file);
-
-	if (file_stream == NULL)
-		return;
-
-	stream = G_OUTPUT_STREAM(file_stream);
-	success = g_output_stream_write_all(stream, footer, strlen(footer),
-		NULL, NULL, &error);
-	g_object_unref(file_stream);
-
-	if (!success)
-		purple_debug_warning("htmllog", "Error writing log footer: %s\n",
-			error->message);
-
-	g_clear_error(&error);
-}
-
-static void
 purple_html_log_finalize(GObject *object)
 {
-	PurpleHtmlLogPrivate *priv = PURPLE_HTML_LOG_GET_PRIVATE(object);
+	PurpleCommonLog *common_log = PURPLE_COMMON_LOG(object);
 
-	if (priv->created)
-		write_footer(PURPLE_COMMON_LOG(object), "</body></html>\n");
+	if (purple_common_log_get_socket(common_log) >= 0)
+		/* XXX: Flags here? */
+		purple_common_log_write(common_log, 0, "</body>\n</html>\n",
+			G_PRIORITY_DEFAULT, NULL);
 
 	G_OBJECT_CLASS(purple_html_log_parent_class)->finalize(object);
 }
@@ -831,9 +557,12 @@ purple_html_log_class_init(PurpleHtmlLog
 static void
 purple_html_log_class_init(PurpleHtmlLogClass *class)
 {
+	PurpleCommonLogClass *common_log_class = PURPLE_COMMON_LOG_CLASS(class);
+	PurpleLogClass *log_class = PURPLE_LOG_CLASS(class);
 	GObjectClass *gobject_class = G_OBJECT_CLASS(class);
-	PurpleLogClass *log_class = PURPLE_LOG_CLASS(class);
 
+	common_log_class->extension = ".html";
+
 	log_class->logger_name = _("HTML");
 	log_class->logger_id = "html";
 
@@ -845,19 +574,13 @@ purple_html_log_class_init(PurpleHtmlLog
 	log_class->read_async = purple_html_log_read_async;
 	log_class->total_size_fn = purple_html_log_total_size;
 	log_class->total_size_async = purple_html_log_total_size_async;
-	log_class->list_syslog_fn = purple_html_log_list_syslog;
-	log_class->list_syslog_async = purple_html_log_list_syslog_async;
 
 	gobject_class->finalize = purple_html_log_finalize;
-
-	g_type_class_add_private(gobject_class, sizeof(PurpleHtmlLogPrivate));
 }
 
 static void
 purple_html_log_init(PurpleHtmlLog *html_log)
 {
-	PurpleHtmlLogPrivate *priv = PURPLE_HTML_LOG_GET_PRIVATE(html_log);
-	priv->created = FALSE;
 }
 
 void
@@ -866,12 +589,15 @@ purple_html_log_system_init(void)
 	purple_prefs_add_string("/purple/logging/format", "html");
 
 	html_klass = g_type_class_ref(PURPLE_TYPE_HTML_LOG);
-	purple_log_logger_add(html_klass);
+	purple_log_logger_add(PURPLE_LOG_CLASS(html_klass));
 }
 
 void
 purple_html_log_system_uninit(void)
 {
-	purple_log_logger_remove(html_klass);
-	g_type_class_unref(html_klass);
+	if (html_klass != NULL) {
+		purple_log_logger_remove(PURPLE_LOG_CLASS(html_klass));
+		g_type_class_unref(html_klass);
+		html_klass = NULL;
+	}
 }
============================================================
--- libpurple/oldlog.c	552d2b6cd4a3e44519526bbd86a9bdb107f75b0e
+++ libpurple/oldlog.c	634f1857889412040fbc7c73678143874f7e6007
@@ -68,6 +68,9 @@ purple_old_log_list(PurpleLogChatType ch
 	gchar *index_tmp;
 	gint index_fd;
 
+	if (chat_type == PURPLE_LOG_SYSTEM)
+		return NULL;
+
 	logfile = g_strdup_printf("%s.log", purple_normalize(account, sn));
 	pathstr = g_build_filename(purple_user_dir(), "logs", logfile, NULL);
 	g_free(logfile);
@@ -253,7 +256,7 @@ purple_old_log_list(PurpleLogChatType ch
 				if (newlen != 0) {
 					PurpleLog *log;
 					PurpleOldLogPrivate *priv;
-					
+
 					log = purple_log_new(old_klass, PURPLE_LOG_IM, sn, account,
 						NULL, lasttime, NULL);
 
@@ -317,7 +320,7 @@ purple_old_log_list(PurpleLogChatType ch
 		if ((newlen = offset - lastoff) != 0) {
 			PurpleLog *log;
 			PurpleOldLogPrivate *priv;
-			
+
 			log = purple_log_new(old_klass, PURPLE_LOG_IM, sn, account,
 				NULL, lasttime, NULL);
 
@@ -580,8 +583,11 @@ purple_old_log_class_init(PurpleOldLogCl
 static void
 purple_old_log_class_init(PurpleOldLogClass *class)
 {
+	PurpleCommonLogClass *common_log_class = PURPLE_COMMON_LOG_CLASS(class);
 	PurpleLogClass *log_class = PURPLE_LOG_CLASS(class);
 
+	common_log_class->extension = ".log";
+
 	log_class->logger_name = _("Old flat format");
 	log_class->logger_id = "old";
 
@@ -608,6 +614,9 @@ purple_old_log_system_uninit(void)
 void
 purple_old_log_system_uninit(void)
 {
-	purple_log_logger_remove(old_klass);
-	g_type_class_unref(old_klass);
+	if (old_klass != NULL) {
+		purple_log_logger_remove(old_klass);
+		g_type_class_unref(old_klass);
+		old_klass = NULL;
+	}
 }
============================================================
--- libpurple/txtlog.c	002edc765caba7e5500f87d4dae2ad237fc34f86
+++ libpurple/txtlog.c	c01a0393a2fe849b18c368f0b3ede8dd986a1e60
@@ -67,75 +67,117 @@ G_DEFINE_TYPE (PurpleTxtLog, purple_txt_
 
 /* Function prototypes */
 G_DEFINE_TYPE (PurpleTxtLog, purple_txt_log, PURPLE_TYPE_COMMON_LOG)
-PurpleLogClass *txt_klass = NULL;
+PurpleCommonLogClass *txt_klass = NULL;
 
 static void purple_txt_log_read_async_2(GObject *, GAsyncResult *, gpointer);
 
-static gboolean
-purple_txt_log_create(PurpleLog *log, GCancellable *cancellable, GError **error)
-{
-	return purple_common_log_create(log, ".txt", cancellable, error);
-}
-
 static gssize
-purple_txt_log_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from,
+purple_txt_log_write(PurpleLog *log, PurpleMessageFlags flags, const gchar *from,
 	time_t time, const gchar *message, GCancellable *cancellable,
 	GError **error)
 {
-	PurpleLogChatType chat_type = purple_log_get_chat_type(log);
-	PurpleCommonLog *common_log = PURPLE_COMMON_LOG(log);
-	GFile *file;
-	GFileOutputStream *stream;
-	GOutputStream *out_stream;
-	const gchar *path;
+	PurpleLogChatType chat_type;
+	PurpleCommonLog *common_log;
 	gchar *stripped = NULL, *date, *line;
-	gsize written, size = 0;
-	gboolean success, write_header;
+	gssize written;
+	gsize total = 0;
+	gboolean write_header;
 
-	if (type & PURPLE_MESSAGE_NO_LOG) {
-		g_set_error_literal(error,
-			G_IO_ERROR,
-			G_IO_ERROR_FAILED,
-			_("Trying to log when flagged not to"));
+	common_log = PURPLE_COMMON_LOG(log);
+	write_header = purple_common_log_get_socket(common_log) < 0 ? TRUE : FALSE;
+	chat_type = purple_log_get_chat_type(log);
 
-		return -1;
-	}
+	if (write_header) {
+		PurpleAccount *account = purple_log_get_account(log);
+		PurplePlugin *plugin;
+		const gchar *prpl;
+		time_t log_time = purple_log_get_time(log);
 
-	path = purple_common_log_get_path(common_log);
+		plugin = purple_find_prpl(purple_account_get_protocol_id(account));
+		prpl = PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(account, NULL);
 
-	if (path == NULL) {
-		/* This log is new.  We could use the loggers 'new' function, but
-		 * creating a new file there would result in empty files in the case
-		 * that you open a convo with someone, but don't say anything.
-		 */
-		/* XXX: Should we be doing this? */
-		if (!purple_txt_log_create(log, cancellable, error))
+		if (chat_type == PURPLE_LOG_SYSTEM)
+			line = g_strdup_printf(_("System log for account %s (%s) "
+				"connected at %s\n"),
+				purple_account_get_username(account), prpl,
+				purple_date_format_full(localtime(&log_time)));
+		else
+			line = g_strdup_printf(_("Conversation with %s at %s on %s (%s)\n"),
+				purple_log_get_name(log),
+				purple_date_format_full(localtime(&log_time)),
+				purple_account_get_username(account), prpl);
+
+		written = purple_common_log_write(common_log, flags, line, cancellable,
+			error);
+
+		g_free(line);
+
+		if (written < 0)
 			return -1;
 
-		path = purple_common_log_get_path(common_log);
-		write_header = TRUE;
-	} else
-		write_header = FALSE;
+		total += written;
+	}
 
-	/* If we can't write to the file, give up before we hurt ourselves */
-	if (path == NULL) {
-		g_set_error_literal(error,
-			G_IO_ERROR,
-			G_IO_ERROR_FAILED,
-			_("Unable to find log path"));
+	stripped = purple_markup_strip_html(message);
+	date = _log_get_timestamp(log, time);
 
-		return -1;
+	if (chat_type == PURPLE_LOG_SYSTEM)
+		line = g_strdup_printf("---- %s @ %s ----\n", stripped, date);
+	else {
+		if (flags & PURPLE_MESSAGE_SEND ||
+			flags & PURPLE_MESSAGE_RECV)
+		{
+			if (flags & PURPLE_MESSAGE_AUTO_RESP)
+				line = g_strdup_printf(_("(%s) %s <%s>: %s\n"), date,
+					from, _("AUTO-REPLY"), stripped);
+			else {
+				if (purple_message_meify(stripped, -1))
+					line = g_strdup_printf("(%s) ***%s %s\n", date, from,
+						stripped);
+				else
+					line = g_strdup_printf("(%s) %s: %s\n", date, from,
+						stripped);
+			}
+		} else if (flags & PURPLE_MESSAGE_SYSTEM ||
+			flags & PURPLE_MESSAGE_ERROR ||
+			flags & PURPLE_MESSAGE_RAW)
+			line = g_strdup_printf("(%s) %s\n", date, stripped);
+		else if (flags & PURPLE_MESSAGE_WHISPER)
+			line = g_strdup_printf("(%s) *%s* %s\n", date, from, stripped);
+		else
+			line = g_strdup_printf("(%s) %s%s%s\n", date, from ? from : "",
+				from ? ": " : "", stripped);
 	}
 
-	file = g_file_new_for_path(path);
-	stream = g_file_append_to(file, G_FILE_CREATE_NONE, cancellable, error);
-	g_object_unref(file);
+	written = purple_common_log_write(common_log, flags, line, cancellable,
+		error);
 
-	if (stream == NULL)
-		return -1;
+	if (written < 0)
+		total = -1;
+	else
+		total += written;
 
-	out_stream = G_OUTPUT_STREAM(stream);
+	g_free(line);
+	g_free(date);
+	g_free(stripped);
 
+	return total;
+}
+
+static void
+purple_txt_log_write_async(PurpleLog *log, PurpleMessageFlags flags,
+	const gchar *from, time_t time, const gchar *message, gint io_priority,
+	GCancellable *cancellable)
+{
+	PurpleLogChatType chat_type;
+	PurpleCommonLog *common_log;
+	gchar *stripped = NULL, *date, *line;
+	gboolean write_header;
+
+	common_log = PURPLE_COMMON_LOG(log);
+	write_header = purple_common_log_get_socket(common_log) < 0 ? TRUE : FALSE;
+	chat_type = purple_log_get_chat_type(log);
+
 	if (write_header) {
 		PurpleAccount *account = purple_log_get_account(log);
 		PurplePlugin *plugin;
@@ -156,17 +198,10 @@ purple_txt_log_write(PurpleLog *log, Pur
 				purple_date_format_full(localtime(&log_time)),
 				purple_account_get_username(account), prpl);
 
-		success = g_output_stream_write_all(out_stream, line,
-			strlen(line), &written, cancellable, error);
-		g_free(line);
+		purple_common_log_write_async(common_log, flags, line, io_priority,
+			cancellable);
 
-		if (!success) {
-			g_object_unref(stream);
-
-			return -1;
-		}
-
-		size += written;
+		g_free(line);
 	}
 
 	stripped = purple_markup_strip_html(message);
@@ -175,10 +210,10 @@ purple_txt_log_write(PurpleLog *log, Pur
 	if (chat_type == PURPLE_LOG_SYSTEM)
 		line = g_strdup_printf("---- %s @ %s ----\n", stripped, date);
 	else {
-		if (type & PURPLE_MESSAGE_SEND ||
-			type & PURPLE_MESSAGE_RECV)
+		if (flags & PURPLE_MESSAGE_SEND ||
+			flags & PURPLE_MESSAGE_RECV)
 		{
-			if (type & PURPLE_MESSAGE_AUTO_RESP)
+			if (flags & PURPLE_MESSAGE_AUTO_RESP)
 				line = g_strdup_printf(_("(%s) %s <%s>: %s\n"), date,
 					from, _("AUTO-REPLY"), stripped);
 			else {
@@ -189,39 +224,34 @@ purple_txt_log_write(PurpleLog *log, Pur
 					line = g_strdup_printf("(%s) %s: %s\n", date, from,
 						stripped);
 			}
-		} else if (type & PURPLE_MESSAGE_SYSTEM ||
-			type & PURPLE_MESSAGE_ERROR ||
-			type & PURPLE_MESSAGE_RAW)
+		} else if (flags & PURPLE_MESSAGE_SYSTEM ||
+			flags & PURPLE_MESSAGE_ERROR ||
+			flags & PURPLE_MESSAGE_RAW)
 			line = g_strdup_printf("(%s) %s\n", date, stripped);
-		else if (type & PURPLE_MESSAGE_WHISPER)
+		else if (flags & PURPLE_MESSAGE_WHISPER)
 			line = g_strdup_printf("(%s) *%s* %s\n", date, from, stripped);
 		else
 			line = g_strdup_printf("(%s) %s%s%s\n", date, from ? from : "",
 				from ? ": " : "", stripped);
 	}
 
-	success = g_output_stream_write_all(out_stream, line, strlen(line),
-		&written, cancellable, error);
+	purple_common_log_write_async(common_log, flags, line, io_priority,
+		cancellable);
 
-	if (success)
-		size += written;
-	else
-		size = -1;
-
 	g_free(line);
 	g_free(date);
 	g_free(stripped);
-	g_object_unref(stream);
-
-	return size;
 }
 
 static GList *
 purple_txt_log_list(PurpleLogChatType type, const gchar *sn,
 	PurpleAccount *account, GCancellable *cancellable, GError **error)
 {
-	return purple_common_log_list(type, sn, account, ".txt",
-		txt_klass, cancellable, error);
+	if (type == PURPLE_LOG_SYSTEM)
+		sn = ".system";
+
+	return purple_common_log_list(type, sn, account, txt_klass, cancellable,
+		error);
 }
 
 static void
@@ -229,33 +259,21 @@ purple_txt_log_list_async(PurpleLogChatT
 	PurpleAccount *account, gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata)
 {
-	purple_common_log_list_async(type, sn, account, ".txt",
-		txt_klass, io_priority, cancellable, cb, userdata);
-}
+	if (type == PURPLE_LOG_SYSTEM)
+		sn = ".system";
 
-static GList *
-purple_txt_log_list_syslog(PurpleAccount *account, GCancellable *cancellable,
-	GError **error)
-{
-	return purple_common_log_list(PURPLE_LOG_SYSTEM, ".system", account,
-		".txt", txt_klass, cancellable, error);
+	purple_common_log_list_async(type, sn, account, txt_klass, io_priority,
+		cancellable, cb, userdata);
 }
 
-static void
-purple_txt_log_list_syslog_async(PurpleAccount *account, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
-{
-	purple_common_log_list_async(PURPLE_LOG_SYSTEM, ".system", account,
-		".txt", txt_klass, io_priority, cancellable, cb, userdata);
-}
-
 static gchar *
 purple_txt_log_read(PurpleLog *log, PurpleLogReadFlags *flags,
 	GCancellable *cancellable, GError **error)
 {
 	gchar *read, *minus_header;
 
-	read = purple_common_log_read(log, flags, cancellable, error);
+	read = purple_common_log_read(PURPLE_COMMON_LOG(log), flags, cancellable,
+		error);
 
 	if (read == NULL)
 		return NULL;
@@ -279,7 +297,7 @@ purple_txt_log_read_async(PurpleLog *log
 	data->cb = cb;
 	data->userdata = userdata;
 
-	purple_common_log_read_async(log, io_priority,
+	purple_common_log_read_async(PURPLE_COMMON_LOG(log), io_priority,
 		cancellable, purple_txt_log_read_async_2, data);
 }
 
@@ -326,7 +344,7 @@ purple_txt_log_total_size(PurpleLogChatT
 purple_txt_log_total_size(PurpleLogChatType type, const gchar *name,
 	PurpleAccount *account, GCancellable *cancellable, GError **error)
 {
-	return purple_common_log_total_size(type, name, account, ".txt",
+	return purple_common_log_total_size(type, name, account, txt_klass,
 		cancellable, error);
 }
 
@@ -335,27 +353,29 @@ purple_txt_log_total_size_async(PurpleLo
 	PurpleAccount *account, gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata)
 {
-	purple_common_log_total_size_async(type, name, account, ".txt",
+	purple_common_log_total_size_async(type, name, account, txt_klass,
 		io_priority, cancellable, cb, userdata);
 }
 
 static void
 purple_txt_log_class_init(PurpleTxtLogClass *class)
 {
+	PurpleCommonLogClass *common_log_class = PURPLE_COMMON_LOG_CLASS(class);
 	PurpleLogClass *log_class = PURPLE_LOG_CLASS(class);
 
+	common_log_class->extension = ".txt";
+
 	log_class->logger_name = _("TXT");
 	log_class->logger_id = "txt";
 
 	log_class->write_fn = purple_txt_log_write;
+	log_class->write_async = purple_txt_log_write_async;
 	log_class->list_fn = purple_txt_log_list;
 	log_class->list_async = purple_txt_log_list_async;
 	log_class->read_fn = purple_txt_log_read;
 	log_class->read_async = purple_txt_log_read_async;
 	log_class->total_size_fn = purple_txt_log_total_size;
 	log_class->total_size_async = purple_txt_log_total_size_async;
-	log_class->list_syslog_fn = purple_txt_log_list_syslog;
-	log_class->list_syslog_async = purple_txt_log_list_syslog_async;
 }
 
 static void
@@ -369,13 +389,16 @@ purple_txt_log_system_init(void)
 	purple_prefs_add_string("/purple/logging/format", "txt");
 
 	txt_klass = g_type_class_ref(PURPLE_TYPE_TXT_LOG);
-	purple_log_logger_add(txt_klass);
+	purple_log_logger_add(PURPLE_LOG_CLASS(txt_klass));
 }
 
 void
 purple_txt_log_system_uninit(void)
 {
-	purple_log_logger_remove(txt_klass);
-	g_type_class_unref(txt_klass);
+	if (txt_klass != NULL) {
+		purple_log_logger_remove(PURPLE_LOG_CLASS(txt_klass));
+		g_type_class_unref(txt_klass);
+		txt_klass = NULL;
+	}
 }
 
============================================================
--- libpurple/commonlog.c	cf289a0c56478a5211e103b0f0a3b06161fed03b
+++ libpurple/commonlog.c	9b4dcc5a9ad85ff700c9ee4a6b2f6956a47671cd
@@ -27,9 +27,12 @@
 #include "internal.h"
 #include "commonlog.h"
 #include "debug.h"
+#include "circbuffer.h"
 
 
 /* Helpful macros */
+#define FILES_PER_ENUM 100
+
 #define PURPLE_IS_ACCOUNT(account) (account != NULL)
 
 #if GLIB_CHECK_VERSION(2, 26, 0)
@@ -72,11 +75,6 @@ G_DEFINE_TYPE (PurpleCommonLog, purple_c
 
 /* Structures */
 G_DEFINE_TYPE (PurpleCommonLog, purple_common_log, PURPLE_TYPE_LOG)
-static void purple_common_log_get_property(GObject *, guint, GValue *,
-	GParamSpec *);
-static void purple_common_log_set_property(GObject *, guint, const GValue *,
-	GParamSpec *);
-static void purple_common_log_finalize(GObject *);
 
 enum {
 	PROP_0,
@@ -94,19 +92,28 @@ struct _PurpleCommonLogPrivate {
 struct _PurpleCommonLogPrivate {
 	gchar *path;
 	gint socket;
+	gboolean creating;
+	guint writeh;
+	PurpleCircBuffer *outbuf;
 };
 
 typedef struct {
 	PurpleCommonLog *log;
+	gint open_flags;
 	GCancellable *cancel;
 	GAsyncReadyCallback cb;
 	gpointer userdata;
-	gchar *path;
 } create_callback_data;
 
 typedef struct {
 	PurpleLog *log;
 	GCancellable *cancel;
+} write_callback_data;
+
+typedef struct {
+	PurpleLog *log;
+	GCancellable *cancel;
+	gint io_priority;
 	GAsyncReadyCallback cb;
 	gpointer userdata;
 	gchar *contents;
@@ -118,8 +125,7 @@ typedef struct {
 	PurpleLogChatType chat_type;
 	gchar *name;
 	PurpleAccount *account;
-	gchar *ext;
-	PurpleLogClass *klass;
+	PurpleCommonLogClass *klass;
 	gint io_priority;
 	GCancellable *cancel;
 	GAsyncReadyCallback cb;
@@ -131,7 +137,7 @@ typedef struct {
 	PurpleLogChatType chat_type;
 	gchar *name;
 	PurpleAccount *account;
-	gchar *ext;
+	PurpleCommonLogClass *klass;
 	gint io_priority;
 	GCancellable *cancel;
 	GAsyncReadyCallback cb;
@@ -142,12 +148,7 @@ typedef struct {
 
 
 /* Prototypes */
-static gboolean purple_common_log_remove(PurpleLog *, GCancellable *,
-	GError **);
-static gssize purple_common_log_size(PurpleLog *, GCancellable *, GError **);
 static gboolean purple_common_log_create_async_2(gpointer);
-static gboolean purple_common_log_read_async_2(GIOChannel *, GIOCondition,
-	gpointer);
 static void purple_common_log_list_async_2(GObject *, GAsyncResult *, gpointer);
 static void purple_common_log_list_async_3(GObject *, GAsyncResult *, gpointer);
 static void purple_common_log_list_async_4(GObject *, GAsyncResult *, gpointer);
@@ -163,18 +164,36 @@ create_callback_data_free(gpointer userd
 	create_callback_data *data = userdata;
 
 	g_object_unref(data->log);
-	g_object_unref(data->cancel);
-	g_free(data->path);
+
+	if (data->cancel != NULL)
+		g_object_unref(data->cancel);
+
 	g_free(data);
 }
 
 static void
+write_callback_data_free(gpointer userdata)
+{
+	write_callback_data *data = userdata;
+
+	g_object_unref(data->log);
+
+	if (data->cancel != NULL)
+		g_object_unref(data->cancel);
+
+	g_free(data);
+}
+
+static void
 read_callback_data_free(gpointer userdata)
 {
 	read_callback_data *data = userdata;
 
 	g_object_unref(data->log);
-	g_object_unref(data->cancel);
+
+	if (data->cancel != NULL)
+		g_object_unref(data->cancel);
+
 	g_free(data->contents);
 	g_free(data);
 };
@@ -194,8 +213,10 @@ list_callback_data_free(gpointer userdat
 	list_callback_data *data = userdata;
 
 	g_free(data->name);
-	g_free(data->ext);
-	g_object_unref(data->cancel);
+
+	if (data->cancel != NULL)
+		g_object_unref(data->cancel);
+
 	g_object_unref(data->dir);
 	g_free(data);
 }
@@ -206,66 +227,15 @@ total_size_callback_data_free(gpointer u
 	total_size_callback_data *data = userdata;
 
 	g_free(data->name);
-	g_free(data->ext);
-	g_object_unref(data->cancel);
-	g_object_unref(data->dir);
-	g_free(data);
-}
 
-static void
-purple_common_log_class_init(PurpleCommonLogClass *class)
-{
-	GObjectClass *gobject_class = G_OBJECT_CLASS(class);
-	PurpleLogClass *log_class = PURPLE_LOG_CLASS(class);
+	if (data->cancel != NULL)
+		g_object_unref(data->cancel);
 
-	gobject_class->set_property = purple_common_log_set_property;
-	gobject_class->get_property = purple_common_log_get_property;
-	gobject_class->finalize = purple_common_log_finalize;
-
-	log_class->logger_name = _("Common Logger");
-	log_class->logger_id = "common";
-
-	log_class->read_finish = purple_common_log_read_finish;
-	log_class->remove_fn = purple_common_log_remove;
-	log_class->size_fn = purple_common_log_size;
-
-	properties[PROP_COMMON_LOG_PATH] =
-		g_param_spec_string("path",
-			"Path",
-			"The path to the log's file on disk",
-			NULL,
-			G_PARAM_READWRITE);
-
-	properties[PROP_COMMON_LOG_SOCKET] =
-		g_param_spec_int("socket",
-			"Socket",
-			"The log's socket upon which to perform IO",
-			-1,
-			G_MAXINT,
-			-1,
-			G_PARAM_READABLE);
-
-	g_object_class_install_property(gobject_class,
-		PROP_COMMON_LOG_PATH,
-		properties[PROP_COMMON_LOG_PATH]);
-
-	g_object_class_install_property(gobject_class,
-		PROP_COMMON_LOG_SOCKET,
-		properties[PROP_COMMON_LOG_SOCKET]);
-
-	g_type_class_add_private(gobject_class, sizeof(PurpleCommonLogPrivate));
+	g_object_unref(data->dir);
+	g_free(data);
 }
 
 static void
-purple_common_log_init(PurpleCommonLog *common_log)
-{
-	PurpleCommonLogPrivate *priv = PURPLE_COMMON_LOG_GET_PRIVATE(common_log);
-
-	priv->path = NULL;
-	priv->socket = -1;
-}
-
-static void
 purple_common_log_set_property(GObject *object, guint prop_id,
 	const GValue *value, GParamSpec *pspec)
 {
@@ -380,8 +350,9 @@ static gchar *
 }
 
 static gchar *
-create_log_path(PurpleLog *log, const gchar *dir, const gchar *ext)
+create_log_path(PurpleLog *log, const gchar *dir)
 {
+	PurpleCommonLogClass *klass;
 	struct tm *tm;
 	const gchar *tz, *date;
 	gchar *filename, *path;
@@ -391,8 +362,9 @@ create_log_path(PurpleLog *log, const gc
 	tm = localtime(&log_time);
 	tz = purple_escape_filename(purple_utf8_strftime("%Z", tm));
 	date = purple_utf8_strftime("%Y-%m-%d.%H%M%S%z", tm);
+	klass = PURPLE_COMMON_LOG_GET_CLASS(log);
 
-	filename = g_strdup_printf("%s%s%s", date, tz, ext != NULL ? ext : "");
+	filename = g_strdup_printf("%s%s%s", date, tz, klass->extension);
 	path = g_build_filename(dir, filename, NULL);
 
 	g_free(filename);
@@ -400,133 +372,140 @@ create_log_path(PurpleLog *log, const gc
 	return path;
 }
 
-gboolean
-purple_common_log_create(PurpleLog *log, const gchar *ext,
+static gint
+purple_common_log_create(PurpleCommonLog *common_log, gint open_flags,
 	GCancellable *cancellable, GError **error)
 {
-	PurpleConversation *conv;
+	PurpleLog *log;
+	gchar *dir;
+	gint socket;
 
-	g_return_val_if_fail(PURPLE_IS_COMMON_LOG(log), FALSE);
+	g_return_val_if_fail(PURPLE_IS_COMMON_LOG(common_log), FALSE);
 
-	if (purple_common_log_get_path(PURPLE_COMMON_LOG(log)) == NULL) {
-		gchar *dir, *new_path;
-		gint socket;
+	log = PURPLE_LOG(common_log);
 
-		/* This log is new */
-		dir = purple_log_get_log_dir(purple_log_get_chat_type(log),
-			purple_log_get_name(log),
-			purple_log_get_account(log));
+	/* This log is new */
+	dir = purple_log_get_log_dir(purple_log_get_chat_type(log),
+		purple_log_get_name(log),
+		purple_log_get_account(log));
 
-		if (dir == NULL) {
-			g_set_error_literal(error,
-				G_IO_ERROR,
-				G_IO_ERROR_FAILED,
-				_("Unable to get log directory"));
+	if (dir == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_FAILED,
+			_("Unable to get log directory"));
 
-			goto failed_write;
-		}
+		return -1;
+	}
 
-		if (purple_build_dir(dir, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
-			g_set_error_literal(error,
-				G_FILE_ERROR,
-				g_file_error_from_errno(errno),
-				g_strerror(errno));
+	if (purple_build_dir(dir, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
+		g_set_error_literal(error,
+			G_FILE_ERROR,
+			g_file_error_from_errno(errno),
+			g_strerror(errno));
 
-			goto failed_write;
-		}
+		return -1;
+	}
 
-		new_path = create_log_path(log, dir, ext);
-		g_free(dir);
+	if (purple_common_log_get_path(common_log) == NULL) {
+		gchar *new_path = create_log_path(log, dir);
+		purple_common_log_set_path(common_log, new_path);
+		g_free(new_path);
+	}
 
-		socket = g_open(new_path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+	g_free(dir);
 
-		if (socket < 0) {
-			g_set_error_literal(error,
-				G_FILE_ERROR,
-				g_file_error_from_errno(errno),
-				g_strerror(errno));
+	socket = g_open(purple_common_log_get_path(common_log),
+		open_flags,
+		S_IRUSR | S_IWUSR);
 
-			goto failed_write;
-		}
-		
-		purple_common_log_set_socket(PURPLE_COMMON_LOG(log), socket);
-		purple_common_log_set_path(PURPLE_COMMON_LOG(log), new_path);
+	if (socket < 0) {
+		g_set_error_literal(error,
+			G_FILE_ERROR,
+			g_file_error_from_errno(errno),
+			g_strerror(errno));
+
+		return -1;
 	}
 
-	return TRUE;
+	return socket;
 
-failed_write:
-	/* XXX: Ideally this should be done with signals */
-	conv = purple_log_get_conversation(log);
+// failed_write:
+	// PurpleConversation *conv;
 
-	if (conv != NULL) {
-		gchar *msg;
+	// /* XXX: Ideally this should be done with signals */
+	// conv = purple_log_get_conversation(log);
 
-		msg = g_strdup_printf(_("Logging of this conversation failed: %s"),
-			(*error)->message);
+	// if (conv != NULL) {
+		// gchar *msg;
 
-		purple_conversation_write(conv, NULL,
-			msg, PURPLE_MESSAGE_ERROR, time(NULL));
+		// msg = g_strdup_printf(_("Logging of this conversation failed: %s"),
+			// (*error)->message);
 
-		g_free(msg);
-	}
+		// purple_conversation_write(conv, NULL,
+			// msg, PURPLE_MESSAGE_ERROR, time(NULL));
 
-	return FALSE;
+		// g_free(msg);
+	// }
+
+	// return FALSE;
 }
 
-void
-purple_common_log_create_async(PurpleLog *log, const gchar *ext,
+static void
+purple_common_log_create_async(PurpleCommonLog *common_log, gint open_flags,
 	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb,
 	gpointer userdata)
 {
-	g_return_if_fail(PURPLE_IS_COMMON_LOG(log));
+	PurpleLog *log;
+	gchar *dir;
+	create_callback_data *data;
 
-	if (purple_common_log_get_path(PURPLE_COMMON_LOG(log)) == NULL) {
-		gchar *dir, *new_path;
-		create_callback_data *data;
+	g_return_if_fail(PURPLE_IS_COMMON_LOG(common_log));
 
-		/* This log is new */
-		dir = purple_log_get_log_dir(purple_log_get_chat_type(log),
-			purple_log_get_name(log),
-			purple_log_get_account(log));
+	log = PURPLE_LOG(common_log);
 
-		if (dir == NULL) {
-			SIMPLE_ASYNC_RESULT_FROM_NEW_ERROR(log, cb, userdata,
-				G_IO_ERROR,
-				G_IO_ERROR_FAILED,
-				_("Unable to get log directory"));
+	/* This log is new */
+	dir = purple_log_get_log_dir(purple_log_get_chat_type(log),
+		purple_log_get_name(log),
+		purple_log_get_account(log));
 
-			return;
-		}
+	if (dir == NULL) {
+		SIMPLE_ASYNC_RESULT_FROM_NEW_ERROR(log, cb, userdata,
+			G_IO_ERROR,
+			G_IO_ERROR_FAILED,
+			_("Unable to get log directory"));
 
-		/* Blocks */
-		if (purple_build_dir(dir, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
-			SIMPLE_ASYNC_RESULT_FROM_NEW_ERROR(log, cb, userdata,
-				G_IO_ERROR,
-				g_file_error_from_errno(errno),
-				"%s", g_strerror(errno));
+		return;
+	}
 
-			return;
-		}
+	/* Blocks */
+	if (purple_build_dir(dir, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
+		SIMPLE_ASYNC_RESULT_FROM_NEW_ERROR(log, cb, userdata,
+			G_IO_ERROR,
+			g_file_error_from_errno(errno),
+			"%s", g_strerror(errno));
 
-		new_path = create_log_path(log, dir, ext);
-		g_free(dir);
+		return;
+	}
 
-		/* Create the file */
-		data = g_new(create_callback_data, 1);
-		data->log = g_object_ref(log);
-		data->cancel = g_object_ref(cancellable);
-		data->cb = cb;
-		data->userdata = userdata;
-		data->path = new_path;
+	if (purple_common_log_get_path(common_log) == NULL) {
+		gchar *new_path = create_log_path(log, dir);
+		purple_common_log_set_path(common_log, new_path);
+		g_free(new_path);
+	}
 
-		g_idle_add_full(io_priority, purple_common_log_create_async_2,
-			data, create_callback_data_free);
-	} else
-		SIMPLE_ASYNC_RESULT_NEW_GOOD(log, cb, userdata, 
-			purple_common_log_create_async,
-			g_simple_async_result_set_op_res_gboolean,
-			TRUE);
+	g_free(dir);
+
+	/* Create the file */
+	data = g_new(create_callback_data, 1);
+	data->log = g_object_ref(common_log);
+	data->cancel = cancellable != NULL ? g_object_ref(cancellable) : NULL;
+	data->cb = cb;
+	data->userdata = userdata;
+	data->open_flags = open_flags;
+
+	g_idle_add_full(io_priority, purple_common_log_create_async_2,
+		data, create_callback_data_free);
 }
 
 static gboolean
@@ -536,7 +515,7 @@ purple_common_log_create_async_2(gpointe
 	GError *err = NULL;
 	gint socket;
 	create_callback_data *data = userdata;
-	
+
 	if (g_cancellable_set_error_if_cancelled(data->cancel, &err)) {
 		SIMPLE_ASYNC_RESULT_FROM_ERROR(data->log,
 			data->cb, data->userdata, err);
@@ -544,7 +523,8 @@ purple_common_log_create_async_2(gpointe
 		return FALSE;
 	}
 
-	socket = g_open(data->path, O_CREAT | O_RDWR | O_NONBLOCK,
+	socket = g_open(purple_common_log_get_path(data->log),
+		data->open_flags | O_NONBLOCK,
 		S_IRUSR | S_IWUSR);
 
 	if (socket < 0) {
@@ -558,20 +538,17 @@ purple_common_log_create_async_2(gpointe
 
 		return FALSE;
 	}
-	
-	purple_common_log_set_socket(data->log, socket);
-	purple_common_log_set_path(data->log, data->path);
 
 	SIMPLE_ASYNC_RESULT_NEW_GOOD(data->log, data->cb,
 		data->userdata, purple_common_log_create_async,
-		g_simple_async_result_set_op_res_gboolean,
-		TRUE);
+		g_simple_async_result_set_op_res_gssize,
+		socket);
 
 	return FALSE;
 }
 
-gboolean
-purple_common_log_create_finish(PurpleLog *log, GAsyncResult *res,
+static gint
+purple_common_log_create_finish(PurpleCommonLog *log, GAsyncResult *res,
 	GError **error)
 {
 	GSimpleAsyncResult *simple;
@@ -582,13 +559,13 @@ purple_common_log_create_finish(PurpleLo
 	simple = G_SIMPLE_ASYNC_RESULT(res);
 
 	if (g_simple_async_result_propagate_error(simple, error))
-		return FALSE;
+		return -1;
 
-	return g_simple_async_result_get_op_res_gboolean(G_SIMPLE_ASYNC_RESULT(res));
+	return g_simple_async_result_get_op_res_gssize(simple);
 }
 
 gchar *
-purple_common_log_read(PurpleLog *log, PurpleLogReadFlags *flags,
+purple_common_log_read(PurpleCommonLog *log, PurpleLogReadFlags *flags,
 	GCancellable *cancellable, GError **error)
 {
 	const gchar *path;
@@ -598,7 +575,7 @@ purple_common_log_read(PurpleLog *log, P
 	if (flags != NULL)
 		*flags = 0;
 
-	path = purple_common_log_get_path(PURPLE_COMMON_LOG(log));
+	path = purple_common_log_get_path(log);
 	result = g_file_get_contents(path, &contents, NULL, error);
 
 	if (result)
@@ -607,41 +584,11 @@ purple_common_log_read(PurpleLog *log, P
 		return NULL;
 }
 
-void
-purple_common_log_read_async(PurpleLog *log, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
-{
-	GError *err = NULL;
-	GIOChannel *chan;
-	const gchar *path;
-	read_callback_data *data;
-
-	path = purple_common_log_get_path(PURPLE_COMMON_LOG(log));
-	chan = g_io_channel_new_file(path, "r", &err);
-
-	if (chan == NULL) {
-		SIMPLE_ASYNC_RESULT_FROM_ERROR(log, cb, userdata, err);
-		return;
-	}
-
-	data = g_new(read_callback_data, 1);
-	data->log = g_object_ref(log);
-	data->cancel = g_object_ref(cancellable);
-	data->cb = cb;
-	data->userdata = userdata;
-	data->contents = NULL;
-	data->size = 0;
-	data->allocated = 0;
-
-	g_io_add_watch_full(chan, io_priority,
-		G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
-		purple_common_log_read_async_2, data, read_callback_data_free);
-
-	g_io_channel_unref(chan);
-}
-
+// static void
+// purple_common_log_read_async_3(gpointer userdata, gint socket,
+	// PurpleInputCondition cond)
 static gboolean
-purple_common_log_read_async_2(GIOChannel *chan, GIOCondition cond,
+purple_common_log_read_async_3(GIOChannel *chan, GIOCondition cond,
 	gpointer userdata)
 {
 	GError *err = NULL;
@@ -658,8 +605,11 @@ purple_common_log_read_async_2(GIOChanne
 
 	status = g_io_channel_read_chars(chan, buffer, sizeof(buffer), &bytes, &err);
 
+	printf("Read %d bytes\n", bytes);
+
 	switch (status) {
 	case G_IO_STATUS_NORMAL:
+		/* Successful read */
 		if (data->size + bytes < data->size) {
 			SIMPLE_ASYNC_RESULT_FROM_NEW_ERROR(data->log, data->cb,
 				data->userdata,
@@ -667,31 +617,32 @@ purple_common_log_read_async_2(GIOChanne
 				G_IO_ERROR_FAILED,
 				_("Log file is too large to read"));
 
-				return FALSE;
+			return FALSE;
 		}
 
 		while (data->size + bytes + 1 > data->allocated)
 			if (data->allocated == 0)
-				data->allocated = bytes + 1;
-			else
-				data->allocated *= 2;
+					data->allocated = bytes + 1;
+				else
+					data->allocated *= 2;
 
-		tmp = g_try_realloc(data->contents, sizeof(gchar) * data->allocated);
+		tmp = g_try_realloc(data->contents, data->allocated);
 
 		if (tmp == NULL) {
 			SIMPLE_ASYNC_RESULT_FROM_NEW_ERROR(data->log, data->cb,
 				data->userdata,
-				G_IO_ERROR,
-				g_file_error_from_errno(errno),
-				"%s", g_strerror(errno));
+				G_FILE_ERROR,
+				G_FILE_ERROR_NOMEM,
+				_("Could not allocate enough memory to read file"));
 
 			return FALSE;
 		}
 
 		data->contents = tmp;
-		memcpy(data->contents + data->size, buffer, sizeof(gchar) * bytes);
+		memcpy(data->contents + data->size, buffer, bytes);
 		data->size += bytes;
 
+	case G_IO_STATUS_AGAIN:
 		return TRUE;
 
 	case G_IO_STATUS_EOF:
@@ -714,9 +665,6 @@ purple_common_log_read_async_2(GIOChanne
 
 		return FALSE;
 
-	case G_IO_STATUS_AGAIN:
-		return TRUE;
-
 	case G_IO_STATUS_ERROR:
 	default:
 		SIMPLE_ASYNC_RESULT_FROM_ERROR(data->log, data->cb, data->userdata, err);
@@ -724,6 +672,67 @@ purple_common_log_read_async_2(GIOChanne
 	}
 }
 
+static void
+purple_common_log_read_async_2(GObject *object, GAsyncResult *res,
+	gpointer userdata)
+{
+	PurpleCommonLog *common_log;
+	GError *err = NULL;
+	GIOChannel *chan;
+	gint socket;
+	read_callback_data *data = userdata;
+
+	common_log = PURPLE_COMMON_LOG(object);
+	socket = purple_common_log_create_finish(common_log, res, &err);
+
+	if (socket < 0) {
+		/* XXX: Signal error */
+
+		read_callback_data_free(data);
+		return;
+	}
+
+	g_clear_error(&err);
+	// data->readh = purple_input_add(socket, PURPLE_INPUT_READ,
+		// purple_common_log_read_async_3, data);
+
+	/* Unfortunately, read/writes on windows don't work properly with
+	 * purple_input_add when the socket is a file descriptor.
+	 */
+#ifdef G_OS_WIN32
+	chan = g_io_channel_win32_new_fd(socket);
+#else
+	chan = g_io_channel_unix_new(socket);
+#endif
+
+	g_io_add_watch_full(chan, data->io_priority, G_IO_IN | G_IO_HUP | G_IO_ERR,
+		purple_common_log_read_async_3, data, read_callback_data_free);
+}
+
+void
+purple_common_log_read_async(PurpleCommonLog *log, gint io_priority,
+	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	read_callback_data *data;
+
+	g_return_if_fail(PURPLE_IS_COMMON_LOG(log));
+
+	printf("read_async called\n", socket);
+
+	data = g_new(read_callback_data, 1);
+	data->log = g_object_ref(log);
+	data->cancel = cancellable != NULL ? g_object_ref(cancellable) : NULL;
+	data->io_priority = io_priority;
+	data->cb = cb;
+	data->userdata = userdata;
+	data->contents = NULL;
+	data->size = 0;
+	data->allocated = 0;
+
+	purple_common_log_create_async(log, O_RDONLY, io_priority,
+		cancellable, purple_common_log_read_async_2, data);
+}
+
 gchar *
 purple_common_log_read_finish(PurpleLog *log, GAsyncResult *res,
 	PurpleLogReadFlags *flags, GError **error)
@@ -747,16 +756,160 @@ gssize
 }
 
 gssize
-purple_common_log_write(PurpleLog *log, const gchar *message,
-	GCancellable *cancellable, GError **error)
+purple_common_log_write(PurpleCommonLog *log, PurpleMessageFlags flags,
+	const gchar *message, GCancellable *cancellable, GError **error)
 {
-	return 0;
+	gint socket;
+	gsize count, bytes_written;
+	gboolean result;
+
+	socket = purple_common_log_get_socket(log);
+
+	if (socket < 1) {
+		result = purple_common_log_create(log, O_CREAT | O_WRONLY, cancellable,
+			error);
+
+		if (!result)
+			return -1;
+
+		socket = purple_common_log_get_socket(log);
+	}
+
+	bytes_written = 0;
+	count = strlen(message);
+
+	while (bytes_written < count) {
+		gssize wrote;
+
+		wrote = write(socket, message + bytes_written, count - bytes_written);
+
+		if (wrote < 0) {
+			g_set_error_literal(error,
+				G_FILE_ERROR,
+				g_file_error_from_errno(errno),
+				g_strerror(errno));
+
+			return -1;
+		}
+
+		bytes_written += wrote;
+	}
+
+	return bytes_written;
 }
 
+static void
+purple_common_log_write_async_3(gpointer userdata, gint socket,
+	PurpleInputCondition cond)
+{
+	PurpleCommonLogPrivate *priv;
+	GError *err = NULL;
+	gsize writelen;
+	gssize bytes;
+	write_callback_data *data = userdata;
+
+	if (g_cancellable_set_error_if_cancelled(data->cancel, &err)) {
+		/* XXX: Signal error? */
+		write_callback_data_free(data);
+		return;
+	}
+
+	/* Data can be written to the file */
+	priv = PURPLE_COMMON_LOG_GET_PRIVATE(data->log);
+	writelen = purple_circ_buffer_get_max_read(priv->outbuf);
+
+	if (writelen == 0) {
+		/* Done writing all the data needed */
+		purple_input_remove(priv->writeh);
+		priv->writeh = 0;
+
+		write_callback_data_free(data);
+		return;
+	}
+
+	bytes = write(socket, priv->outbuf->outptr, writelen);
+
+	if (bytes < 0) {
+		if (errno == EAGAIN)
+			return;
+
+		/* XXX: Signal error? */
+		write_callback_data_free(data);
+		return;
+	}
+
+	purple_circ_buffer_mark_read(priv->outbuf, bytes);
+}
+
+static void
+purple_common_log_write_async_2(GObject *object, GAsyncResult *res,
+	gpointer userdata)
+{
+	PurpleCommonLog *common_log;
+	PurpleCommonLogPrivate *priv;
+	write_callback_data *data = userdata;
+
+	common_log = PURPLE_COMMON_LOG(object);
+	priv = PURPLE_COMMON_LOG_GET_PRIVATE(common_log);
+
+	if (res != NULL) {
+		GError *err = NULL;
+		gint socket;
+
+		priv->creating = FALSE;
+
+		socket = purple_common_log_create_finish(common_log, res, &err);
+
+		if (socket < 0) {
+			/* XXX: Signal error */
+			write_callback_data_free(data);
+			return;
+		}
+
+		purple_common_log_set_socket(common_log, socket);
+		g_clear_error(&err);
+	}
+
+	/* File has been opened, time to repeatedly write to it */
+	if (priv->writeh == 0)
+		priv->writeh = purple_input_add(priv->socket, PURPLE_INPUT_WRITE,
+			purple_common_log_write_async_3, data);
+}
+
+void
+purple_common_log_write_async(PurpleCommonLog *log, PurpleMessageFlags flags,
+	const gchar *message, gint io_priority, GCancellable *cancellable)
+{
+	PurpleCommonLogPrivate *priv;
+	write_callback_data *data;
+
+	g_return_if_fail(PURPLE_IS_COMMON_LOG(log));
+
+	data = g_new(write_callback_data, 1);
+	data->log = g_object_ref(log);
+
+	data->cancel = cancellable != NULL ? g_object_ref(cancellable) : NULL;
+
+	priv = PURPLE_COMMON_LOG_GET_PRIVATE(log);
+	purple_circ_buffer_append(priv->outbuf, message, strlen(message));
+
+	if (priv->socket < 0) {
+		/* Watch that we're not creating the log multiple times */
+		if (!priv->creating) {
+			priv->creating = TRUE;
+
+			purple_common_log_create_async(log, O_CREAT | O_WRONLY,
+				io_priority, cancellable,
+				purple_common_log_write_async_2, data);
+		}
+	} else
+		purple_common_log_write_async_2(G_OBJECT(log), NULL, data);
+}
+
 /* XXX: Poorly named */
 static PurpleLog *
 log_from_data(const gchar *path, PurpleLogChatType chat_type, const gchar *name,
-	PurpleAccount *account, PurpleLogClass *klass)
+	PurpleAccount *account, PurpleCommonLogClass *klass)
 {
 	PurpleLog *log;
 	gchar *basename;
@@ -782,8 +935,8 @@ log_from_data(const gchar *path, PurpleL
 		(end = strchr(rest, '.')) == NULL ||
 		strchr(rest, ' ') != NULL)
 	{
-		log = purple_log_new(klass, chat_type, name, account, NULL,
-			stamp, NULL);
+		log = purple_log_new(PURPLE_LOG_CLASS(klass), chat_type, name, account,
+			NULL, stamp, NULL);
 	} else {
 		gchar *tmp = g_strndup(rest, end - rest);
 		tm.tm_zone = tmp;
@@ -798,8 +951,8 @@ log_from_data(const gchar *path, PurpleL
 	stamp = purple_str_to_time(basename, FALSE, &tm, NULL, NULL);
 	g_free(basename);
 
-	log = purple_log_new(klass, chat_type, name, account, NULL,
-		stamp, (stamp != 0) ?  &tm : NULL);
+	log = purple_log_new(PURPLE_LOG_CLASS(klass), chat_type, name, account,
+		NULL, stamp, (stamp != 0) ?  &tm : NULL);
 #endif
 
 	purple_common_log_set_path(PURPLE_COMMON_LOG(log), path);
@@ -809,7 +962,7 @@ purple_common_log_list(PurpleLogChatType
 
 GList *
 purple_common_log_list(PurpleLogChatType chat_type, const gchar *name,
-	PurpleAccount *account, const gchar *ext, PurpleLogClass *klass,
+	PurpleAccount *account, PurpleCommonLogClass *klass,
 	GCancellable *cancellable, GError **error)
 {
 	GError *err = NULL;
@@ -821,8 +974,7 @@ purple_common_log_list(PurpleLogChatType
 
 	g_return_val_if_fail(name != NULL, NULL);
 	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
-	g_return_val_if_fail(ext != NULL, NULL);
-	g_return_val_if_fail(PURPLE_IS_LOG_CLASS(klass), NULL);
+	g_return_val_if_fail(PURPLE_IS_COMMON_LOG_CLASS(klass), NULL);
 
 	path = purple_log_get_log_dir(chat_type, name, account);
 
@@ -866,7 +1018,7 @@ purple_common_log_list(PurpleLogChatType
 		child_path = g_file_get_path(child_file);
 		g_object_unref(child_file);
 
-		if (!purple_str_has_suffix(child_path, ext)) {
+		if (!purple_str_has_suffix(child_path, klass->extension)) {
 			g_free(child_path);
 			continue;
 		}
@@ -890,9 +1042,8 @@ purple_common_log_list_async(PurpleLogCh
 
 void
 purple_common_log_list_async(PurpleLogChatType chat_type, const gchar *name,
-	PurpleAccount *account, const gchar *ext, PurpleLogClass *klass,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb,
-	gpointer userdata)
+	PurpleAccount *account, PurpleCommonLogClass *klass, gint io_priority,
+	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
 {
 	gchar *path;
 	list_callback_data *data;
@@ -901,10 +1052,9 @@ purple_common_log_list_async(PurpleLogCh
 	data->chat_type = chat_type;
 	data->name = g_strdup(name);
 	data->account = account; /* XXX: g_object_ref(account) */
-	data->ext = g_strdup(ext);
 	data->klass = klass;
 	data->io_priority = io_priority;
-	data->cancel = g_object_ref(cancellable);
+	data->cancel = cancellable != NULL ? g_object_ref(cancellable) : NULL;
 	data->cb = cb;
 	data->userdata = userdata;
 
@@ -935,8 +1085,8 @@ purple_common_log_list_async_2(GObject *
 		return;
 	}
 
-	g_file_enumerator_next_files_async(enumerator, 20, data->io_priority,
-		data->cancel, purple_common_log_list_async_3, data);
+	g_file_enumerator_next_files_async(enumerator, FILES_PER_ENUM,
+		data->io_priority, data->cancel, purple_common_log_list_async_3, data);
 }
 
 static void
@@ -980,7 +1130,7 @@ purple_common_log_list_async_3(GObject *
 		child_path = g_file_get_path(child_file);
 		g_object_unref(child_file);
 
-		if (!purple_str_has_suffix(child_path, data->ext)) {
+		if (!purple_str_has_suffix(child_path, data->klass->extension)) {
 			g_free(child_path);
 			continue;
 		}
@@ -1006,8 +1156,8 @@ purple_common_log_list_async_3(GObject *
 			(GDestroyNotify) purple_log_list_free);
 	}
 
-	g_file_enumerator_next_files_async(enumerator, 20, data->io_priority,
-		data->cancel, purple_common_log_list_async_3, data);
+	g_file_enumerator_next_files_async(enumerator, FILES_PER_ENUM,
+		data->io_priority, data->cancel, purple_common_log_list_async_3, data);
 }
 
 static void
@@ -1059,8 +1209,8 @@ purple_common_log_total_size(PurpleLogCh
  */
 gssize
 purple_common_log_total_size(PurpleLogChatType chat_type, const gchar *name,
-	PurpleAccount *account, const gchar *ext, GCancellable *cancellable,
-	GError **error)
+	PurpleAccount *account, PurpleCommonLogClass *klass,
+	GCancellable *cancellable, GError **error)
 {
 	// _purple_logsize_user *lu;
 	GError *err = NULL;
@@ -1073,7 +1223,6 @@ purple_common_log_total_size(PurpleLogCh
 
 	g_return_val_if_fail(name != NULL, -1);
 	g_return_val_if_fail(account != NULL, -1);
-	g_return_val_if_fail(ext != NULL, -1);
 
 	path = purple_log_get_log_dir(chat_type, name, account);
 
@@ -1119,7 +1268,7 @@ purple_common_log_total_size(PurpleLogCh
 		child_path = g_file_get_path(child_file);
 		g_object_unref(child_file);
 
-		if (!purple_str_has_suffix(child_path, ext)) {
+		if (!purple_str_has_suffix(child_path, klass->extension)) {
 			g_free(child_path);
 			continue;
 		}
@@ -1144,7 +1293,7 @@ purple_common_log_total_size_async(Purpl
 
 void
 purple_common_log_total_size_async(PurpleLogChatType chat_type,
-	const gchar *name, PurpleAccount *account, const gchar *ext,
+	const gchar *name, PurpleAccount *account, PurpleCommonLogClass *klass,
 	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb,
 	gpointer userdata)
 {
@@ -1155,9 +1304,9 @@ purple_common_log_total_size_async(Purpl
 	data->chat_type = chat_type;
 	data->name = g_strdup(name);
 	data->account = account; /* XXX: g_object_ref(account) */
-	data->ext = g_strdup(ext);
+	data->klass = klass; /* XXX: g_object_ref(account) */
 	data->io_priority = io_priority;
-	data->cancel = g_object_ref(cancellable);
+	data->cancel = cancellable != NULL ? g_object_ref(cancellable) : NULL;
 	data->cb = cb;
 	data->userdata = userdata;
 	data->total = 0;
@@ -1191,8 +1340,9 @@ purple_common_log_total_size_async_2(GOb
 		return;
 	}
 
-	g_file_enumerator_next_files_async(enumerator, 20, data->io_priority,
-		data->cancel, purple_common_log_total_size_async_3, data);
+	g_file_enumerator_next_files_async(enumerator, FILES_PER_ENUM,
+		data->io_priority, data->cancel, purple_common_log_total_size_async_3,
+		data);
 }
 
 static void
@@ -1232,7 +1382,7 @@ purple_common_log_total_size_async_3(GOb
 		child_path = g_file_get_path(child_file);
 		g_object_unref(child_file);
 
-		if (!purple_str_has_suffix(child_path, data->ext)) {
+		if (!purple_str_has_suffix(child_path, data->klass->extension)) {
 			g_free(child_path);
 			continue;
 		}
@@ -1247,8 +1397,9 @@ purple_common_log_total_size_async_3(GOb
 	g_list_foreach(file_infos, (GFunc) g_object_unref, NULL);
 	g_list_free(file_infos);
 
-	g_file_enumerator_next_files_async(enumerator, 20, data->io_priority,
-		data->cancel, purple_common_log_total_size_async_3, data);
+	g_file_enumerator_next_files_async(enumerator, FILES_PER_ENUM,
+		data->io_priority, data->cancel, purple_common_log_total_size_async_3,
+		data);
 }
 
 static void
@@ -1345,6 +1496,73 @@ purple_common_log_finalize(GObject *obje
 		priv->socket = -1;
 	}
 
+	if (priv->writeh > 0) {
+		purple_input_remove(priv->writeh);
+		priv->writeh = 0;
+	}
+
+	if (priv->outbuf != NULL) {
+		purple_circ_buffer_destroy(priv->outbuf);
+		priv->outbuf = NULL;
+	}
+
 	G_OBJECT_CLASS(purple_common_log_parent_class)->finalize(object);
 }
 
+static void
+purple_common_log_class_init(PurpleCommonLogClass *class)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+	PurpleLogClass *log_class = PURPLE_LOG_CLASS(class);
+
+	class->extension = NULL;
+
+	gobject_class->set_property = purple_common_log_set_property;
+	gobject_class->get_property = purple_common_log_get_property;
+	gobject_class->finalize = purple_common_log_finalize;
+
+	log_class->logger_name = _("Common Logger");
+	log_class->logger_id = "common";
+
+	log_class->read_finish = purple_common_log_read_finish;
+	log_class->remove_fn = purple_common_log_remove;
+	log_class->size_fn = purple_common_log_size;
+
+	properties[PROP_COMMON_LOG_PATH] =
+		g_param_spec_string("path",
+			"Path",
+			"The path to the log's file on disk",
+			NULL,
+			G_PARAM_READWRITE);
+
+	properties[PROP_COMMON_LOG_SOCKET] =
+		g_param_spec_int("socket",
+			"Socket",
+			"The log's socket upon which to perform IO",
+			-1,
+			G_MAXINT,
+			-1,
+			G_PARAM_READABLE);
+
+	g_object_class_install_property(gobject_class,
+		PROP_COMMON_LOG_PATH,
+		properties[PROP_COMMON_LOG_PATH]);
+
+	g_object_class_install_property(gobject_class,
+		PROP_COMMON_LOG_SOCKET,
+		properties[PROP_COMMON_LOG_SOCKET]);
+
+	g_type_class_add_private(gobject_class, sizeof(PurpleCommonLogPrivate));
+}
+
+static void
+purple_common_log_init(PurpleCommonLog *common_log)
+{
+	PurpleCommonLogPrivate *priv = PURPLE_COMMON_LOG_GET_PRIVATE(common_log);
+
+	priv->path = NULL;
+	priv->socket = -1;
+	priv->creating = FALSE;
+	priv->writeh = 0;
+	priv->outbuf = purple_circ_buffer_new(0);
+}
============================================================
--- libpurple/commonlog.h	1dda2a0e61b5c886a8afbc1d2d368c3afa278db1
+++ libpurple/commonlog.h	5558901d66f5cf4e166de33a3c95fb811876326a
@@ -51,6 +51,8 @@ struct _PurpleCommonLogClass {
 struct _PurpleCommonLogClass {
 	PurpleLogClass parent_class;
 
+	const gchar *extension;
+
 	void (*_purple_reserved1)(void);
 	void (*_purple_reserved2)(void);
 	void (*_purple_reserved3)(void);
@@ -106,68 +108,22 @@ gint purple_common_log_get_socket(Purple
  */
 gint purple_common_log_get_socket(PurpleCommonLog *common_log);
 
-/**
- * Creates a new log file in the standard log location
- * with the given file extension, named for the current time,
- * for writing.  If a log file is already open, the existing
- * file handle is retained.  The log's logger_data value is
- * set to a PurpleLogCommonLoggerData struct containing the log
- * file handle and log path.
- *
- * This function is intended to be used as a "common"
- * implementation of a logger's @c write function.
- * It should only be passed to purple_log_logger_new() and never
- * called directly.
- *
- * @param log          The log to write to
- * @param ext          (allow-none): The file extension to give to this log file
- * @param cancellable  (allow-none): GCancellable object
- * @param error        (out) (allow-none): a GError location to store the error
- */
-gboolean purple_common_log_create(PurpleLog *log, const gchar *ext,
+gchar *purple_common_log_read(PurpleCommonLog *log, PurpleLogReadFlags *flags,
 	GCancellable *cancellable, GError **error);
 
-/**
- * Asynchronously create a new log in the standard log location
- *
- * For more details, see purple_common_log_create() which is
- * the synchronous version of this call.
- *
- * @param log          The log to write to
- * @param ext          (allow-none): The file extension to give to this log file
- * @param io_priority  The io priority of the request
- * @param cancellable  (allow-none): GCancellable object
- * @param cb           (allow-none): A GAsyncReadyCallback to call when the request is satisfied
- * @param userdata     (allow-none): The data to pass to callback function
- *
- * @since 3.0.0
- */
-void purple_common_log_create_async(PurpleLog *log, const gchar *ext,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb,
-	gpointer userdata);
+void purple_common_log_read_async(PurpleCommonLog *log, gint io_priority,
+	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
-/**
- * Finishes asyncronously creating a new log in the standard log location
- * See purple_common_log_create() for more details.
- *
- * @param log          The log to write to
- * @param res          A GAsyncResult
- * @param error        (out) (allow-none): a GError location to store the error
- *
- * @return             %TRUE if the log was created, %FALSE otherwise
- *
- * @since 3.0.0
- */
-gboolean purple_common_log_create_finish(PurpleLog *log, GAsyncResult *res,
-	GError **error);
-
-gchar *purple_common_log_read(PurpleLog *log, PurpleLogReadFlags *flags,
-	GCancellable *cancellable, GError **error);
-void purple_common_log_read_async(PurpleLog *log, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 gchar *purple_common_log_read_finish(PurpleLog *log, GAsyncResult *res,
 	PurpleLogReadFlags *flags, GError **error);
 
+gssize purple_common_log_write(PurpleCommonLog *log, PurpleMessageFlags flags,
+	const gchar *message, GCancellable *cancellable, GError **error);
+
+void purple_common_log_write_async(PurpleCommonLog *log,
+	PurpleMessageFlags flags, const gchar *message,
+	gint io_priority, GCancellable *cancellable);
+
 /**
  * Returns a sorted list of logs of the requested type
  *
@@ -180,7 +136,6 @@ gchar *purple_common_log_read_finish(Pur
  * @param chat_type    The type of the logs being listed
  * @param name         The name of the log
  * @param account      The account of the log
- * @param ext          The file extension this log format uses
  * @param klass        The log class to create the new logs with, or
  *                     NULL to use the default type
  * @param cancellable  (allow-none): GCancellable object
@@ -189,7 +144,7 @@ GList *purple_common_log_list(PurpleLogC
  * @return             The sorted list of logs matching the parameters
  */
 GList *purple_common_log_list(PurpleLogChatType chat_type, const gchar *name,
-	PurpleAccount *account, const gchar *ext, PurpleLogClass *klass,
+	PurpleAccount *account, PurpleCommonLogClass *klass,
 	GCancellable *cancellable, GError **error);
 
 /**
@@ -203,7 +158,6 @@ GList *purple_common_log_list(PurpleLogC
  * @param type         The type of the logs being listed
  * @param name         The name of the log
  * @param account      The account of the log
- * @param ext          The file extension this log format uses
  * @param klass        The log class to create the new logs with, or 
  *                     NULL to use the default type
  * @param io_priority  The io priority of the request
@@ -215,9 +169,8 @@ void purple_common_log_list_async(Purple
  * @since 3.0.0
  */
 void purple_common_log_list_async(PurpleLogChatType type, const gchar *name,
-	PurpleAccount *account, const gchar *ext, PurpleLogClass *klass,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb,
-	gpointer userdata);
+	PurpleAccount *account, PurpleCommonLogClass *klass, gint io_priority,
+	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finishes asynchronously listing the logs of a requested type.
@@ -236,8 +189,7 @@ GList *purple_common_log_list_finish(GAs
 GList *purple_common_log_list_finish(GAsyncResult *res, GError **error);
 
 /**
- * Returns the total size of all the logs for a given user, with
- * a given extension.
+ * Returns the total size of all the logs for a user.
  *
  * This function should only be used with logs that are created
  * with purple_common_log_create().  It's intended to be used as
@@ -249,26 +201,24 @@ GList *purple_common_log_list_finish(GAs
  * @param name         The name of the logs to size
  *                     (e.g. the username or chat name)
  * @param account      The account of the log
- * @param ext          The file extension this log format uses
+ * @param klass        The log class to size the logs of
  * @param cancellable  (allow-none): GCancellable object
  * @param error        (out) (allow-none): a GError location to store the error
  *
- * @return             The size of all the logs with the specified extension
- *                     for the specified user
+ * @return             The size of all the logs for the specified user
  */
 gssize purple_common_log_total_size(PurpleLogChatType chat_type,
-	const gchar *name, PurpleAccount *account, const gchar *ext,
+	const gchar *name, PurpleAccount *account, PurpleCommonLogClass *klass,
 	GCancellable *cancellable, GError **error);
 
 /**
- * Asychronously gets the total size of all the logs for a given user, with
- * a given extension.
+ * Asychronously gets the total size of all the logs for a user
  * See purple_common_log_total_size() for more details.
  *
  * @param chat_type    The type of the logs being sized
  * @param name         The name of the logs to size (e.g. the username or chat name)
  * @param account      The account of the log
- * @param ext          The file extension this log format uses
+ * @param klass        The log class to size the logs of
  * @param io_priority  The io priority of the request
  * @param cancellable  (allow-none): GCancellable object
  * @param cb           (allow-none): A GAsyncReadyCallback to call when the
@@ -278,7 +228,7 @@ void purple_common_log_total_size_async(
  * @since 3.0.0
  */
 void purple_common_log_total_size_async(PurpleLogChatType chat_type,
-	const gchar *name, PurpleAccount *account, const gchar *ext,
+	const gchar *name, PurpleAccount *account, PurpleCommonLogClass *klass,
 	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb,
 	gpointer userdata);
 


More information about the Commits mailing list