cpw.nader.asynclogging-3: 5dcc469a: Began work on gobjectifying the logger s...

morshed.nader at gmail.com morshed.nader at gmail.com
Tue Jan 11 22:25:50 EST 2011


----------------------------------------------------------------------
Revision: 5dcc469a155e3384d5d6adf07cf4229633cc9dad
Parent:   8caaf0ca0385ed083a5f8e05073a89225efadeb1
Author:   morshed.nader at gmail.com
Date:     01/11/11 22:21:08
Branch:   im.pidgin.cpw.nader.asynclogging-3
URL: http://d.pidgin.im/viewmtn/revision/info/5dcc469a155e3384d5d6adf07cf4229633cc9dad

Changelog: 

Began work on gobjectifying the logger stuff and re-writing a lot of things, progress looks good so far, just a lot of grunt work
Moved the html/txt/old loggers into their own files

Changes against parent 8caaf0ca0385ed083a5f8e05073a89225efadeb1

  added    libpurple/htmllog.c
  added    libpurple/htmllog.h
  added    libpurple/oldlog.c
  added    libpurple/oldlog.h
  added    libpurple/txtlog.c
  added    libpurple/txtlog.h
  added    libpurple/xmllog.c
  added    libpurple/xmllog.h
  patched  libpurple/Makefile.am
  patched  libpurple/Makefile.mingw
  patched  libpurple/account.c
  patched  libpurple/conversation.c
  patched  libpurple/core.c
  patched  libpurple/log.c
  patched  libpurple/log.h
  patched  pidgin/gtklog.c

-------------- next part --------------
============================================================
--- libpurple/conversation.c	5189741e8d77163db36db82793df9a1f5a37693b
+++ libpurple/conversation.c	cae696ec52d118bcb15f3e51abee287a8bbc964a
@@ -226,7 +226,8 @@ open_log(PurpleConversation *conv)
 static void
 open_log(PurpleConversation *conv)
 {
-	conv->logs = g_list_append(NULL, purple_log_new(conv->type == PURPLE_CONV_TYPE_CHAT ? PURPLE_LOG_CHAT :
+	conv->logs = g_list_append(NULL, purple_log_new(G_TYPE_INVALID,
+							   conv->type == PURPLE_CONV_TYPE_CHAT ? PURPLE_LOG_CHAT :
 							   PURPLE_LOG_IM, conv->name, conv->account,
 							   conv, time(NULL), NULL));
 }
@@ -805,7 +806,7 @@ purple_conversation_close_logs(PurpleCon
 {
 	g_return_if_fail(conv != NULL);
 
-	g_list_foreach(conv->logs, (GFunc)purple_log_free, NULL);
+	g_list_foreach(conv->logs, (GFunc) g_object_unref, NULL);
 	g_list_free(conv->logs);
 	conv->logs = NULL;
 }
============================================================
--- libpurple/core.c	2431d70fed20726a6a5ad9ac00b90938d628f01d
+++ libpurple/core.c	aedd34e95744a3f856b81d15db1ece18d0161f28
@@ -163,7 +163,7 @@ purple_core_init(const char *ui)
 	purple_certificate_init();
 	purple_conversations_init();
 	purple_blist_init();
-	purple_log_init();
+	purple_log_system_init();
 	purple_network_init();
 	purple_privacy_init();
 	purple_pounces_init();
@@ -260,7 +260,7 @@ purple_core_quit(void)
 	purple_cmds_uninit();
 	/* Everything after util_uninit cannot try to write things to the confdir */
 	purple_util_uninit();
-	purple_log_uninit();
+	purple_log_system_uninit();
 
 	purple_signals_uninit();
 
============================================================
--- libpurple/log.c	a190fb11d41f65733874f305d45724048e02f66a
+++ libpurple/log.c	224d1d377ac7d62ed2d31962910eac5d5cf4f4ba
@@ -30,13 +30,15 @@
 #include "debug.h"
 #include "internal.h"
 #include "log.h"
+#include "htmllog.h"
+#include "txtlog.h"
+#include "oldlog.h"
 #include "prefs.h"
 #include "util.h"
 #include "stringref.h"
 #include "imgstore.h"
 #include "time.h"
 
-
 #if ! GLIB_CHECK_VERSION(2, 19, 8)
 //Fixes strict-aliasing warning
 #undef g_static_mutex_get_mutex_impl_shortcut
@@ -48,94 +50,80 @@
 #endif
 
 
-typedef struct {
-	PurpleAccount *account;
-	gchar *name;
-} _purple_logsize_user;
+G_DEFINE_TYPE (PurpleLog, purple_log, G_TYPE_OBJECT)
+static void purple_log_get_property(GObject *, guint, GValue *, GParamSpec *);
+static void purple_log_set_property(GObject *, guint, const GValue *, GParamSpec *);
+static void purple_log_finalize(GObject *);
 
-typedef struct {
-	PurpleLog *log;
-	GAsyncReadyCallback cb;
-	gpointer userdata;
-} _purple_log_free_callback_data;
+enum {
+	PROP_0,
+	PROP_LOG_CHAT_TYPE,
+	PROP_LOG_NAME,
+	PROP_LOG_ACCOUNT,
+	PROP_LOG_TIME,
+	PROP_LOG_CONV,
+	PROP_LOG_LOGGER_DATA,
+	PROP_LOG_TM,
+	LAST_PROP
+};
 
-typedef struct {
-	PurpleLog *log;
-	GAsyncReadyCallback cb;
-	gpointer userdata;
-} _purple_log_delete_callback_data;
+static GParamSpec *properties[LAST_PROP] = { 0 };
 
-typedef struct {
-	_purple_logsize_user *lu;
-	PurpleLog *log;
-	GAsyncReadyCallback cb;
-	gpointer userdata;
-} _purple_log_write_callback_data;
+#define PURPLE_LOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), PURPLE_TYPE_LOG, PurpleLogPrivate))
+typedef struct _PurpleLogPrivate PurpleLogPrivate;
 
-typedef struct {
-	PurpleLog *log;
-	GAsyncReadyCallback cb;
-	gpointer userdata;
-} _purple_log_read_callback_data;
+struct _PurpleLogPrivate {
+	PurpleLogChatType chat_type;  /**< The type of log this is */
+	gchar *name;                  /**< The name of this log */
+	PurpleAccount *account;       /**< The account this log is taking place on */
+	time_t time;                  /**< The time this conversation started, converted to the local timezone */
+	PurpleConversation *conv;     /**< The conversation being logged */
+	gpointer logger_data;         /**< Data used by the log logger */
+	struct tm *tm;                /**< The time this conversation started */
+};
 
 typedef struct {
-	PurpleLog *log;
-	GAsyncReadyCallback cb;
-	gpointer userdata;
-} _purple_log_size_callback_data;
+	PurpleAccount *account;
+	gchar *name;
+} _purple_logsize_user;
 
 typedef struct {
-	PurpleLog *log;
-	PurpleMessageFlags type;
+	PurpleMessageFlags flags;
 	gchar *from;
 	gchar *message;
 	time_t time;
-} _purple_log_logger_write_callback_data;
+} _thread_write_callback_data;
 
 typedef struct {
-	PurpleLog *log;
-	PurpleLogReadFlags *flags;
-} _purple_log_logger_read_callback_data;
+	PurpleLogReadFlags flags;
+	gchar *text;
+} _purple_log_read_res_callback_data;
 
 typedef struct {
 	PurpleAccount *account;
-	PurpleLogType type;
+	PurpleLogChatType chat_type;
 	gchar *name;
 	gchar *ext;
-} _purple_logger_total_size_callback_data;
+} _thread_callback_data;
 
 typedef struct {
-	PurpleLog *log;
-} _purple_logger_sizer_callback_data;
-
-typedef struct {
-	PurpleLogSetCallback set_cb;
-	GHashTable *sets;
-	GMutex *mutex;
-} _purple_logger_get_sets_common_callback_data;
-
-typedef struct {
-	PurpleLog *log;
-} _purple_logger_deleter_callback_data;
-
-typedef struct {
-	PurpleLog *log;
+	PurpleAccount *account;
+	PurpleLogChatType chat_type;
+	gchar *name;
 	gchar *ext;
-} _purple_logger_writer_callback_data;
+} _purple_logger_total_size_callback_data;
 
 typedef struct {
 	PurpleAccount *account;
-	PurpleLogLogger *logger;
-	PurpleLogType type;
+	PurpleLogChatType chat_type;
+	GType log_type;
 	gchar *name;
 	gchar *ext;
 } _purple_logger_lister_callback_data;
 
 typedef struct {
 	GAsyncReadyCallback cb;
-	GCancellable *cancellable;
 	gpointer userdata;
-	gint io_priority;
 	guint counter;
 	gssize total;
 } _purple_log_total_size_callback_data;
@@ -160,7 +148,7 @@ typedef struct {
 	GList *logs;
 	gpointer userdata;
 	guint counter;
-} _purple_log_logs_callback_data;
+} _get_logs_callback_data;
 
 typedef struct {
 	GAsyncReadyCallback cb;
@@ -170,309 +158,514 @@ typedef struct {
 	guint counter;
 } _purple_log_sets_callback_data;
 
-static void purple_log_logger_write_callback_data_free(gpointer);
-static void purple_logger_writer_callback_data_free(gpointer);
 static void purple_logger_lister_callback_data_free(gpointer);
+static void thread_callback_data_free(gpointer);
+static void thread_write_callback_data_free(gpointer);
 static void purple_logger_total_size_callback_data_free(gpointer);
 
-static PurpleLog *purple_log_ref(PurpleLog *);
-static void purple_log_unref(PurpleLog *);
-
 static guint log_set_hash(gconstpointer);
 static gboolean log_set_equal(gconstpointer, gconstpointer);
 static void log_add_log_set_to_hash(GHashTable *, PurpleLogSet *);
 
-static gboolean log_get_log_sets_common(GHashTable *, GMutex *, PurpleLogSetCallback, GError **);
+static GHashTable *log_get_log_sets_common(GCancellable *, GError **);
 static void log_get_log_sets_common_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
-static void log_get_log_sets_common_async(GHashTable *, GMutex *, PurpleLogSetCallback,
-	gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static void log_get_log_sets_common_async(gint, GCancellable *, GAsyncReadyCallback, gpointer);
 
-static void deleter_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+static void create_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+static void purple_log_real_create_async(PurpleLog *, gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static gboolean purple_log_real_create_finish(PurpleLog *, GAsyncResult *, GError **);
 
-static gchar *process_txt_log(gchar *, gchar *);
+static void log_read_res_free(gpointer);
+static void read_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+static void purple_log_real_read_async(PurpleLog *, gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static gchar *purple_log_real_read_finish(PurpleLog *, GAsyncResult *, PurpleLogReadFlags *, GError **);
 
-static gsize html_logger_write(PurpleLog *, PurpleMessageFlags, const gchar *,
-	time_t, const gchar *);
-static void html_logger_write_async(PurpleLog *, PurpleMessageFlags, const gchar *,
-	time_t, const gchar *, gint, GCancellable *, GAsyncReadyCallback, gpointer);
-static void html_logger_finalize(PurpleLog *);
-static GList *html_logger_list(PurpleLogType, const gchar *, PurpleAccount *);
-static void html_logger_list_async(PurpleLogType, const gchar *, PurpleAccount *,
-	gint, GCancellable *, GAsyncReadyCallback, gpointer);
-static GList *html_logger_list_syslog(PurpleAccount *);
-static void html_logger_list_syslog_async(PurpleAccount *, gint, GCancellable *,
-	GAsyncReadyCallback, gpointer);
-static gchar *html_logger_read(PurpleLog *, PurpleLogReadFlags *);
-static void html_logger_read_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
-static void html_logger_read_async(PurpleLog *, PurpleLogReadFlags *,
-	gint, GCancellable *, GAsyncReadyCallback, gpointer);
-static gint html_logger_total_size(PurpleLogType, const gchar *, PurpleAccount *);
-static void html_logger_total_size_async(PurpleLogType, const gchar *, PurpleAccount *,
-	gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static void size_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+static void purple_log_real_size_async(PurpleLog *, gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static gssize purple_log_real_size_finish(PurpleLog *, GAsyncResult *, GError **);
 
-static GList *old_logger_list(PurpleLogType, const gchar *, PurpleAccount *);
-static gint old_logger_total_size(PurpleLogType, const gchar *, PurpleAccount *);
-static gchar *old_logger_read(PurpleLog *, PurpleLogReadFlags *);
-static gint old_logger_size(PurpleLog *);
-static void old_logger_get_log_sets(PurpleLogSetCallback, GHashTable *);
-static void old_logger_finalize(PurpleLog *);
+static void write_update_size_cache(PurpleLog *, gssize);
+static void write_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+static void purple_log_real_write_async(PurpleLog *, PurpleMessageFlags, const gchar *, time_t, const gchar *, gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static gssize purple_log_real_write_finish(PurpleLog *, GAsyncResult *, GError **);
 
-static gsize txt_logger_write(PurpleLog *, PurpleMessageFlags, const gchar *, time_t,
-	const gchar *);
-static void txt_logger_write_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
-static void txt_logger_write_async(PurpleLog *, PurpleMessageFlags, const gchar *, time_t,
-	const gchar *, gint, GCancellable *, GAsyncReadyCallback, gpointer);
-static void txt_logger_finalize(PurpleLog *);
-static GList *txt_logger_list(PurpleLogType, const gchar *, PurpleAccount *);
-static void txt_logger_list_async(PurpleLogType, const gchar *, PurpleAccount *,
-	gint, GCancellable *, GAsyncReadyCallback, gpointer);
-static GList *txt_logger_list_syslog(PurpleAccount *);
-static void txt_logger_list_syslog_async(PurpleAccount *, gint, GCancellable *,
-	GAsyncReadyCallback, gpointer);
-static gchar *txt_logger_read(PurpleLog *, PurpleLogReadFlags *);
-static void txt_logger_read_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
-static void txt_logger_read_async(PurpleLog *, PurpleLogReadFlags *, gint, GCancellable *,
-	GAsyncReadyCallback, gpointer);
-static gint txt_logger_total_size(PurpleLogType, const gchar *, PurpleAccount *);
-static void txt_logger_total_size_async(PurpleLogType, const gchar *, PurpleAccount *,
-	gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static void total_size_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+static void purple_log_real_total_size_async(PurpleLog *, PurpleLogChatType, const gchar *, PurpleAccount *, gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static gssize purple_log_real_total_size_finish(PurpleLog *, GAsyncResult *, GError **);
 
-static void log_free_cb(GObject *, GAsyncResult *, gpointer);
-static void log_delete_cb(GObject *, GAsyncResult *, gpointer);
-static void log_write_cb(GObject *, GAsyncResult *, gpointer);
-static void log_read_cb(GObject *, GAsyncResult *, gpointer);
-static void log_size_cb(GObject *, GAsyncResult *, gpointer);
+static void remove_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+static void purple_log_real_remove_async(PurpleLog *, gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static gboolean purple_log_real_remove_finish(PurpleLog *, GAsyncResult *, GError **);
+
+static void get_logs_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+static void purple_log_real_get_logs_async(PurpleLog *, PurpleLogChatType, const gchar *, PurpleAccount *, gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static GList *purple_log_real_get_logs_finish(PurpleLog *, GAsyncResult *, GError **);
+
+static void get_log_sets_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+static void purple_log_real_get_log_sets_async(PurpleLog *, gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static GHashTable *purple_log_real_get_log_sets_finish(PurpleLog *, GAsyncResult *, GError **);
+
+static void list_syslog_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+static void purple_log_real_list_syslog_async(PurpleLog *, PurpleAccount *, gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static GList *purple_log_real_list_syslog_finish(PurpleLog *, GAsyncResult *, 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_total_size_intermediate_cb(GObject *, GAsyncResult *, gpointer);
 static void log_total_size_list_cb(GObject *, GAsyncResult *, gpointer);
 static void log_hash_cb(GObject *, GAsyncResult *, gpointer);
-static void log_get_activity_score_cb(GObject *, GAsyncResult *, gpointer);
-static void log_get_activity_score_size_cb(GObject *, GAsyncResult *, gpointer);
 
 
 G_LOCK_DEFINE(loggers);
 G_LOCK_DEFINE(current_logger);
-G_LOCK_DEFINE(html_logger);
-G_LOCK_DEFINE(txt_logger);
-G_LOCK_DEFINE(old_logger);
 G_LOCK_DEFINE(logsize_users);
 G_LOCK_DEFINE(logsize_users_decayed);
-G_LOCK_DEFINE(log_ref_table);
 
 
-static GSList *loggers = NULL;
-static PurpleLogLogger *current_logger = NULL;
+static GArray *loggers = NULL;
+static GType current_logger = G_TYPE_INVALID;
+static GHashTable *logsize_users = NULL;
+static GHashTable *logsize_users_decayed = NULL;
 
-static PurpleLogLogger *html_logger;
-static PurpleLogLogger *txt_logger;
-static PurpleLogLogger *old_logger;
 
-static GHashTable *logsize_users = NULL;
-static GHashTable *logsize_users_decayed = NULL;
-static GHashTable *log_ref_table = NULL;
+GType
+purple_log_chat_type_get_type(void)
+{
+	static GType etype = 0;
 
+	if (G_UNLIKELY(etype == 0)) {
+		static const GEnumValue values[] = {
+            { PURPLE_LOG_IM, "PURPLE_LOG_IM", "Purple Log IM" },
+            { PURPLE_LOG_IM, "PURPLE_LOG_CHAT", "Purple Log Chat" },
+            { PURPLE_LOG_IM, "PURPLE_LOG_SYSTEM", "Purple Log System" },
+            { 0, NULL, NULL }
+        };
 
+        etype = g_enum_register_static(g_intern_static_string("PurpleLogChatType"), values);
+    }
+
+    return etype;
+}
+
+static gpointer
+purple_struct_tm_copy(gpointer original)
+{
+	struct tm *copy;
+
+	copy = g_new(struct tm, 1);
+	memcpy(copy, original, sizeof(struct tm));
+
+	return copy;
+}
+
+#if GLIB_CHECK_VERSION(2, 26, 0)
+
+typedef struct tm PurpleStructTM;
+G_DEFINE_BOXED_TYPE (PurpleStructTM, purple_struct_tm, purple_struct_tm_copy, g_free);
+
+#else
+
+GType
+purple_struct_tm_get_type(void)
+{
+	static GType object_type = 0;
+
+	if (G_UNLIKELY (object_type == 0))
+		object_type = g_boxed_type_register_static(g_intern_static_string("PurpleStructTm"),
+			purple_struct_tm_copy, g_free);
+
+	return object_type;
+}
+
+#endif
+
 static void
-purple_log_logger_write_callback_data_free(gpointer userdata)
+purple_log_class_init(PurpleLogClass *class)
 {
-	_purple_log_logger_write_callback_data *callback_data = userdata;
+	GObjectClass *gobject_class = G_OBJECT_CLASS(class);
 
-	g_free(callback_data->from);
-	g_free(callback_data->message);
-	g_free(callback_data);
+	gobject_class->set_property = purple_log_set_property;
+	gobject_class->get_property = purple_log_get_property;
+	gobject_class->finalize = purple_log_finalize;
+
+	class->logger_name = "";
+	class->logger_id = "";
+	class->create_async = purple_log_real_create_async;
+	class->create_finish = purple_log_real_create_finish;
+	class->write_async = purple_log_real_write_async;
+	class->write_finish = purple_log_real_write_finish;
+	class->list_async = purple_log_real_get_logs_async;
+	class->list_finish = purple_log_real_get_logs_finish;
+	class->read_async = purple_log_real_read_async;
+	class->read_finish = purple_log_real_read_finish;
+	class->size_async = purple_log_real_size_async;
+	class->size_finish = purple_log_real_size_finish;
+	class->total_size_async = purple_log_real_total_size_async;
+	class->total_size_finish = purple_log_real_total_size_finish;
+	class->list_syslog_async = purple_log_real_list_syslog_async;
+	class->list_syslog_finish = purple_log_real_list_syslog_finish;
+	class->get_log_sets_async = purple_log_real_get_log_sets_async;
+	class->get_log_sets_finish = purple_log_real_get_log_sets_finish;
+	class->remove_async = purple_log_real_remove_async;
+	class->remove_finish = purple_log_real_remove_finish;
+	class->is_removable_async = purple_log_real_is_removable_async;
+	class->is_removable_finish = purple_log_real_is_removable_finish;
+
+	properties[PROP_LOG_CHAT_TYPE] =
+		g_param_spec_enum("chat-type",
+			"Chat Type",
+			"The chat type of the log",
+			PURPLE_TYPE_LOG_CHAT_TYPE,
+			PURPLE_LOG_SYSTEM,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READABLE);
+
+	properties[PROP_LOG_NAME] =
+		g_param_spec_string("name",
+			"Name",
+			"The name of the conversation (buddy name, chat name, etc.)",
+			"",
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READABLE);
+
+	properties[PROP_LOG_ACCOUNT] =
+		//Ideally should be g_param_spec_object
+		g_param_spec_pointer("account",
+			"Account",
+			"The account the conversation is occurring on",
+//			PURPLE_TYPE_ACCOUNT,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READABLE);
+
+	properties[PROP_LOG_TIME] =
+		g_param_spec_long("time",
+			"Time",
+			"The time this conversation started, converted to the local timezone",
+			0,
+			G_MAXLONG,
+			0,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READABLE);
+
+	properties[PROP_LOG_CONV] =
+		//Ideally should be g_param_spec_object
+		g_param_spec_pointer("conv",
+			"Conversation",
+			"The conversation being logged",
+//			PURPLE_TYPE_CONVERSATION,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READABLE);
+
+	properties[PROP_LOG_LOGGER_DATA] =
+		//Ideally should be g_param_spec_object
+		g_param_spec_pointer("logger-data",
+			"Logger Data",
+			"Data used by loggers", //better description
+//			PURPLE_TYPE_CONVERSATION,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READABLE);
+
+	properties[PROP_LOG_TM] =
+		g_param_spec_boxed("tm",
+			"Time (tm)",
+			"The time this conversation started",
+			PURPLE_TYPE_STRUCT_TM,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READABLE);
+
+	g_object_class_install_property(gobject_class,
+		PROP_LOG_CHAT_TYPE,
+		properties[PROP_LOG_CHAT_TYPE]);
+
+	g_object_class_install_property(gobject_class,
+		PROP_LOG_NAME,
+		properties[PROP_LOG_NAME]);
+
+	g_object_class_install_property(gobject_class,
+		PROP_LOG_ACCOUNT,
+		properties[PROP_LOG_ACCOUNT]);
+
+	g_object_class_install_property(gobject_class,
+		PROP_LOG_TIME,
+		properties[PROP_LOG_TIME]);
+
+	g_object_class_install_property(gobject_class,
+		PROP_LOG_CONV,
+		properties[PROP_LOG_CONV]);
+
+	g_object_class_install_property(gobject_class,
+		PROP_LOG_LOGGER_DATA,
+		properties[PROP_LOG_LOGGER_DATA]);
+
+	g_object_class_install_property(gobject_class,
+		PROP_LOG_TM,
+		properties[PROP_LOG_TM]);
+
+	g_type_class_add_private(gobject_class, sizeof(PurpleLogPrivate));
 }
 
 static void
-purple_logger_writer_callback_data_free(gpointer userdata)
+purple_log_init(PurpleLog *log)
 {
-	_purple_logger_writer_callback_data *callback_data = userdata;
+	PurpleLogPrivate *priv = PURPLE_LOG_GET_PRIVATE(log);
 
-	g_free(callback_data->ext);
-	g_free(callback_data);
+	priv->chat_type = PURPLE_LOG_SYSTEM;
+	priv->name = NULL;
+	priv->account = NULL;
+	priv->time = 0;
+	priv->conv = NULL;
+	priv->logger_data = NULL;
+	//These needed?
+	priv->tm = NULL;
 }
 
 static void
-purple_logger_lister_callback_data_free(gpointer userdata)
+purple_log_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
-	_purple_logger_lister_callback_data *callback_data = userdata;
+	PurpleLog *log = PURPLE_LOG(object);
 
-	g_free(callback_data->name);
-	g_free(callback_data->ext);
-	g_free(callback_data);
+	switch (prop_id) {
+	case PROP_LOG_LOGGER_DATA:
+		purple_log_set_logger_data(log, g_value_get_pointer(value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
-purple_logger_total_size_callback_data_free(gpointer userdata)
+purple_log_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 {
-	_purple_logger_total_size_callback_data *callback_data = userdata;
+	PurpleLog *log = PURPLE_LOG(object);
 
-	g_free(callback_data->name);
-	g_free(callback_data->ext);
-	g_free(callback_data);
+	switch (prop_id) {
+	case PROP_LOG_CHAT_TYPE:
+		g_value_set_enum(value, purple_log_get_chat_type(log));
+		break;
+	case PROP_LOG_NAME:
+		g_value_set_string(value, purple_log_get_name(log));
+		break;
+	case PROP_LOG_ACCOUNT:
+		//g_value_set_object
+		g_value_set_pointer(value, purple_log_get_account(log));
+		break;
+	case PROP_LOG_TIME:
+		g_value_set_long(value, purple_log_get_time(log));
+		break;
+	case PROP_LOG_CONV:
+		//g_value_set_object
+		g_value_set_pointer(value, purple_log_get_conversation(log));
+		break;
+	case PROP_LOG_LOGGER_DATA:
+		g_value_set_pointer(value, purple_log_get_logger_data(log));
+		break;
+	case PROP_LOG_TM:
+		g_value_set_boxed(value, purple_log_get_tm(log));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+		break;
+	}
 }
 
+void
+purple_log_set_logger_data(PurpleLog *log, gpointer logger_data)
+{
+	g_return_if_fail(PURPLE_IS_LOG(log));
 
-/**************************************************************************
- * PUBLIC LOGGING FUNCTIONS ***********************************************
- **************************************************************************/
+	PURPLE_LOG_GET_PRIVATE(log)->logger_data = logger_data;
 
-PurpleLog *
-purple_log_new(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	PurpleConversation *conv, time_t time, const struct tm *tm)
+	g_object_notify(G_OBJECT(log), "logger-data");
+}
+
+PurpleLogChatType
+purple_log_get_chat_type(const PurpleLog *log)
 {
-	PurpleLog *log;
+	g_return_val_if_fail(PURPLE_IS_LOG(log), PURPLE_LOG_SYSTEM);
 
-	g_return_val_if_fail(name != NULL, NULL);
-	g_return_val_if_fail(account != NULL, NULL);
+	return PURPLE_LOG_GET_PRIVATE(log)->chat_type;
+}
 
-	log = purple_log_ref(g_slice_new(PurpleLog));
-	/* IMPORTANT: Make sure to initialize all the members of PurpleLog */
-	PURPLE_DBUS_REGISTER_POINTER(log, PurpleLog);
+G_CONST_RETURN gchar *
+purple_log_get_name(const PurpleLog *log)
+{
+	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
 
-	log->type = type;
-	log->name = g_strdup(purple_normalize(account, name));
-	log->account = account;
-	log->conv = conv;
-	log->time = time;
-	log->logger = purple_log_logger_get();
-	log->logger_data = NULL;
+	return PURPLE_LOG_GET_PRIVATE(log)->name;
+}
 
-	if (tm == NULL)
-		log->tm = NULL;
-	else {
-		/* There's no need to zero this as we immediately do a direct copy. */
-		log->tm = g_slice_new(struct tm);
+PurpleAccount *
+purple_log_get_account(const PurpleLog *log)
+{
+	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
 
-		*(log->tm) = *tm;
+	return PURPLE_LOG_GET_PRIVATE(log)->account;
+}
 
-#ifdef HAVE_STRUCT_TM_TM_ZONE
-		/* XXX: This is so wrong... */
-		if (log->tm->tm_zone != NULL) {
-			gchar *tmp = g_locale_from_utf8(log->tm->tm_zone, -1, NULL, NULL, NULL);
+time_t
+purple_log_get_time(const PurpleLog *log)
+{
+	g_return_val_if_fail(PURPLE_IS_LOG(log), 0);
 
-			if (tmp != NULL)
-				log->tm->tm_zone = tmp;
-			else
-				/* Just shove the UTF-8 bytes in and hope... */
-				log->tm->tm_zone = g_strdup(log->tm->tm_zone);
-		}
-#endif
-	}
+	return PURPLE_LOG_GET_PRIVATE(log)->time;
+}
 
-	// TODO: Non-blocking create?
-	if (log->logger && log->logger->create)
-		(log->logger->create)(log);
+PurpleConversation *
+purple_log_get_conversation(const PurpleLog *log)
+{
+	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
 
-	return log;
+	return PURPLE_LOG_GET_PRIVATE(log)->conv;
 }
 
-static PurpleLog *
-purple_log_ref(PurpleLog *log)
+gpointer
+purple_log_get_logger_data(const PurpleLog *log)
 {
-	guint ref_count;
-	gpointer value;
+	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
 
-	G_LOCK(log_ref_table);
+	return PURPLE_LOG_GET_PRIVATE(log)->logger_data;
+}
 
-	if (g_hash_table_lookup_extended(log_ref_table, log, NULL, &value)) {
-		ref_count = GPOINTER_TO_UINT(value);
-		ref_count++;
-	} else
-		ref_count = 1;
+G_CONST_RETURN struct tm *
+purple_log_get_tm(const PurpleLog *log)
+{
+	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
 
-	g_hash_table_insert(log_ref_table, log, GUINT_TO_POINTER(ref_count));
+	return PURPLE_LOG_GET_PRIVATE(log)->tm;
+}
 
-	G_UNLOCK(log_ref_table);
+static void
+purple_logger_lister_callback_data_free(gpointer userdata)
+{
+	_purple_logger_lister_callback_data *callback_data = userdata;
 
-	return log;
+	g_free(callback_data->name);
+	g_free(callback_data->ext);
+	g_free(callback_data);
 }
 
 static void
-purple_log_unref(PurpleLog *log)
+thread_callback_data_free(gpointer userdata)
 {
-	guint ref_count;
-	gpointer value;
+	_thread_callback_data *callback_data = userdata;
 
-	G_LOCK(log_ref_table);
+	// if (callback_data->account != NULL)
+		// g_object_unref(callback_data->account);
 
-	if (g_hash_table_lookup_extended(log_ref_table, log, NULL, &value)) {
-		ref_count = GPOINTER_TO_UINT(value);
-		ref_count--;
-		g_hash_table_insert(log_ref_table, log, GUINT_TO_POINTER(ref_count));
-	} else
-		// Spit out an error?
-		ref_count = 0;
+	g_free(callback_data->name);
+	g_free(callback_data->ext);
+	g_free(callback_data);
+}
 
-	G_UNLOCK(log_ref_table);
+static void
+thread_write_callback_data_free(gpointer userdata)
+{
+	_thread_write_callback_data *callback_data = userdata;
 
-	if (ref_count < 1) {
-		if (log->logger->async->finalize_async != NULL)
-			log->logger->async->finalize_async(log, G_PRIORITY_LOW, NULL, log_free_cb, log);
-		else if (log->logger->finalize != NULL) {
-			GSimpleAsyncResult *simple;
+	g_free(callback_data->from);
+	g_free(callback_data->message);
+	g_free(callback_data);
+}
 
-			log->logger->finalize(log);
+static void
+purple_logger_total_size_callback_data_free(gpointer userdata)
+{
+	_purple_logger_total_size_callback_data *callback_data = userdata;
 
-			simple = g_simple_async_result_new(NULL, log_free_cb, log, purple_log_free);
-			g_simple_async_result_complete_in_idle(simple);
-			g_object_unref(simple);
-		}
-	}
+	g_free(callback_data->name);
+	g_free(callback_data->ext);
+	g_free(callback_data);
 }
 
-void
-purple_log_free(PurpleLog *log)
+
+/**************************************************************************
+ * PUBLIC LOGGING FUNCTIONS ***********************************************
+ **************************************************************************/
+
+PurpleLog *
+purple_log_new(GType log_type, PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account,
+	PurpleConversation *conv, time_t time, const struct tm *tm)
 {
-	g_return_if_fail(log != NULL);
+	PurpleLog *log;
 
-	purple_log_unref(log);
+	g_return_val_if_fail(log_type == G_TYPE_INVALID || g_type_is_a(log_type, PURPLE_TYPE_LOG), NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+	g_return_val_if_fail(account != NULL, NULL);
+
+	if (log_type == G_TYPE_INVALID)
+		log_type = current_logger;
+
+	log = g_object_new(log_type,
+		"type", chat_type,
+		"name", g_strdup(purple_normalize(account, name)),
+		"account", account,
+		"conv", conv,
+		"time", time,
+		"tm", tm,
+		NULL);
+
+	PURPLE_DBUS_REGISTER_POINTER(log, PurpleLog);
+
+	// if (tm == NULL)
+		// log->tm = NULL;
+	// else {
+		// /* There's no need to zero this as we immediately do a direct copy. */
+		// log->tm = g_slice_new(struct tm);
+
+		// *(log->tm) = *tm;
+
+// #ifdef HAVE_STRUCT_TM_TM_ZONE
+		// /* XXX: This is so wrong... */
+		// if (log->tm->tm_zone != NULL) {
+			// gchar *tmp = g_locale_from_utf8(log->tm->tm_zone, -1, NULL, NULL, NULL);
+
+			// if (tmp != NULL)
+				// log->tm->tm_zone = tmp;
+			// else
+				// /* Just shove the UTF-8 bytes in and hope... */
+				// log->tm->tm_zone = g_strdup(log->tm->tm_zone);
+		// }
+// #endif
+	// }
+
+	// TODO: Non-blocking create?
+	// if (log->logger->create)
+		// (log->logger->create_fn)(log);
+
+	return log;
 }
 
-void
-purple_log_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
-	const gchar *message)
+static void
+write_update_size_cache(PurpleLog *log, gssize size)
 {
 	_purple_logsize_user *lu;
 	gpointer ptrsize;
-	gsize written, total;
+	gssize total;
 
-	g_return_if_fail(from != NULL);
-	g_return_if_fail(message != NULL);
-	g_return_if_fail(log != NULL);
-	g_return_if_fail(log->logger != NULL);
-	g_return_if_fail(log->logger->write != NULL);
+	if (size <= 0)
+		return;
 
-	written = (log->logger->write)(log, type, from, time, message);
-
 	lu = g_new(_purple_logsize_user, 1);
 	total = 0;
 
-	lu->name = g_strdup(purple_normalize(log->account, log->name));
-	lu->account = log->account;
+	lu->name = g_strdup(purple_normalize(purple_log_get_account(log), purple_log_get_name(log)));
+	lu->account = purple_log_get_account(log);
 
 	G_LOCK(logsize_users);
 	if(g_hash_table_lookup_extended(logsize_users, lu, NULL, &ptrsize)) {
 		gchar *tmp = lu->name;
 
 		total = GPOINTER_TO_INT(ptrsize);
-		total += written;
+		total += size;
 		g_hash_table_replace(logsize_users, lu, GINT_TO_POINTER(total));
-		purple_debug_info("log", "HASH(purple_log_write): total size %i\n", total);
+		// purple_debug_info("log", "HASH(purple_log_write): total size %i\n", total);
 
 		/* The hash table takes ownership of lu, so create a new one
 		 * for the logsize_users_decayed check below. */
 		lu = g_new(_purple_logsize_user, 1);
 		lu->name = g_strdup(tmp);
-		lu->account = log->account;
+		lu->account = purple_log_get_account(log);
 	}
 	G_UNLOCK(logsize_users);
 
 	G_LOCK(logsize_users_decayed);
 	if(g_hash_table_lookup_extended(logsize_users_decayed, lu, NULL, &ptrsize)) {
 		total = GPOINTER_TO_INT(ptrsize);
-		total += written;
+		total += size;
 		g_hash_table_replace(logsize_users_decayed, lu, GINT_TO_POINTER(total));
 	} else {
 		g_free(lu->name);
@@ -481,204 +674,393 @@ purple_log_write(PurpleLog *log, PurpleM
 	G_UNLOCK(logsize_users_decayed);
 }
 
+gssize
+purple_log_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time, const gchar *message, GCancellable *cancellable, GError **error)
+{
+	PurpleLogClass *class;
+	gssize size;
+
+	g_return_val_if_fail(PURPLE_IS_LOG(log), -1);
+	g_return_val_if_fail(from != NULL, -1);
+	g_return_val_if_fail(message != NULL, -1);
+
+	class = PURPLE_LOG_GET_CLASS(log);
+
+	if (class->write_fn == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported"));
+
+		return -1;
+	}
+
+	size = class->write_fn(log, type, from, time, message, cancellable, error);
+	write_update_size_cache(log, size);
+
+	return size;
+}
+
+static void
+write_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
+{
+	_thread_write_callback_data *callback_data =
+		g_simple_async_result_get_op_res_gpointer(simple);
+	PurpleLog *log = PURPLE_LOG(object);
+	PurpleMessageFlags flags = callback_data->flags;
+	GError *error = NULL;
+	gchar *from = callback_data->from, *message = callback_data->message;
+	gssize size;
+	time_t time = callback_data->time;
+
+	size = purple_log_write(log, flags, from, time, message, cancellable, &error);
+
+	if (size < 0)
+		g_simple_async_result_set_from_error(simple, error);
+	else {
+		write_update_size_cache(log, size);
+		g_simple_async_result_set_op_res_gssize(simple, size);
+	}
+
+	g_clear_error(&error);
+}
+
+static void
+purple_log_real_write_async(PurpleLog *log, PurpleMessageFlags flags, const gchar *from, time_t time, const gchar *message, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	_thread_write_callback_data *callback_data;
+	GSimpleAsyncResult *simple;
+
+	callback_data = g_new0(_thread_write_callback_data, 1);
+	callback_data->flags = flags;
+	callback_data->from = g_strdup(from);
+	callback_data->message = g_strdup(message);
+	callback_data->time = time;
+
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata,
+		purple_log_real_write_async);
+
+	g_simple_async_result_set_op_res_gpointer(simple, callback_data, thread_write_callback_data_free);
+	g_simple_async_result_run_in_thread(simple, write_thread, io_priority, cancellable);
+	g_object_unref(simple);
+}
+
 void
 purple_log_write_async(PurpleLog *log, PurpleMessageFlags type, const gchar *from,
 	time_t time, const gchar *message, gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata)
 {
-	_purple_log_write_callback_data *callback_data;
-	_purple_logsize_user *lu;
-	GSimpleAsyncResult *simple;
-	gssize size;
-
 	g_return_if_fail(message != NULL);
 	g_return_if_fail(from != NULL);
-	g_return_if_fail(log != NULL);
-	g_return_if_fail(log->logger != NULL);
+	g_return_if_fail(PURPLE_IS_LOG(log));
 
-	lu = g_new(_purple_logsize_user, 1);
+	PURPLE_LOG_GET_CLASS(log)->write_async(log,
+		type, from, time, message, io_priority,
+		cancellable, cb, userdata);
+}
 
-	lu->name = g_strdup(purple_normalize(log->account, log->name));
-	lu->account = log->account;
+static gssize
+purple_log_real_write_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+	return g_simple_async_result_get_op_res_gssize(G_SIMPLE_ASYNC_RESULT(res));
+}
 
-	callback_data = g_new0(_purple_log_write_callback_data, 1);
-	callback_data->cb = cb;
-	callback_data->userdata = userdata;
-	callback_data->lu = lu;
-	callback_data->log = log;
+gssize
+purple_log_write_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+	g_return_val_if_fail(PURPLE_IS_LOG(log), -1);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(res), -1);
 
-	purple_log_ref(log);
+	if (G_IS_SIMPLE_ASYNC_RESULT(res)) {
+		GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
 
-	if (log->logger->async->write_async)
-		(log->logger->async->write_async)(log, type, from,
-			time, message, io_priority, cancellable, log_write_cb, callback_data);
-	else if (log->logger->write) {
-		size = (log->logger->write)(log, type, from, time, message);
+		if (g_simple_async_result_propagate_error(simple, error))
+			return -1;
+	}
 
-		simple = g_simple_async_result_new(NULL, log_write_cb, callback_data,
-			purple_log_write_async);
+	return PURPLE_LOG_GET_CLASS(log)->write_finish(log, res, error);
+}
 
-		g_simple_async_result_set_op_res_gssize(simple, size);
-		g_simple_async_result_complete_in_idle(simple);
+gboolean
+purple_log_create(PurpleLog *log, GCancellable *cancellable, GError **error)
+{
+	PurpleLogClass *class;
 
-		g_object_unref(simple);
-	} else {
-		simple = g_simple_async_result_new_error(NULL, log_write_cb, callback_data,
-			G_IO_ERROR, G_IO_ERROR_FAILED, _("The logger has no write function"));
+	g_return_val_if_fail(PURPLE_IS_LOG(log), FALSE);
 
-		g_simple_async_result_complete_in_idle(simple);
+	class = PURPLE_LOG_GET_CLASS(log);
 
-		g_object_unref(simple);
+	if (class->create_fn == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported"));
+
+		return FALSE;
 	}
+
+	return class->create_fn(log, cancellable, error);
 }
 
-gssize
-purple_log_write_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+static void
+create_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
 {
+	PurpleLog *log = PURPLE_LOG(object);
+	GError *error = NULL;
+	gboolean result;
+
+	result = purple_log_create(log, cancellable, &error);
+
+	if (!result)
+		g_simple_async_result_set_from_error(simple, error);
+	else
+		g_simple_async_result_set_op_res_gboolean(simple, TRUE);
+
+	g_clear_error(&error);
+}
+
+static void
+purple_log_real_create_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
 	GSimpleAsyncResult *simple;
 
-	simple = G_SIMPLE_ASYNC_RESULT(res);
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata, purple_log_real_create_async);
 
-	if (g_simple_async_result_propagate_error(simple, error))
-		return -1;
+	g_simple_async_result_run_in_thread(simple, create_thread, io_priority, cancellable);
+	g_object_unref(simple);
+}
 
-	g_return_val_if_fail(g_simple_async_result_get_source_tag(simple) == purple_log_write_async, -1);
+void
+purple_log_create_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	g_return_if_fail(PURPLE_IS_LOG(log));
 
-	return g_simple_async_result_get_op_res_gssize(simple);
+	PURPLE_LOG_GET_CLASS(log)->create_async(log, io_priority, cancellable, cb, userdata);
 }
 
+static gboolean
+purple_log_real_create_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+	return g_simple_async_result_get_op_res_gboolean(G_SIMPLE_ASYNC_RESULT(res));
+}
+
+gboolean
+purple_log_create_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+	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)->create_finish(log, res, error);
+}
+
 gchar *
-purple_log_read(PurpleLog *log, PurpleLogReadFlags *flags)
+purple_log_read(PurpleLog *log, PurpleLogReadFlags *flags, GCancellable *cancellable, GError **error)
 {
-	gchar *ret;
+	PurpleLogClass *class;
 
-	g_return_val_if_fail(log != NULL, NULL);
-	g_return_val_if_fail(log->logger != NULL, NULL);
+	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
 
-	if (log->logger->read) {
-		ret = (log->logger->read)(log, flags);
-		purple_str_strip_char(ret, '\r');
+	class = PURPLE_LOG_GET_CLASS(log);
 
-		return ret;
+	if (class->read_fn == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported"));
+
+		return NULL;
 	}
 
-	return g_strdup_printf("<b><font color=\"red\">%s</font></b>",
-		_("The logger has no read function"));
+	return class->read_fn(log, flags, cancellable, error);
 }
 
-void
-purple_log_read_async(PurpleLog *log, PurpleLogReadFlags *flags, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+static void
+log_read_res_free(gpointer data)
 {
-	_purple_log_read_callback_data *callback_data;
-	GSimpleAsyncResult *simple;
-	gchar *read;
+	_purple_log_read_res_callback_data *res_data = data;
 
-	g_return_if_fail(log != NULL);
-	g_return_if_fail(log->logger != NULL);
+	g_free(res_data->text);
+	g_free(res_data);
+}
 
-	callback_data = g_new0(_purple_log_read_callback_data, 1);
-	callback_data->cb = cb;
-	callback_data->userdata = userdata;
-	callback_data->log = log;
+static void
+read_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
+{
+	PurpleLog *log = PURPLE_LOG(object);
+	PurpleLogClass *class = PURPLE_LOG_GET_CLASS(log);
+	PurpleLogReadFlags flags;
+	GError *error = NULL;
+	gchar *text;
 
-	purple_log_ref(log);
+	if (class->read_fn == NULL) {
+		g_simple_async_result_set_error(simple, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported"));
 
-	if (log->logger->async->read_async)
-		(log->logger->async->read_async)(log, flags,
-			io_priority, cancellable, log_read_cb, callback_data);
-	else if (log->logger->read) {
-		read = (log->logger->read)(log, flags);
+		return;
+	}
 
-		simple = g_simple_async_result_new(NULL, log_read_cb, callback_data,
-			purple_log_read_async);
+	text = class->read_fn(log, &flags, cancellable, &error);
 
-		g_simple_async_result_set_op_res_gpointer(simple, read, g_free);
-		g_simple_async_result_complete_in_idle(simple);
+	if (text == NULL)
+		g_simple_async_result_set_from_error(simple, error);
+	else {
+		_purple_log_read_res_callback_data *res_data;
 
-		g_object_unref(simple);
-	} else {
-		simple = g_simple_async_result_new_error(NULL, log_read_cb, callback_data,
-			G_IO_ERROR, G_IO_ERROR_FAILED, "<font color=\"red\"><b>%s</b></font>",
-			_("The logger has no read function"));
+		res_data = g_new0(_purple_log_read_res_callback_data, 1);
+		res_data->flags = flags;
+		res_data->text = text;
 
-		g_simple_async_result_complete_in_idle(simple);
+		g_simple_async_result_set_op_res_gpointer(simple, res_data, log_read_res_free);
+	}
 
-		g_object_unref(simple);
-	}
+	g_clear_error(&error);
 }
 
-gchar *
-purple_log_read_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+static void
+purple_log_real_read_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
 {
 	GSimpleAsyncResult *simple;
 
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata,
+		purple_log_real_read_async);
+
+	g_simple_async_result_run_in_thread(simple, read_thread, io_priority, cancellable);
+	g_object_unref(simple);
+}
+
+void
+purple_log_read_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	g_return_if_fail(PURPLE_IS_LOG(log));
+
+	PURPLE_LOG_GET_CLASS(log)->read_async(log,
+		io_priority, cancellable, cb, userdata);
+}
+
+static gchar *
+purple_log_real_read_finish(PurpleLog *log, GAsyncResult *res, PurpleLogReadFlags *flags, GError **error)
+{
+	_purple_log_read_res_callback_data *res_data;
+	GSimpleAsyncResult *simple;
+
 	simple = G_SIMPLE_ASYNC_RESULT(res);
+	res_data = g_simple_async_result_get_op_res_gpointer(simple);
 
-	if (g_simple_async_result_propagate_error(simple, error))
-		return NULL;
+	if (flags != NULL)
+		*flags = res_data->flags;
 
-	g_return_val_if_fail(g_simple_async_result_get_source_tag(simple) == purple_log_read_async, NULL);
+	return res_data->text;
+}
 
-	return g_simple_async_result_get_op_res_gpointer(simple);
+gchar *
+purple_log_read_finish(PurpleLog *log, GAsyncResult *res, PurpleLogReadFlags *flags, GError **error)
+{
+	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(res), NULL);
+
+	if (G_IS_SIMPLE_ASYNC_RESULT(res)) {
+		GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
+
+		if (g_simple_async_result_propagate_error(simple, error))
+			return NULL;
+	}
+
+	return PURPLE_LOG_GET_CLASS(log)->read_finish(log, res, flags, error);
 }
 
-gint
-purple_log_get_size(PurpleLog *log)
+gssize
+purple_log_get_size(PurpleLog *log, GCancellable *cancellable, GError **error)
 {
-	g_return_val_if_fail(log != NULL, 0);
-	g_return_val_if_fail(log->logger != NULL, 0);
+	PurpleLogClass *class;
 
-	if (log->logger->size)
-		return (log->logger->size)(log);
+	g_return_val_if_fail(PURPLE_IS_LOG(log), 0);
 
-	return 0;
+	class = PURPLE_LOG_GET_CLASS(log);
+
+	if (class->size_fn == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported"));
+
+		return -1;
+	}
+
+	return class->size_fn(log, cancellable, error);
 }
 
-void
-purple_log_get_size_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
-	GAsyncReadyCallback cb, gpointer userdata)
+static void
+size_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
 {
-	_purple_log_size_callback_data *callback_data;
-	GSimpleAsyncResult *simple;
+	PurpleLog *log = PURPLE_LOG(object);
+	PurpleLogClass *class = PURPLE_LOG_GET_CLASS(log);
+	GError *error = NULL;
+	gssize size;
 
-	g_return_if_fail(log != NULL);
-	g_return_if_fail(log->logger != NULL);
+	if (class->size_fn == NULL) {
+		g_simple_async_result_set_error(simple, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported"));
 
-	callback_data = g_new0(_purple_log_size_callback_data, 1);
-	callback_data->cb = cb;
-	callback_data->userdata = userdata;
-	callback_data->log = log;
+		return;
+	}
 
-	purple_log_ref(log);
+	size = class->size_fn(log, cancellable, &error);
 
-	if (log->logger->async->size_async)
-		(log->logger->async->size_async)(log, io_priority, cancellable,
-			log_size_cb, callback_data);
-	else if (log->logger->size) {
-		gssize size = (log->logger->size)(log);
+	if (size < 0)
+		g_simple_async_result_set_from_error(simple, error);
+	else
+		g_simple_async_result_set_op_res_gssize(simple, size);
 
-		simple = g_simple_async_result_new(NULL, log_size_cb, callback_data,
-			purple_log_get_size_async);
+	g_clear_error(&error);
+}
 
-		g_simple_async_result_set_op_res_gssize(simple, size);
-		g_simple_async_result_complete_in_idle(simple);
+static void
+purple_log_real_size_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	GSimpleAsyncResult *simple;
 
-		g_object_unref(simple);
-	} else {
-		simple = g_simple_async_result_new_error(NULL, log_size_cb, callback_data,
-			G_IO_ERROR, G_IO_ERROR_FAILED, _("Logger has no get-size function"));
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata,
+		purple_log_real_size_async);
 
-		g_simple_async_result_complete_in_idle(simple);
+	g_simple_async_result_run_in_thread(simple, size_thread, io_priority, cancellable);
+	g_object_unref(simple);
+}
 
-		g_object_unref(simple);
-	}
+void
+purple_log_get_size_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	g_return_if_fail(PURPLE_IS_LOG(log));
+
+	PURPLE_LOG_GET_CLASS(log)->size_async(log, io_priority, cancellable, cb, userdata);
 }
 
+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));
+}
+
 gssize
 purple_log_get_size_finish(PurpleLog *log, GAsyncResult *res, GError **error)
 {
-	return purple_log_common_sizer_finish(log, res, error);
+	g_return_val_if_fail(PURPLE_IS_LOG(log), -1);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(res), -1);
+
+	if (G_IS_SIMPLE_ASYNC_RESULT(res)) {
+		GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
+
+		if (g_simple_async_result_propagate_error(simple, error))
+			return -1;
+	}
+
+	return PURPLE_LOG_GET_CLASS(log)->size_finish(log, res, error);
 }
 
 static guint
@@ -700,14 +1082,13 @@ _purple_logsize_user_free_key(_purple_lo
 	g_free(lu);
 }
 
-gint
-purple_log_get_total_size(PurpleLogType type, const gchar *name, PurpleAccount *account)
+gssize
+purple_logs_get_total_size(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account, GCancellable *cancellable, GError **error)
 {
 	_purple_logsize_user *lu;
-	GSList *n;
 	gpointer ptrsize;
 	gboolean result;
-	gint size = 0;
+	gssize size = 0, total = 0;
 
 	g_return_val_if_fail(name != NULL, 0);
 	g_return_val_if_fail(account != NULL, 0);
@@ -720,41 +1101,147 @@ purple_log_get_total_size(PurpleLogType 
 	result = g_hash_table_lookup_extended(logsize_users, lu, NULL, &ptrsize);
 	G_UNLOCK(logsize_users);
 
-	if(result) {
-		size = GPOINTER_TO_INT(ptrsize);
-		purple_debug_info("log", "HASH(purple_log_get_total_size): using size from hash %i {name = \"%s\"\n}", size, name);
+	if (result) {
+		total = GPOINTER_TO_INT(ptrsize);
+		// purple_debug_info("log", "HASH(purple_log_get_total_size): using size from hash %i {name = \"%s\"\n}", size, name);
 
 		g_free(lu->name);
 		g_free(lu);
 	} else {
-		for (n = purple_log_logger_get_all(); n; n = g_slist_next(n)) {
-			PurpleLogLogger *logger = n->data;
+		GArray *array = purple_log_logger_get_all();
+		guint i;
 
-			if(logger->total_size){
-				size += (logger->total_size)(type, name, account);
-			} else if(logger->list) {
-				GList *logs = (logger->list)(type, name, account);
+		for (i = 0; i < array->len; i++) {
+			PurpleLogClass *class;
+			GType log_type = g_array_index(array, GType, i);
 
+			class = g_type_class_peek(log_type);
+
+			if (class->total_size_fn){
+				size = class->total_size_fn(chat_type, name, account, cancellable, error);
+
+				if (size < 0)
+					return -1;
+
+				total += size;
+			} else {
+				GList *logs = purple_logs_get_logs(chat_type, name, account, cancellable, error);
+
+				if (logs == NULL)
+					return -1;
+
 				for ( ; logs != NULL; logs = g_list_delete_link(logs, logs)) {
 					PurpleLog *log = logs->data;
-					size += purple_log_get_size(log);
-					purple_log_free(log);
+
+					size = purple_log_get_size(log, cancellable, error);
+
+					if (size < 0) {
+						for ( ; logs != NULL; logs = g_list_delete_link(logs, logs))
+							g_object_unref(logs->data);
+
+						return -1;
+					}
+
+					total += size;
+					g_object_unref(log);
 				}
 			}
 		}
 
-		purple_debug_info("log", "HASH(purple_log_get_total_size): write size to hash %i {name = \"%s\"\n}", size, name);
+		// purple_debug_info("log", "HASH(purple_log_get_total_size): write size to hash %i {name = \"%s\"\n}", total, name);
 
 		G_LOCK(logsize_users);
-		g_hash_table_replace(logsize_users, lu, GINT_TO_POINTER(size));
+		g_hash_table_replace(logsize_users, lu, GINT_TO_POINTER(total));
 		G_LOCK(logsize_users);
 	}
 
-	return size;
+	return total;
 }
 
+static void
+total_size_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
+{
+	_thread_callback_data *callback_data =
+		g_simple_async_result_get_op_res_gpointer(simple);
+	PurpleAccount *account = callback_data->account; // g_object_unref
+	PurpleLog *log = PURPLE_LOG(object);
+	PurpleLogClass *class = PURPLE_LOG_GET_CLASS(log);
+	PurpleLogChatType chat_type = callback_data->chat_type;
+	GError *error = NULL;
+	gchar *name = callback_data->name;
+	gssize size;
+
+	if (class->total_size_fn != NULL) {
+		size = class->total_size_fn(chat_type, name, account, cancellable, &error);
+
+		if (size < 0)
+			g_simple_async_result_set_from_error(simple, error);
+		else
+			g_simple_async_result_set_op_res_gssize(simple, size);
+
+		g_clear_error(&error);
+	} else if (class->list_fn != NULL) {
+		/* List the logs and manually size them all up */
+		GList *logs = purple_logs_get_logs(chat_type, name, account, cancellable, &error);
+		gssize total;
+
+		if (logs == NULL) {
+			g_simple_async_result_set_from_error(simple, error);
+			return;
+		}
+
+		g_clear_error(&error);
+
+		for (total = 0; logs != NULL; logs = g_list_delete_link(logs, logs)) {
+			PurpleLog *log_to_size = logs->data;
+
+			size = purple_log_get_size(log_to_size, cancellable, &error);
+
+			if (size < 0) {
+				g_simple_async_result_set_from_error(simple, error);
+
+				for ( ; logs != NULL; logs = g_list_delete_link(logs, logs))
+					g_object_unref(logs->data);
+
+				return;
+			}
+
+			total += size;
+
+			g_object_unref(log_to_size);
+			g_clear_error(&error);
+		}
+
+		g_simple_async_result_set_op_res_gssize(simple, total);
+	} else {
+		g_simple_async_result_set_error(simple, G_IO_ERROR,
+			G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
+
+		return;
+	}
+}
+
+static void
+purple_log_real_total_size_async(PurpleLog *log, PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	_thread_callback_data *callback_data;
+	GSimpleAsyncResult *simple;
+
+	callback_data = g_new0(_thread_callback_data, 1);
+	callback_data->chat_type = chat_type;
+	callback_data->name = g_strdup(name);
+	callback_data->account = account; // g_object_ref
+
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata,
+		purple_log_real_total_size_async);
+
+	g_simple_async_result_set_op_res_gpointer(simple, callback_data, thread_callback_data_free);
+	g_simple_async_result_run_in_thread(simple, total_size_thread, io_priority, cancellable);
+	g_object_unref(simple);
+}
+
 // TODO: Would it make sense to allow the caller to pass in a list of logs, if
-// TODO: it just got them from purple_log_get_logs_async()?  Pidgin asks
+// TODO: it just got them from purple_logs_get_logs_async()?  Pidgin asks
 // TODO: for the total size, which means that for some loggers, we end up
 // TODO: calling list *again* needlessly (to loop over them and size them).
 // TODO: If we had a list of logs, we could loop over them and find those
@@ -765,16 +1252,16 @@ void
 // TODO: 	if (!log->logger->total_size && log->logger->size)
 // TODO: 		Call the size function.
 void
-purple_log_get_total_size_async(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+purple_logs_get_total_size_async(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
 {
 	_purple_log_total_size_callback_data *callback_data;
 	_purple_logsize_user *lu;
 	GSimpleAsyncResult *simple;
-	GSList *n;
+	GArray *array;
 	gpointer ptrsize;
 	gboolean result;
 	gssize size = 0;
+	guint i;
 
 	g_return_if_fail(name != NULL);
 	g_return_if_fail(account != NULL);
@@ -786,12 +1273,12 @@ purple_log_get_total_size_async(PurpleLo
 	callback_data = g_new0(_purple_log_total_size_callback_data, 1);
 	callback_data->cb = cb;
 	callback_data->userdata = userdata;
-	callback_data->total = 0;
 
 	G_LOCK(logsize_users);
 	result = g_hash_table_lookup_extended(logsize_users, lu, NULL, &ptrsize);
 	G_UNLOCK(logsize_users);
 
+	// Fix this up?
 	if (result) {
 		size = GPOINTER_TO_INT(ptrsize);
 
@@ -800,7 +1287,7 @@ purple_log_get_total_size_async(PurpleLo
 
 		callback_data->counter = 1;
 
-		simple = g_simple_async_result_new(NULL, log_total_size_list_cb,
+		simple = g_simple_async_result_new(NULL, log_total_size_cb,
 			callback_data, purple_log_common_total_sizer_async);
 
 		g_simple_async_result_set_op_res_gssize(simple, size);
@@ -814,75 +1301,53 @@ purple_log_get_total_size_async(PurpleLo
 	g_free(lu->name);
 	g_free(lu);
 
-	n = purple_log_logger_get_all();
-	callback_data->counter = g_slist_length(n);
-	callback_data->cancellable = g_object_ref(cancellable);
-	callback_data->io_priority = io_priority;
+	array = purple_log_logger_get_all();
+	callback_data->counter = array->len;
+	callback_data->total = 0;
 
-	for ( ; n; n = g_slist_next(n)) {
-		PurpleLogLogger *logger = n->data;
+	for (i = 0; i < array->len; i++) {
+		PurpleLog *log;
+		GType log_type = g_array_index(array, GType, i);
 
-		if (logger->async->total_size_async)
-			(logger->async->total_size_async)(type, name, account, io_priority,
-				cancellable, log_total_size_cb, callback_data);
-		else if (logger->async->list_async)
-			/* List the logs and manually size them all up */
-			(logger->async->list_async)(type, name, account, io_priority,
-				cancellable, log_total_size_list_cb, callback_data);
-		else if (logger->total_size) {
-			size = (logger->total_size)(type, name, account);
+		log = g_object_new(log_type, NULL);
 
-			simple = g_simple_async_result_new(NULL, log_total_size_cb, callback_data,
-				purple_log_common_total_sizer_async);
+		PURPLE_LOG_GET_CLASS(log)->total_size_async(log, chat_type, name, account, io_priority, cancellable, log_total_size_cb, callback_data);
+	}
+}
 
-			g_simple_async_result_set_op_res_gssize(simple, size);
-			g_simple_async_result_complete_in_idle(simple);
+static gssize
+purple_log_real_total_size_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+	return g_simple_async_result_get_op_res_gssize(G_SIMPLE_ASYNC_RESULT(res));
+}
 
-			g_object_unref(simple);
-		} else if (logger->list) {
-			/* List the logs and manually size them all up */
-			GList *logs = (logger->list)(type, name, account);
+gssize
+purple_log_get_total_size_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+	g_return_val_if_fail(PURPLE_IS_LOG(log), -1);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(res), -1);
 
-			simple = g_simple_async_result_new(NULL, log_total_size_list_cb, callback_data,
-				purple_log_common_lister_async);
+	if (G_IS_SIMPLE_ASYNC_RESULT(res)) {
+		GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
 
-			g_simple_async_result_set_op_res_gpointer(simple, logs, (GDestroyNotify) g_list_free);
-			g_simple_async_result_complete_in_idle(simple);
-
-			g_object_unref(simple);
-		} else {
-			simple = g_simple_async_result_new_error(NULL, log_total_size_list_cb, callback_data,
-				G_IO_ERROR, G_IO_ERROR_FAILED, _("Logger has no get-size or list function"));
-
-			g_simple_async_result_complete_in_idle(simple);
-
-			g_object_unref(simple);
-		}
+		if (g_simple_async_result_propagate_error(simple, error))
+			return -1;
 	}
-}
 
-gssize
-purple_log_get_total_size_finish(GAsyncResult *res, GError **error)
-{
-	return purple_log_common_total_sizer_finish(res, &err);
+	return PURPLE_LOG_GET_CLASS(log)->total_size_finish(log, res, error);
 }
 
-gint
-purple_log_get_activity_score(PurpleLogType type, const gchar *name, PurpleAccount *account)
+gssize
+purple_log_get_activity_score(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account, GCancellable *cancellable, GError **error)
 {
 	_purple_logsize_user *lu;
-	PurpleLogLogger *logger;
-	PurpleLog *log;
-	GList *logs;
-	GSList *n;
 	gpointer ptrscore;
 	time_t now;
 	gboolean result;
-	gdouble score_double;
 	gint score;
 
-	g_return_val_if_fail(name != NULL, 0);
-	g_return_val_if_fail(account != NULL, 0);
+	g_return_val_if_fail(name != NULL, -1);
+	g_return_val_if_fail(account != NULL, -1); //PURPLE_IS_ACCOUNT(account)
 
 	time(&now);
 
@@ -899,23 +1364,37 @@ purple_log_get_activity_score(PurpleLogT
 		g_free(lu->name);
 		g_free(lu);
 	} else {
-		score_double = 0.0;
+		GArray *array = purple_log_logger_get_all();
+		gdouble score_double = 0.0;
+		guint i;
 
-		for (n = purple_log_logger_get_all(); n; n = g_slist_next(n)) {
-			logger = n->data;
+		for (i = 0; i < array->len; i++) {
+			GList *logs;
 
-			if(logger->list) {
-				logs = (logger->list)(type, name, account);
+			logs = purple_logs_get_logs(chat_type, name, account, cancellable, error);
 
-				while (logs) {
-					log = logs->data;
-					/* Activity score counts bytes in the log, exponentially
-					   decayed with a half-life of 14 days. */
-					score_double += purple_log_get_size(log) *
-						pow(0.5, difftime(now, log->time)/1209600.0);
-					purple_log_free(log);
-					logs = g_list_delete_link(logs, logs);
+			if (logs == NULL)
+				return -1;
+
+			for ( ; logs != NULL; logs = g_list_delete_link(logs, logs)) {
+				PurpleLog *log = PURPLE_LOG(logs->data);
+				gssize size;
+				time_t log_time;
+
+				size = purple_log_get_size(log, cancellable, error);
+
+				if (size < 0) {
+					for ( ; logs != NULL; logs = g_list_delete_link(logs, logs))
+						g_object_unref(logs->data);
+
+					return -1;
 				}
+
+				/* Activity score counts bytes in the log, exponentially
+				   decayed with a half-life of 14 days. */
+				log_time = purple_log_get_time(log);
+				score_double += size * pow(0.5, difftime(now, log_time) / 1209600.0);
+				g_object_unref(log);
 			}
 		}
 
@@ -929,57 +1408,45 @@ purple_log_get_activity_score(PurpleLogT
 	return score;
 }
 
-void
-purple_log_get_activity_score_async(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+static void
+activity_score_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
 {
-	_purple_log_activity_score_data *callback_data;
-	_purple_logsize_user *lu;
-	GSimpleAsyncResult *simple;
-	gpointer ptrscore;
-	gint score;
-	gboolean result;
+	_thread_callback_data *callback_data =
+		g_simple_async_result_get_op_res_gpointer(simple);
+	PurpleAccount *account = callback_data->account;
+	PurpleLogChatType chat_type = callback_data->chat_type;
+	GError *error = NULL;
+	gchar *name = callback_data->name;
+	gssize score;
 
-	g_return_if_fail(name != NULL);
-	g_return_if_fail(account != NULL);
+	score = purple_log_get_activity_score(chat_type, name, account, cancellable, &error);
 
-	lu = g_new(_purple_logsize_user, 1);
-	lu->name = g_strdup(purple_normalize(account, name));
-	lu->account = account; //g_object_ref?
-
-	G_LOCK(logsize_users_decayed);
-	result = g_hash_table_lookup_extended(logsize_users_decayed, lu, NULL, &ptrscore);
-	G_UNLOCK(logsize_users_decayed);
-
-	if (result) {
-		score = GPOINTER_TO_INT(ptrscore);
-
-		g_free(lu->name);
-		g_free(lu);
-
-		simple = g_simple_async_result_new(NULL, cb,
-			userdata, purple_log_get_activity_score_async);
-
+	if (score < 0)
+		g_simple_async_result_set_from_error(simple, error);
+	else
 		g_simple_async_result_set_op_res_gssize(simple, score);
-		g_simple_async_result_complete_in_idle(simple);
 
-		g_object_unref(simple);
+	g_clear_error(&error);
+}
 
-		return;
-	}
+void
+purple_log_get_activity_score_async(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account,
+	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	_thread_callback_data *callback_data;
+	GSimpleAsyncResult *simple;
 
-	callback_data = g_new0(_purple_log_activity_score_data, 1);
-	callback_data->cb = cb;
-	callback_data->userdata = userdata;
-	callback_data->lu = lu;
-	callback_data->cancel = cancellable;
-	callback_data->io_priority = io_priority;
+	callback_data = g_new0(_thread_callback_data, 1);
+	callback_data->chat_type = chat_type;
+	callback_data->name = g_strdup(name);
+	callback_data->account = account; // g_object_ref
 
-	if (cancellable != NULL)
-		g_object_ref(cancellable);
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata,
+		purple_log_get_activity_score_async);
 
-	purple_log_get_logs_async(type, name, account, io_priority, cancellable,
-		log_get_activity_score_cb, callback_data);
+	g_simple_async_result_set_op_res_gpointer(simple, callback_data, thread_callback_data_free);
+	g_simple_async_result_run_in_thread(simple, activity_score_thread, io_priority, cancellable);
+	g_object_unref(simple);
 }
 
 gint
@@ -998,85 +1465,119 @@ gboolean
 }
 
 gboolean
-purple_log_is_deletable(PurpleLog *log)
+purple_log_is_removable(PurpleLog *log, GCancellable *cancellable, GError **error)
 {
-	g_return_val_if_fail(log != NULL, FALSE);
-	g_return_val_if_fail(log->logger != NULL, FALSE);
+	PurpleLogClass *class;
 
-	if (log->logger->remove == NULL && log->logger->async->remove_async == NULL)
+	g_return_val_if_fail(PURPLE_IS_LOG(log), FALSE);
+
+	class = PURPLE_LOG_GET_CLASS(log);
+
+	if (class->remove_fn == NULL && class->remove_async == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported"));
+
 		return FALSE;
+	}
 
-	if (log->logger->is_deletable != NULL)
-		return log->logger->is_deletable(log);
+	if (class->is_removable_fn != NULL)
+		return class->is_removable_fn(log, cancellable, error);
 
 	return TRUE;
 }
 
 gboolean
-purple_log_delete(PurpleLog *log)
+purple_log_remove(PurpleLog *log, GCancellable *cancellable, GError **error)
 {
-	g_return_val_if_fail(log != NULL, FALSE);
-	g_return_val_if_fail(log->logger != NULL, FALSE);
+	PurpleLogClass *class;
+	gboolean ret;
 
-	if (log->logger->remove != NULL)
-		return log->logger->remove(log);
+	g_return_val_if_fail(PURPLE_IS_LOG(log), FALSE);
 
-	return FALSE;
+	class = PURPLE_LOG_GET_CLASS(log);
+
+	if (class->remove_fn == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported"));
+
+		return FALSE;
+	}
+
+	ret = class->remove_fn(log, cancellable, error);
+
+	return ret;
 }
 
-void
-purple_log_delete_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
-	GAsyncReadyCallback cb, gpointer userdata)
+static void
+remove_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
 {
-	_purple_log_delete_callback_data *callback_data;
-	GSimpleAsyncResult *simple;
+	PurpleLog *log = PURPLE_LOG(object);
+	GError *error = NULL;
 	gboolean result;
 
-	g_return_if_fail(log != NULL);
-	g_return_if_fail(log->logger != NULL);
+	result = purple_log_remove(log, cancellable, &error);
 
-	callback_data = g_new0(_purple_log_delete_callback_data, 1);
-	callback_data->cb = cb;
-	callback_data->userdata = userdata;
-	callback_data->log = log;
+	if (!result)
+		g_simple_async_result_set_from_error(simple, error);
+	else
+		g_simple_async_result_set_op_res_gboolean(simple, result);
 
-	if (log->logger->async->remove_async != NULL)
-		(log->logger->async->remove_async)(log, io_priority, cancellable,
-			log_delete_cb, callback_data);
-	else {
-		/* As there is no nonblocking function we can call blocking analog */
-		result = FALSE;
+	g_clear_error(&error);
+}
 
-		if (log->logger->remove != NULL)
-			result = (log->logger->remove)(log);
+static void
+purple_log_real_remove_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
+	GAsyncReadyCallback cb, gpointer userdata)
+{
+	GSimpleAsyncResult *simple;
 
-		simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_delete_async);
+	g_return_if_fail(PURPLE_IS_LOG(log));
 
-		g_simple_async_result_set_op_res_gboolean(simple, result);
-		g_simple_async_result_complete_in_idle(simple);
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata,
+		purple_log_real_remove_async);
 
-		g_object_unref(simple);
-	}
+	g_simple_async_result_run_in_thread(simple, remove_thread, io_priority, cancellable);
+	g_object_unref(simple);
 }
 
+void
+purple_log_remove_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
+	GAsyncReadyCallback cb, gpointer userdata)
+{
+	g_return_if_fail(PURPLE_IS_LOG(log));
+
+	PURPLE_LOG_GET_CLASS(log)->remove_async(log, io_priority,
+		cancellable, cb, userdata);
+}
+
+static gboolean
+purple_log_real_remove_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+	return g_simple_async_result_get_op_res_gboolean(G_SIMPLE_ASYNC_RESULT(res));
+}
+
 gboolean
-purple_log_delete_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+purple_log_remove_finish(PurpleLog *log, GAsyncResult *res, GError **error)
 {
-	GSimpleAsyncResult *simple;
+	g_return_val_if_fail(PURPLE_IS_LOG(log), -1);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(res), -1);
 
-	simple = G_SIMPLE_ASYNC_RESULT(res);
+	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;
+		if (g_simple_async_result_propagate_error(simple, error))
+			return -1;
+	}
 
-	if (g_simple_async_result_get_source_tag(simple) == purple_log_delete_async)
-		return g_simple_async_result_get_op_res_gboolean(simple);
-	else
-		return purple_log_common_deleter_finish(log, res, error);
+	return PURPLE_LOG_GET_CLASS(log)->remove_finish(log, res, error);
 }
 
 gchar *
-purple_log_get_log_dir(PurpleLogType type, const gchar *name, PurpleAccount *account)
+purple_log_get_log_dir(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account)
 {
 	PurplePlugin *prpl;
 	PurplePluginProtocolInfo *prpl_info;
@@ -1097,11 +1598,11 @@ purple_log_get_log_dir(PurpleLogType typ
 	acct_name = g_strdup(purple_escape_filename(purple_normalize(account,
 		purple_account_get_username(account))));
 
-	if (type == PURPLE_LOG_CHAT) {
+	if (chat_type == PURPLE_LOG_CHAT) {
 		temp = g_strdup_printf("%s.chat", purple_normalize(account, name));
 		target = purple_escape_filename(temp);
 		g_free(temp);
-	} else if(type == PURPLE_LOG_SYSTEM) {
+	} else if (chat_type == PURPLE_LOG_SYSTEM) {
 		target = ".system";
 	} else {
 		target = purple_escape_filename(purple_normalize(account, name));
@@ -1124,180 +1625,126 @@ logger_pref_cb(const gchar *name, Purple
 static void
 logger_pref_cb(const gchar *name, PurplePrefType type, gconstpointer value, gpointer data)
 {
-	PurpleLogLogger *logger;
-	GSList *l;
+	GArray *array;
+	guint i;
 
-	for (l = purple_log_logger_get_all(); l; l = g_slist_next(l)) {
-		logger = l->data;
+	array = purple_log_logger_get_all();
 
-		if (purple_strequal(logger->id, value)) {
-			purple_log_logger_set(logger);
+	for (i = 0; i < array->len; i++) {
+		PurpleLogClass *class;
+		GType log_type = g_array_index(array, GType, i);
+
+		class = g_type_class_peek(log_type);
+
+		if (purple_strequal(class->logger_id, value)) {
+			purple_log_logger_set(log_type);
 			return;
 		}
 	}
 
-	G_LOCK(txt_logger);
-	purple_log_logger_set(txt_logger);
-	G_UNLOCK(txt_logger);
+	purple_log_logger_set(PURPLE_TYPE_TXT_LOG);
 }
 
-
-PurpleLogLogger *
-purple_log_logger_new(const gchar *id, const gchar *name, gint functions, ...)
+void
+purple_log_logger_add(GType log_type)
 {
-	PurpleLogLogger *logger;
-	va_list args;
+	PurpleLogClass *class;
+	guint i;
 
-	g_return_val_if_fail(id != NULL, NULL);
-	g_return_val_if_fail(name != NULL, NULL);
-	g_return_val_if_fail(functions >= 1, NULL);
+	g_return_if_fail(log_type == G_TYPE_INVALID || g_type_is_a(log_type, PURPLE_TYPE_LOG));
 
-	logger = g_new0(PurpleLogLogger, 1);
-	logger->id = g_strdup(id);
-	logger->name = g_strdup(name);
+	G_LOCK(loggers);
+	for (i = 0; i < loggers->len; i++) {
+		if (log_type == g_array_index(loggers, GType, i)) {
+			G_UNLOCK(loggers);
+			return;
+		}
+	}
 
-	va_start(args, functions);
+	loggers = g_array_append_val(loggers, log_type);
+	G_UNLOCK(loggers);
 
-	if (functions >= 1)
-		logger->create = va_arg(args, void *);
-	if (functions >= 2)
-		logger->write = va_arg(args, void *);
-	if (functions >= 3)
-		logger->finalize = va_arg(args, void *);
-	if (functions >= 4)
-		logger->list = va_arg(args, void *);
-	if (functions >= 5)
-		logger->read = va_arg(args, void *);
-	if (functions >= 6)
-		logger->size = va_arg(args, void *);
-	if (functions >= 7)
-		logger->total_size = va_arg(args, void *);
-	if (functions >= 8)
-		logger->list_syslog = va_arg(args, void *);
-	if (functions >= 9)
-		logger->get_log_sets = va_arg(args, void *);
-	if (functions >= 10)
-		logger->remove = va_arg(args, void *);
-	if (functions >= 11)
-		logger->is_deletable = va_arg(args, void *);
+	class = g_type_class_ref(log_type);
 
-	logger->async = g_new0(PurpleLogLoggerAsyncFuncs, 1);
-
-	/* callbacks functions */
-	if (functions >= 12)
-		logger->async->create_async = va_arg(args, void *);
-	if (functions >= 13)
-		logger->async->write_async = va_arg(args, void *);
-	if (functions >= 14)
-		logger->async->finalize_async = va_arg(args, void *);
-	if (functions >= 15)
-		logger->async->list_async = va_arg(args, void *);
-	if (functions >= 16)
-		logger->async->read_async = va_arg(args, void *);
-	if (functions >= 17)
-		logger->async->size_async = va_arg(args, void *);
-	if (functions >= 18)
-		logger->async->total_size_async = va_arg(args, void *);
-	if (functions >= 19)
-		logger->async->list_syslog_async = va_arg(args, void *);
-	if (functions >= 20)
-		logger->async->get_log_sets_async = va_arg(args, void *);
-	if (functions >= 21)
-		logger->async->remove_async = va_arg(args, void *);
-
-	if (functions >= 22)
-		purple_debug_info("log", "Dropping new functions for logger: %s (%s)\n", name, id);
-
-	va_end(args);
-
-	return logger;
+	if (purple_strequal(purple_prefs_get_string("/purple/logging/format"), class->logger_id))
+		purple_prefs_trigger_callback("/purple/logging/format");
 }
 
 void
-purple_log_logger_free(PurpleLogLogger *logger)
+purple_log_logger_remove(GType log_type)
 {
-	g_return_if_fail(logger != NULL);
+	guint i;
 
-	g_free(logger->name);
-	g_free(logger->id);
-	g_free(logger->async);
-	g_free(logger);
-}
+	g_return_if_fail(log_type == G_TYPE_INVALID || g_type_is_a(log_type, PURPLE_TYPE_LOG));
 
-void
-purple_log_logger_add(PurpleLogLogger *logger)
-{
-	g_return_if_fail(logger != NULL);
-
 	G_LOCK(loggers);
-	if (g_slist_find(loggers, logger))
-		return;
+	for (i = 0; i < loggers->len; i++) {
+		if (log_type == g_array_index(loggers, GType, i)) {
+			loggers = g_array_remove_index_fast(loggers, i);
+			g_type_class_unref(g_type_class_peek(log_type));
 
-	loggers = g_slist_append(loggers, logger);
+			return;
+		}
+	}
 	G_UNLOCK(loggers);
 
-	if (purple_strequal(purple_prefs_get_string("/purple/logging/format"), logger->id))
-		purple_prefs_trigger_callback("/purple/logging/format");
+	//warning?
 }
 
 void
-purple_log_logger_remove(PurpleLogLogger *logger)
+purple_log_logger_set(GType log_type)
 {
-	g_return_if_fail(logger != NULL);
+	g_return_if_fail(log_type == G_TYPE_INVALID || g_type_is_a(log_type, PURPLE_TYPE_LOG));
 
-	G_LOCK(loggers);
-	loggers = g_slist_remove(loggers, logger);
-	G_UNLOCK(loggers);
-}
-
-void
-purple_log_logger_set(PurpleLogLogger *logger)
-{
-	g_return_if_fail(logger != NULL);
-
 	G_LOCK(current_logger);
-	current_logger = logger;
+	current_logger = log_type;
 	G_UNLOCK(current_logger);
 }
 
-PurpleLogLogger *
-purple_log_logger_get(void)
+GType
+purple_log_logger_get_default(void)
 {
-	PurpleLogLogger *logger;
+	GType log_type;
 
 	G_LOCK(current_logger);
-	logger = current_logger;
+	log_type = current_logger;
 	G_UNLOCK(current_logger);
 
-	return logger;
+	return log_type;
 }
 
-GSList *
+GArray *
 purple_log_logger_get_all(void)
 {
-	GSList *list;
+	GArray *array;
 
 	G_LOCK(loggers);
-	list = loggers;
+	array = loggers;
 	G_UNLOCK(loggers);
 
-	return list;
+	return array;
 }
 
 GList *
 purple_log_logger_get_options(void)
 {
-	GSList *n;
+	GArray *array;
 	GList *list = NULL;
+	guint i;
 
-	for (n = purple_log_logger_get_all(); n; n = g_slist_next(n)) {
-		PurpleLogLogger *data = n->data;
+	array = purple_log_logger_get_all();
 
-		if (!data->write && !data->async->write_async)
+	for (i = 0; i < array->len; i++) {
+		PurpleLogClass *class;
+		GType log_type = g_array_index(array, GType, i);
+
+		class = g_type_class_ref(log_type);
+
+		if (class->write_fn == NULL && class->write_async == NULL)
 			continue;
 
-		list = g_list_append(list, data->name);
-		list = g_list_append(list, data->id);
+		list = g_list_append(list, (void *) class->logger_name);
+		list = g_list_append(list, (void *) class->logger_id);
 	}
 
 	return list;
@@ -1308,95 +1755,167 @@ purple_log_compare(gconstpointer y, gcon
 {
 	const PurpleLog *a = y, *b = z;
 
-	return b->time - a->time;
+	return purple_log_get_time(b) - purple_log_get_time(a);
 }
 
 GList *
-purple_log_get_logs(PurpleLogType type, const gchar *name, PurpleAccount *account)
+purple_log_get_logs(PurpleLog *log, PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account, GCancellable *cancellable, GError **error)
 {
+	PurpleLogClass *class;
+
+	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+	g_return_val_if_fail(account != NULL, NULL); // PURPLE_IS_ACCOUNT(account)
+
+	class = PURPLE_LOG_GET_CLASS(log);
+
+	if (class->list_fn == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported"));
+
+		return NULL;
+	}
+
+	return class->list_fn(log, chat_type, name, account, cancellable, error);
+}
+
+GList *
+purple_logs_get_logs(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account, GCancellable *cancellable, GError **error)
+{
+	GArray *array;
 	GList *logs = NULL;
-	GSList *n;
+	guint i;
 
 	g_return_val_if_fail(name != NULL, NULL);
-	g_return_val_if_fail(account != NULL, NULL);
+	g_return_val_if_fail(account != NULL, NULL); // PURPLE_IS_ACCOUNT(account)
 
-	for (n = purple_log_logger_get_all(); n; n = g_slist_next(n)) {
-		PurpleLogLogger *logger = n->data;
+	array = purple_log_logger_get_all();
 
-		if (!logger->list)
-			continue;
+	for (i = 0; i < array->len; i++) {
+		PurpleLog *log;
+		GList *list;
+		GType log_type = g_array_index(array, GType, i);
 
-		logs = g_list_concat(logger->list(type, name, account), logs);
+		log = g_object_new(log_type, NULL);
+		list = purple_log_get_logs(log, chat_type, name, account, cancellable, error);
+
+		if (list == NULL) {
+			for ( ; logs != NULL; logs = g_list_delete_link(logs, logs))
+				g_object_unref(logs->data);
+
+			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_log_get_logs_async(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+static void
+get_logs_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
 {
-	_purple_log_logs_callback_data *callback_data;
+	_thread_callback_data *callback_data =
+		g_simple_async_result_get_op_res_gpointer(simple);
+	PurpleAccount *account = callback_data->account; // g_object_unref
+	PurpleLog *log = PURPLE_LOG(object);
+	PurpleLogChatType chat_type = callback_data->chat_type;
+	GError *error = NULL;
+	GList *logs;
+	gchar *name = callback_data->name;
+
+	logs = purple_log_get_logs(log, chat_type, name, account, cancellable, &error);
+
+	if (logs == NULL)
+		g_simple_async_result_set_from_error(simple, error);
+	else
+		g_simple_async_result_set_op_res_gpointer(simple, logs, NULL);
+
+	g_clear_error(&error);
+}
+
+static void
+purple_log_real_get_logs_async(PurpleLog *log, PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	_thread_callback_data *callback_data;
 	GSimpleAsyncResult *simple;
-	GSList *n;
 
-	g_return_if_fail(name != NULL);
-	g_return_if_fail(account != NULL);
+	callback_data = g_new0(_thread_callback_data, 1);
+	callback_data->chat_type = chat_type;
+	callback_data->name = g_strdup(name);
+	callback_data->account = account; // g_object_ref
 
-	n = purple_log_logger_get_all();
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata,
+		purple_log_real_get_logs_async);
 
-	callback_data = g_new0(_purple_log_logs_callback_data, 1);
-	callback_data->cb = cb;
-	callback_data->userdata = userdata;
-	callback_data->counter = g_slist_length(n);
-	callback_data->logs = NULL;
+	g_simple_async_result_set_op_res_gpointer(simple, callback_data, thread_callback_data_free);
+	g_simple_async_result_run_in_thread(simple, get_logs_thread, io_priority, cancellable);
+	g_object_unref(simple);
+}
 
-	for ( ; n; n = g_slist_next(n)) {
-		PurpleLogLogger *logger = n->data;
+void
+purple_log_get_logs_async(PurpleLog *log, PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	g_return_if_fail(PURPLE_IS_LOG(log));
+	g_return_if_fail(name != NULL);
+	g_return_if_fail(account != NULL); // PURPLE_IS_ACCOUNT(account)
 
-		if (logger->async->list_async)
-			(logger->async->list_async)(type, name, account, io_priority, cancellable,
-				log_list_cb, callback_data);
-		else if (logger->list) {
-			/* Call the blocking list function instead. */
-			GList *logs = (logger->list)(type, name, account);
+	PURPLE_LOG_GET_CLASS(log)->list_async(log, chat_type, name, account, io_priority, cancellable, cb, userdata);
+}
 
-			simple = g_simple_async_result_new(NULL,
-				log_list_cb,
-				callback_data,
-				purple_log_common_lister_async);
-			g_simple_async_result_set_op_res_gpointer(simple, logs, NULL);
+void
+purple_logs_get_logs_async(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	_get_logs_callback_data *callback_data;
+	GArray *array;
+	guint i;
 
-			g_simple_async_result_complete_in_idle(simple);
+	g_return_if_fail(name != NULL);
+	g_return_if_fail(account != NULL); // PURPLE_IS_ACCOUNT(account)
 
-			g_object_unref(simple);
-		} else {
-			simple = g_simple_async_result_new_error(NULL, log_list_cb,
-				callback_data,
-				G_FILE_ERROR,
-				G_FILE_ERROR_FAILED,
-				_("No available non-blocking log-getting function"));
+	array = purple_log_logger_get_all();
 
-			g_simple_async_result_complete_in_idle(simple);
+	callback_data = g_new0(_get_logs_callback_data, 1);
+	callback_data->cb = cb;
+	callback_data->userdata = userdata;
+	callback_data->counter = array->len;
+	callback_data->logs = NULL;
 
-			g_object_unref(simple);
-		}
+	for (i = 0; i < array->len; i++) {
+		PurpleLog *log;
+		GType log_type = g_array_index(array, GType, i);
+
+		log = g_object_new(log_type, NULL);
+		purple_log_get_logs_async(log, chat_type, name, account, io_priority, cancellable, log_list_cb, callback_data);
+		g_object_unref(log);
 	}
 }
 
+static GList *
+purple_log_real_get_logs_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+	return g_simple_async_result_get_op_res_gpointer(G_SIMPLE_ASYNC_RESULT(res));
+}
+
 GList *
-purple_log_get_logs_finish(GAsyncResult *res, GError **error)
+purple_log_get_logs_finish(PurpleLog *log, GAsyncResult *res, GError **error)
 {
-	GSimpleAsyncResult *simple;
+	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(res), NULL);
 
-	simple = G_SIMPLE_ASYNC_RESULT(res);
+	if (G_IS_SIMPLE_ASYNC_RESULT(res)) {
+		GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
 
-	if (g_simple_async_result_propagate_error(simple, error))
-		return NULL;
+		if (g_simple_async_result_propagate_error(simple, error))
+			return NULL;
+	}
 
-	g_return_val_if_fail(g_simple_async_result_get_source_tag(simple) == purple_log_common_lister_async, NULL);
-
-	return g_simple_async_result_get_op_res_gpointer(simple);
+	return PURPLE_LOG_GET_CLASS(log)->list_finish(log, res, error);
 }
 
 gint
@@ -1458,94 +1977,189 @@ log_add_log_set_to_hash(GHashTable *sets
 		purple_log_set_free(set);
 }
 
+static gboolean
+steal_log_sets(gpointer key, gpointer value, gpointer sets)
+{
+	log_add_log_set_to_hash(sets, key);
+	return TRUE;
+}
+
 GHashTable *
-purple_log_get_log_sets(void)
+purple_log_get_log_sets(PurpleLog *log, GCancellable *cancellable, GError **error)
 {
-	GHashTable *sets;
-	GSList *n;
+	PurpleLogClass *class;
 
+	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
+
+	class = PURPLE_LOG_GET_CLASS(log);
+
+	if (class->get_log_sets_fn == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported"));
+
+		return NULL;
+	}
+
+	return class->get_log_sets_fn(log, cancellable, error);
+}
+
+GHashTable *
+purple_logs_get_log_sets(GCancellable *cancellable, GError **error)
+{
+	GArray *array;
+	GHashTable *sets, *one_set;
+	GError *err = NULL;
+	guint i;
+
 	sets = g_hash_table_new_full(log_set_hash, log_set_equal,
 		(GDestroyNotify) purple_log_set_free, NULL);
 
-	/* Get the log sets from all the loggers. */
-	for (n = purple_log_logger_get_all(); n; n = g_slist_next(n)) {
-		PurpleLogLogger *logger = n->data;
+	array = purple_log_logger_get_all();
 
-		if (!logger->get_log_sets)
-			continue;
+	for (i = 0; i < array->len; i++) {
+		PurpleLog *log;
+		GType log_type = g_array_index(array, GType, i);
 
-		logger->get_log_sets(log_add_log_set_to_hash, sets);
+		log = g_object_new(log_type, NULL);
+		purple_log_get_log_sets(log, cancellable, &err);
+
+		if (one_set == NULL) {
+			g_object_unref(log);
+
+			if (err->code == G_IO_ERROR_NOT_SUPPORTED) {
+				g_clear_error(&err);
+				continue;
+			} else {
+				g_propagate_error(error, err);
+				g_hash_table_destroy(sets);
+				return NULL;
+			}
+		}
+
+		g_hash_table_foreach_steal(one_set, steal_log_sets, sets);
+		g_hash_table_destroy(one_set);
+		g_object_unref(log);
 	}
 
-	log_get_log_sets_common(sets, NULL, log_add_log_set_to_hash, NULL);
+	one_set = log_get_log_sets_common(cancellable, error);
 
+	if (one_set == NULL) {
+		g_hash_table_destroy(sets);
+		return NULL;
+	}
+
+	g_hash_table_foreach_steal(one_set, steal_log_sets, sets);
+	g_hash_table_destroy(one_set);
+
 	/* Return the GHashTable of unique PurpleLogSets. */
 	return sets;
 }
 
+static void
+get_log_sets_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
+{
+	PurpleLog *log = PURPLE_LOG(object);
+	PurpleLogClass *class = PURPLE_LOG_GET_CLASS(log);
+	GError *error = NULL;
+	GHashTable *sets;
+
+	if (class->get_log_sets_fn == NULL) {
+		g_simple_async_result_set_error(simple, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported"));
+
+		return;
+	}
+
+	sets = class->get_log_sets_fn(log, cancellable, &error);
+
+	if (sets == NULL)
+		g_simple_async_result_set_from_error(simple, error);
+	else
+		g_simple_async_result_set_op_res_gpointer(simple, sets, (GDestroyNotify) g_hash_table_unref);
+
+	g_clear_error(&error);
+}
+
+static void
+purple_log_real_get_log_sets_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	GSimpleAsyncResult *simple;
+
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata,
+		purple_log_real_get_log_sets_async);
+
+	g_simple_async_result_run_in_thread(simple, get_log_sets_thread, io_priority, cancellable);
+	g_object_unref(simple);
+}
+
 void
-purple_log_get_log_sets_async(gint io_priority, GCancellable *cancellable,
+purple_log_get_log_sets_async(PurpleLog *log, gint io_priority,
+	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	g_return_if_fail(PURPLE_IS_LOG(log));
+
+	PURPLE_LOG_GET_CLASS(log)->get_log_sets_async(log,
+		io_priority, cancellable, cb, userdata);
+}
+
+void
+purple_logs_get_log_sets_async(gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata)
 {
 	_purple_log_sets_callback_data *callback_data;
-	GSList *n;
+	GArray *array;
+	guint i;
 
 	callback_data = g_new0(_purple_log_sets_callback_data, 1);
 	callback_data->cb = cb;
-	callback_data->mutex = g_mutex_new();
 	callback_data->userdata = userdata;
 	callback_data->sets = g_hash_table_new_full(log_set_hash, log_set_equal,
 		(GDestroyNotify) purple_log_set_free, NULL);
 
 	/* +1 is need special for log_get_log_sets_common_async call */
-	n = purple_log_logger_get_all();
-	callback_data->counter = g_slist_length(n) + 1;
+	array = purple_log_logger_get_all();
+	callback_data->counter = array->len + 1;
 
 	/* Get the log sets from all the loggers. */
-	for ( ; n; n = g_slist_next(n)) {
-		PurpleLogLogger *logger = n->data;
+	for (i = 0; i < array->len; i++) {
+		PurpleLog *log;
+		GType log_type = g_array_index(array, GType, i);
 
-		if (logger->async->get_log_sets_async)
-			(logger->async->get_log_sets_async)(log_add_log_set_to_hash, callback_data->sets,
-				io_priority, cancellable, log_hash_cb, callback_data);
-		else if (logger->get_log_sets) {
-			GSimpleAsyncResult *simple;
+		log = g_object_new(log_type, NULL);
 
-			/* As there is no nonblocking  function we can call blocking analog */
-			g_mutex_lock(callback_data->mutex);
-			(logger->get_log_sets)(log_add_log_set_to_hash, callback_data->sets);
-			g_mutex_unlock(callback_data->mutex);
+		purple_log_get_log_sets_async(log, io_priority, cancellable,
+			log_hash_cb, callback_data);
+	}
 
-			simple = g_simple_async_result_new(NULL, log_hash_cb, callback_data,
-				log_get_log_sets_common_async);
+	log_get_log_sets_common_async(io_priority, cancellable, log_hash_cb, callback_data);
+}
 
-			g_hash_table_ref(callback_data->sets);
-			g_simple_async_result_set_op_res_gpointer(simple, callback_data->sets,
-				(GDestroyNotify) g_hash_table_unref);
-			g_simple_async_result_complete_in_idle(simple);
-
-			g_object_unref(simple);
-		} else
-			callback_data->counter--;
-	}
-
-	log_get_log_sets_common_async(callback_data->sets, callback_data->mutex,
-		log_add_log_set_to_hash, io_priority, cancellable, log_hash_cb, callback_data);
+static GHashTable * 
+purple_log_real_get_log_sets_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+	return g_hash_table_ref(g_simple_async_result_get_op_res_gpointer(G_SIMPLE_ASYNC_RESULT(res)));
 }
 
 GHashTable *
-purple_log_get_log_sets_finish(GAsyncResult *res, GError **error)
+purple_log_get_log_sets_finish(PurpleLog *log, GAsyncResult *res, GError **error)
 {
-	GSimpleAsyncResult *simple;
+	g_return_val_if_fail(log == NULL || PURPLE_IS_LOG(log), NULL);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(res), NULL);
 
-	simple = G_SIMPLE_ASYNC_RESULT(res);
+	if (G_IS_SIMPLE_ASYNC_RESULT(res)) {
+		GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
 
-	if (g_simple_async_result_propagate_error(simple, error))
-		return NULL;
+		if (g_simple_async_result_propagate_error(simple, error))
+			return NULL;
+	}
 
-	g_return_val_if_fail(g_simple_async_result_get_source_tag(simple) == log_get_log_sets_common_async, NULL);
-
-	return g_hash_table_ref(g_simple_async_result_get_op_res_gpointer(simple));
+	if (log == NULL)
+		/* Result of purple_log_common */
+		return g_hash_table_ref(g_simple_async_result_get_op_res_gpointer(G_SIMPLE_ASYNC_RESULT(res)));
+	else
+		return PURPLE_LOG_GET_CLASS(log)->get_log_sets_finish(log, res, error);
 }
 
 void
@@ -1562,80 +2176,156 @@ GList *
 }
 
 GList *
-purple_log_get_system_logs(PurpleAccount *account)
+purple_log_get_system_logs(PurpleLog *log, PurpleAccount *account, GCancellable *cancellable, GError **error)
 {
+	PurpleLogClass *class;
+
+	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
+	g_return_val_if_fail(account != NULL, NULL); // PURPLE_IS_ACCOUNT(account)
+
+	class = PURPLE_LOG_GET_CLASS(log);
+
+	if (class->list_syslog_fn == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_NOT_SUPPORTED,
+			_("Operation not supported"));
+
+		return NULL;
+	}
+
+	return class->list_syslog_fn(log, account, cancellable, error);
+}
+
+GList *
+purple_logs_get_system_logs(PurpleAccount *account, GCancellable *cancellable, GError **error)
+{
+	GArray *array;
 	GList *logs = NULL;
-	GSList *n;
+	guint i;
 
+	// PURPLE_IS_ACCOUNT(account)
 	g_return_val_if_fail(account != NULL, NULL);
 
-	for (n = purple_log_logger_get_all(); n; n = g_slist_next(n)) {
-		PurpleLogLogger *logger = n->data;
+	array = purple_log_logger_get_all();
 
-		if (!logger->list_syslog)
-			continue;
+	for (i = 0; i < array->len; i++) {
+		PurpleLog *log;
+		GList *list;
+		GType log_type = g_array_index(array, GType, i);
 
-		logs = g_list_concat(logger->list_syslog(account), logs);
+		log = g_object_new(log_type, NULL);
+		list = purple_log_get_system_logs(log, account, cancellable, error);
+
+		if (list == NULL) {
+			for ( ; logs != NULL; logs = g_list_delete_link(logs, logs))
+				g_object_unref(logs->data);
+
+			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_log_get_system_logs_async(PurpleAccount *account, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+static void
+list_syslog_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
 {
-	_purple_log_logs_callback_data *callback_data;
+	_thread_callback_data *callback_data =
+		g_simple_async_result_get_op_res_gpointer(simple);
+	PurpleAccount *account = callback_data->account;
+	GError *error = NULL;
+	GList *list;
+
+	list = purple_log_get_system_logs(PURPLE_LOG(object), account, cancellable, &error);
+
+	if (list == NULL)
+		g_simple_async_result_set_from_error(simple, error);
+	else
+		g_simple_async_result_set_op_res_gpointer(simple, list, NULL);
+
+	g_clear_error(&error);
+}
+
+static void
+purple_log_real_list_syslog_async(PurpleLog *log, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	_thread_callback_data *callback_data;
 	GSimpleAsyncResult *simple;
-	GSList *n;
 
-	g_return_if_fail(account != NULL);
+	callback_data = g_new0(_thread_callback_data, 1);
+	callback_data->account = account; // g_object_ref
 
-	n = purple_log_logger_get_all();
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata,
+		purple_log_real_list_syslog_async);
 
-	callback_data = g_new0(_purple_log_logs_callback_data, 1);
-	callback_data->userdata = userdata;
-	callback_data->cb = cb;
-	callback_data->counter = g_slist_length(n);
-	callback_data->logs = NULL;
+	g_simple_async_result_set_op_res_gpointer(simple, callback_data, thread_callback_data_free);
+	g_simple_async_result_run_in_thread(simple, list_syslog_thread, io_priority, cancellable);
+	g_object_unref(simple);
+}
 
-	for ( ; n; n = g_slist_next(n)) {
-		PurpleLogLogger *logger = n->data;
+void
+purple_log_get_system_logs_async(PurpleLog *log, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	g_return_if_fail(PURPLE_IS_LOG(log));
+	g_return_if_fail(account != NULL); // PURPLE_IS_ACCOUNT(account)
 
-		if (logger->async->list_syslog_async)
-			(logger->async->list_syslog_async)(account, io_priority, cancellable,
-				log_system_list_cb, callback_data);
-		else if (logger->list_syslog) {
-			/* Call the blocking list function instead. */
-			GList *logs = (logger->list_syslog)(account);
+	PURPLE_LOG_GET_CLASS(log)->list_syslog_async(log, account, io_priority, cancellable, cb, userdata);
+}
 
-			simple = g_simple_async_result_new(NULL,
-				log_system_list_cb,
-				callback_data,
-				purple_log_common_lister_async);
+void
+purple_logs_get_system_logs_async(PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	_get_logs_callback_data *callback_data;
+	GArray *array;
+	guint i;
 
-			g_simple_async_result_set_op_res_gpointer(simple, logs, NULL);
-			g_simple_async_result_complete_in_idle(simple);
+	g_return_if_fail(account != NULL); // PURPLE_IS_ACCOUNT(account)
 
-			g_object_unref(simple);
-		} else {
-			simple = g_simple_async_result_new_error(NULL, log_system_list_cb,
-				callback_data,
-				G_FILE_ERROR,
-				G_FILE_ERROR_FAILED,
-				_("No available non-blocking system-log-getting function"));
+	array = purple_log_logger_get_all();
 
-			g_simple_async_result_complete_in_idle(simple);
+	callback_data = g_new0(_get_logs_callback_data, 1);
+	callback_data->userdata = userdata;
+	callback_data->cb = cb;
+	callback_data->counter = array->len;
+	callback_data->logs = NULL;
 
-			g_object_unref(simple);
-		}
+	for (i = 0; i < array->len; i++) {
+		PurpleLog *log;
+		GType log_type = g_array_index(array, GType, i);
+
+		log = g_object_new(log_type, NULL);
+		purple_log_get_system_logs_async(log, account, io_priority, cancellable, log_system_list_cb, callback_data);
+
+		g_object_unref(log);
 	}
 }
 
+static GList *
+purple_log_real_list_syslog_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+	return g_simple_async_result_get_op_res_gpointer(G_SIMPLE_ASYNC_RESULT(res));
+}
+
 GList *
-purple_log_get_system_logs_finish(GAsyncResult *res, GError **error)
+purple_log_get_system_logs_finish(PurpleLog *log, GAsyncResult *res, GError **error)
 {
-	return purple_log_common_lister_finish(res, error);
+	g_return_val_if_fail(PURPLE_IS_LOG(log), NULL);
+	g_return_val_if_fail(G_IS_ASYNC_RESULT(res), NULL);
+
+	if (G_IS_SIMPLE_ASYNC_RESULT(res)) {
+		GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT(res);
+
+		if (g_simple_async_result_propagate_error(simple, error))
+			return NULL;
+	}
+
+	return PURPLE_LOG_GET_CLASS(log)->list_syslog_finish(log, res, error);
 }
 
 /****************************************************************************
@@ -1643,87 +2333,91 @@ void
  ****************************************************************************/
 
 void
-purple_log_init(void)
+purple_log_system_init(void)
 {
-	void *handle;
+	void *handle = purple_log_system_get_handle();
 
 	if (!g_thread_supported())
 		g_thread_init(NULL);
 
-	handle = purple_log_get_handle();
+	G_LOCK(loggers);
+	loggers = g_array_sized_new(FALSE, FALSE, sizeof(GType), 3);
+	G_UNLOCK(loggers);
 
 	purple_prefs_add_none("/purple/logging");
 	purple_prefs_add_bool("/purple/logging/log_ims", TRUE);
 	purple_prefs_add_bool("/purple/logging/log_chats", TRUE);
 	purple_prefs_add_bool("/purple/logging/log_system", FALSE);
 
-	purple_prefs_add_string("/purple/logging/format", "html");
+	purple_html_log_system_init();
+	purple_txt_log_system_init();
+	purple_old_log_system_init();
 
-	G_LOCK(html_logger);
-	html_logger = purple_log_logger_new("html", _("HTML"), 21,
-		NULL,
-		html_logger_write,
-		html_logger_finalize,
-		html_logger_list,
-		html_logger_read,
-		purple_log_common_sizer,
-		html_logger_total_size,
-		html_logger_list_syslog,
-		NULL,
-		purple_log_common_deleter,
-		purple_log_common_is_deletable,
-		NULL,
-		html_logger_write_async,
-		NULL,
-		html_logger_list_async,
-		html_logger_read_async,
-		purple_log_common_sizer_async,
-		html_logger_total_size_async,
-		html_logger_list_syslog_async,
-		NULL,
-		purple_log_common_deleter_async);
-	purple_log_logger_add(html_logger);
-	G_UNLOCK(html_logger);
+	// G_LOCK(html_logger);
+	// html_logger = purple_log_logger_new("html", _("HTML"), 21,
+		// NULL,
+		// html_logger_write,
+		// html_logger_finalize,
+		// html_logger_list,
+		// html_logger_read,
+		// purple_log_common_sizer,
+		// html_logger_total_size,
+		// html_logger_list_syslog,
+		// NULL,
+		// purple_log_common_deleter,
+		// purple_log_common_is_removable,
+		// NULL,
+		// html_logger_write_async,
+		// NULL,
+		// html_logger_list_async,
+		// html_logger_read_async,
+		// purple_log_common_sizer_async,
+		// html_logger_total_size_async,
+		// html_logger_list_syslog_async,
+		// NULL,
+		// purple_log_common_deleter_async);
+	// purple_log_logger_add(html_logger);
+	// G_UNLOCK(html_logger);
 
-	G_LOCK(txt_logger);
-	txt_logger = purple_log_logger_new("txt", _("Plain text"), 21,
-		NULL,
-		txt_logger_write,
-		txt_logger_finalize,
-		txt_logger_list,
-		txt_logger_read,
-		purple_log_common_sizer,
-		txt_logger_total_size,
-		txt_logger_list_syslog,
-		NULL,
-		purple_log_common_deleter,
-		purple_log_common_is_deletable,
-		NULL,
-		txt_logger_write_async,
-		NULL,
-		txt_logger_list_async,
-		txt_logger_read_async,
-		purple_log_common_sizer_async,
-		txt_logger_total_size_async,
-		txt_logger_list_syslog_async,
-		NULL,
-		purple_log_common_deleter_async);
-	purple_log_logger_add(txt_logger);
-	G_UNLOCK(txt_logger);
+	// G_LOCK(txt_logger);
+	// txt_logger = purple_log_logger_new("txt", _("Plain text"), 21,
+		// NULL,
+		// txt_logger_write,
+		// txt_logger_finalize,
+		// txt_logger_list,
+		// txt_logger_read,
+		// purple_log_common_sizer,
+		// txt_logger_total_size,
+		// txt_logger_list_syslog,
+		// NULL,
+		// purple_log_common_deleter,
+		// purple_log_common_is_removable,
+		// NULL,
+		// txt_logger_write_async,
+		// NULL,
+		// txt_logger_list_async,
+		// txt_logger_read_async,
+		// purple_log_common_sizer_async,
+		// txt_logger_total_size_async,
+		// txt_logger_list_syslog_async,
+		// NULL,
+		// purple_log_common_deleter_async);
+	// purple_log_logger_add(txt_logger);
+	// G_UNLOCK(txt_logger);
 
-	G_LOCK(old_logger);
-	old_logger = purple_log_logger_new("old", _("Old flat format"), 9,
-		NULL,
-		NULL,
-		old_logger_finalize,
-		old_logger_list,
-		old_logger_read,
-		old_logger_size,
-		old_logger_total_size,
-		NULL,
-		old_logger_get_log_sets);
-	purple_log_logger_add(old_logger);
-	G_UNLOCK(old_logger);
+	// G_LOCK(old_logger);
+	// old_logger = purple_log_logger_new("old", _("Old flat format"), 9,
+		// NULL,
+		// NULL,
+		// old_logger_finalize,
+		// old_logger_list,
+		// old_logger_read,
+		// old_logger_size,
+		// old_logger_total_size,
+		// NULL,
+		// old_logger_get_log_sets);
+	// purple_log_logger_add(old_logger);
+	// G_UNLOCK(old_logger);
 
 	purple_signal_register(handle, "log-timestamp",
 #if SIZEOF_TIME_T == 4
@@ -1758,14 +2452,10 @@ purple_log_init(void)
 		(GEqualFunc) _purple_logsize_user_equal,
 		(GDestroyNotify) _purple_logsize_user_free_key, NULL);
 	G_UNLOCK(logsize_users_decayed);
-
-	G_LOCK(log_ref_table);
-	log_ref_table = g_hash_table_new(NULL, NULL);
-	G_UNLOCK(log_ref_table);
 }
 
 void *
-purple_log_get_handle(void)
+purple_log_system_get_handle(void)
 {
 	static gint handle;
 
@@ -1773,55 +2463,42 @@ void
 }
 
 void
-purple_log_uninit(void)
+purple_log_system_uninit(void)
 {
-	purple_signals_unregister_by_instance(purple_log_get_handle());
+	purple_signals_unregister_by_instance(purple_log_system_get_handle());
 
-	G_LOCK(html_logger);
-	purple_log_logger_remove(html_logger);
-	purple_log_logger_free(html_logger);
-	html_logger = NULL;
-	G_UNLOCK(html_logger);
+	purple_html_log_system_uninit();
+	purple_txt_log_system_uninit();
+	purple_old_log_system_uninit();
 
-	G_LOCK(txt_logger);
-	purple_log_logger_remove(txt_logger);
-	purple_log_logger_free(txt_logger);
-	txt_logger = NULL;
-	G_UNLOCK(txt_logger);
+	G_LOCK(loggers);
+	g_array_free(loggers, TRUE);
+	loggers = NULL;
+	current_logger = G_TYPE_INVALID;
+	G_UNLOCK(loggers);
 
-	G_LOCK(old_logger);
-	purple_log_logger_remove(old_logger);
-	purple_log_logger_free(old_logger);
-	old_logger = NULL;
-	G_UNLOCK(old_logger);
-
 	G_LOCK(logsize_users);
 	g_hash_table_destroy(logsize_users);
+	logsize_users = NULL;
 	G_UNLOCK(logsize_users);
 
 	G_LOCK(logsize_users_decayed);
 	g_hash_table_destroy(logsize_users_decayed);
+	logsize_users_decayed = NULL;
 	G_UNLOCK(logsize_users_decayed);
-
-	G_LOCK(log_ref_table);
-	g_hash_table_destroy(log_ref_table);
-	G_UNLOCK(log_ref_table);
 }
 
-/****************************************************************************
- * LOGGERS ******************************************************************
- ****************************************************************************/
-
-static gchar *
-log_get_timestamp(PurpleLog *log, time_t when)
+gchar *
+_log_get_timestamp(PurpleLog *log, time_t when)
 {
 	gboolean show_date;
 	gchar *date;
 	struct tm tm;
 
-	show_date = log->type == PURPLE_LOG_SYSTEM || time(NULL) > when + 20 * 60;
+	show_date = purple_log_get_chat_type(log) == PURPLE_LOG_SYSTEM ||
+		time(NULL) > when + 20 * 60;
 
-	date = purple_signal_emit_return_1(purple_log_get_handle(), "log-timestamp",
+	date = purple_signal_emit_return_1(purple_log_system_get_handle(), "log-timestamp",
 		log, when, show_date);
 
 	if (date != NULL)
@@ -1837,8 +2514,8 @@ log_get_timestamp(PurpleLog *log, time_t
 
 /* 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 *
-convert_image_tags(const PurpleLog *log, const gchar *msg)
+gchar *
+_convert_image_tags(const PurpleLog *log, const gchar *msg)
 {
 	GString *newmsg = NULL;
 	GData *attributes;
@@ -1859,8 +2536,11 @@ convert_image_tags(const PurpleLog *log,
 			imgid = atoi(idstr);
 
 		if (imgid != 0) {
+			PurpleAccount *account = purple_log_get_account(log);
+			PurpleLogChatType chat_type = purple_log_get_chat_type(log);
 			PurpleStoredImage *image = purple_imgstore_find_by_id(imgid);
 			gchar *new_filename = NULL, *path = NULL, *dir;
+			const gchar *name = purple_log_get_name(log);
 			gconstpointer image_data;
 			size_t image_byte_count;
 
@@ -1873,7 +2553,7 @@ convert_image_tags(const PurpleLog *log,
 
 			image_data = purple_imgstore_get_data(image);
 			image_byte_count = purple_imgstore_get_size(image);
-			dir = purple_log_get_log_dir(log->type, log->name, log->account);
+			dir = purple_log_get_log_dir(chat_type, name, account);
 			new_filename = purple_util_get_image_filename(image_data, image_byte_count);
 
 			path = g_build_filename(dir, new_filename, NULL);
@@ -1921,70 +2601,95 @@ convert_image_tags(const PurpleLog *log,
 	return g_string_free(newmsg, FALSE);
 }
 
-void
-purple_log_common_writer(PurpleLog *log, const gchar *ext)
+gboolean
+purple_log_common_writer(PurpleLog *log, const gchar *ext, GCancellable *cancellable, GError **error)
 {
-	g_return_if_fail(log != NULL);
+	PurpleLogCommonLoggerData *data = purple_log_get_logger_data(log);
 
-	if (log->logger_data == NULL) {
-		PurpleLogCommonLoggerData *data = log->logger_data;
+	g_return_val_if_fail(PURPLE_IS_LOG(log), FALSE);
+
+	if (data == NULL) {
 		struct tm *tm;
 		const gchar *tz, *date;
 		gchar *dir, *filename, *path;
+		time_t log_time;
 
 		/* This log is new */
-		dir = purple_log_get_log_dir(log->type, log->name, log->account);
+		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)
-			return;
+		if (dir == NULL) {
+			g_set_error_literal(error,
+				G_IO_ERROR,
+				G_IO_ERROR_FAILED,
+				_("Unable to get log directory"));
 
-		purple_build_dir (dir, S_IRUSR | S_IWUSR | S_IXUSR);
+			return FALSE;
+		}
 
-		tm = localtime(&log->time);
+		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));
+
+			return FALSE;
+		}
+
+		log_time = purple_log_get_time(log);
+		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);
 
 		filename = g_strdup_printf("%s%s%s", date, tz, ext != NULL ? ext : "");
+		path = g_build_filename(dir, filename, NULL);
 
-		path = g_build_filename(dir, filename, NULL);
 		g_free(dir);
 		g_free(filename);
 
-		log->logger_data = data = g_slice_new0(PurpleLogCommonLoggerData);
-
+		data = g_slice_new0(PurpleLogCommonLoggerData);
+		purple_log_set_logger_data(log, data);
 		data->file = g_fopen(path, "a");
 
 		if (data->file == NULL) {
-			purple_debug_error("log", "Could not create log file %s\n", path);
+			PurpleConversation *conv = purple_log_get_conversation(log);
 
-			if (log->conv != NULL)
-				purple_conversation_write(log->conv, NULL, _("Logging of this conversation failed."),
+			g_set_error_literal(error,
+				G_FILE_ERROR,
+				g_file_error_from_errno(errno),
+				g_strerror(errno));
+
+			if (conv != NULL)
+				purple_conversation_write(conv, NULL,
+					_("Logging of this conversation failed."),
 					PURPLE_MESSAGE_ERROR, time(NULL));
 
 			g_free(path);
 
-			return;
+			return FALSE;
 		}
 
 		data->path = path;
 	}
+
+	return TRUE;
 }
 
 static void
 writer_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
 {
-	_purple_logger_writer_callback_data *callback_data =
-		g_simple_async_result_get_op_res_gpointer(simple);
-	PurpleLog *log = callback_data->log;
-	PurpleLogCommonLoggerData *data = log->logger_data;
+	PurpleLog *log = PURPLE_LOG(object);
+	PurpleLogCommonLoggerData *data = purple_log_get_logger_data(log);
 
 	if (data == NULL) {
+		_thread_callback_data *callback_data =
+			g_simple_async_result_get_op_res_gpointer(simple);
 		struct tm *tm;
 		const gchar *tz, *date;
 		gchar *dir, *filename, *path, *ext = callback_data->ext;
+		time_t log_time;
 
 		/* This log is new */
-		dir = purple_log_get_log_dir(log->type, log->name, log->account);
+		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_simple_async_result_set_error(simple, G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -2000,7 +2705,8 @@ writer_thread(GSimpleAsyncResult *simple
 			return;
 		}
 
-		tm = localtime(&log->time);
+		log_time = purple_log_get_time(log);
+		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);
 
@@ -2010,15 +2716,18 @@ writer_thread(GSimpleAsyncResult *simple
 		g_free(dir);
 		g_free(filename);
 
-		log->logger_data = data = g_slice_new0(PurpleLogCommonLoggerData);
+		data = g_slice_new0(PurpleLogCommonLoggerData);
+		purple_log_set_logger_data(log, data);
 		data->file = g_fopen(path, "a");
 
 		if (data->file == NULL) {
+			PurpleConversation *conv = purple_log_get_conversation(log);
+
 			g_simple_async_result_set_error(simple, G_FILE_ERROR,
 				g_file_error_from_errno(errno), "%s", g_strerror(errno));
 
-			if (log->conv != NULL)
-				purple_conversation_write(log->conv, NULL,
+			if (conv != NULL)
+				purple_conversation_write(conv, NULL,
 					_("Logging of this conversation failed."),
 					PURPLE_MESSAGE_ERROR, time(NULL));
 
@@ -2037,18 +2746,17 @@ purple_log_common_writer_async(PurpleLog
 purple_log_common_writer_async(PurpleLog *log, const gchar *ext, gint io_priority,
 	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
 {
-	_purple_logger_writer_callback_data *callback_data;
+	_thread_callback_data *callback_data;
 	GSimpleAsyncResult *simple;
 
-	g_return_if_fail(log != NULL);
+	g_return_if_fail(PURPLE_IS_LOG(log));
 
-	callback_data = g_new0(_purple_logger_writer_callback_data, 1);
-	callback_data->log = log;
+	callback_data = g_new0(_thread_callback_data, 1);
 	callback_data->ext = g_strdup(ext);
 
-	simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_common_writer_async);
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata, purple_log_common_writer_async);
 
-	g_simple_async_result_set_op_res_gpointer(simple, callback_data, purple_logger_writer_callback_data_free);
+	g_simple_async_result_set_op_res_gpointer(simple, callback_data, thread_callback_data_free);
 	g_simple_async_result_run_in_thread(simple, writer_thread, io_priority, cancellable);
 
 	g_object_unref(simple);
@@ -2070,8 +2778,8 @@ GList *
 }
 
 GList *
-purple_log_common_lister(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	const gchar *ext, PurpleLogLogger *logger)
+purple_log_common_lister(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account,
+	const gchar *ext, GType log_type, GCancellable *cancellable, GError **error)
 {
 	PurpleLog *log;
 	GDir *dir;
@@ -2084,14 +2792,20 @@ purple_log_common_lister(PurpleLogType t
 	g_return_val_if_fail(name != NULL, NULL);
 	g_return_val_if_fail(account != NULL, NULL);
 	g_return_val_if_fail(ext != NULL, NULL);
-	g_return_val_if_fail(logger != NULL, NULL);
+	g_return_val_if_fail(log_type == G_TYPE_INVALID || g_type_is_a(log_type, PURPLE_TYPE_LOG), NULL);
 
-	path = purple_log_get_log_dir(type, name, account);
+	path = purple_log_get_log_dir(chat_type, name, account);
 
-	if (path == NULL)
+	if (path == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_FAILED,
+			_("Unable to get log directory"));
+
 		return NULL;
+	}
 
-	dir = g_dir_open(path, 0, NULL);
+	dir = g_dir_open(path, 0, error);
 
 	if (dir == NULL) {
 		g_free(path);
@@ -2100,6 +2814,13 @@ purple_log_common_lister(PurpleLogType t
 	}
 
 	while ((filename = g_dir_read_name(dir)) != NULL) {
+		if (g_cancellable_set_error_if_cancelled(cancellable, error)) {
+			g_dir_close(dir);
+			g_free(path);
+
+			return NULL;
+		}
+
 		if (purple_str_has_suffix(filename, ext) &&
 		    strlen(filename) >= (17 + strlen(ext))) {
 #if defined (HAVE_TM_GMTOFF) && defined (HAVE_STRUCT_TM_TM_ZONE)
@@ -2115,21 +2836,21 @@ purple_log_common_lister(PurpleLogType t
 				tm.tm_gmtoff = tz_off - tm.tm_gmtoff;
 
 			if (stamp == 0 || rest == NULL || (end = strchr(rest, '.')) == NULL || strchr(rest, ' ') != NULL) {
-				log = purple_log_new(type, name, account, NULL, stamp, NULL);
+				log = purple_log_new(log_type, chat_type, name, account, NULL, stamp, NULL);
 			} else {
 				gchar *tmp = g_strndup(rest, end - rest);
 				tm.tm_zone = tmp;
-				log = purple_log_new(type, name, account, NULL, stamp, &tm);
+				log = purple_log_new(log_type, chat_type, name, account, NULL, stamp, &tm);
 				g_free(tmp);
 			}
 #else
 			time_t stamp = purple_str_to_time(filename, FALSE, &tm, NULL, NULL);
 
-			log = purple_log_new(type, name, account, NULL, stamp, (stamp != 0) ?  &tm : NULL);
+			log = purple_log_new(log_type, chat_type, name, account, NULL, stamp, (stamp != 0) ?  &tm : NULL);
 #endif
 
-			log->logger = logger;
-			log->logger_data = data = g_slice_new0(PurpleLogCommonLoggerData);
+			data = g_slice_new0(PurpleLogCommonLoggerData);
+			purple_log_set_logger_data(log, data);
 
 			data->path = g_build_filename(path, filename, NULL);
 			list = g_list_prepend(list, log);
@@ -2148,112 +2869,40 @@ common_lister_thread(GSimpleAsyncResult 
 	_purple_logger_lister_callback_data *callback_data =
 		g_simple_async_result_get_op_res_gpointer(simple);
 	PurpleAccount *account = callback_data->account;
-	PurpleLog *log;
-	PurpleLogCommonLoggerData *data;
-	PurpleLogLogger *logger = callback_data->logger;
-	PurpleLogType type = callback_data->type;
-	GError *err = NULL;
-	GList *list = NULL;
-	GDir *dir;
-	const gchar *name = callback_data->name, *ext = callback_data->ext, *filename;
-	gchar *path;
-	struct tm tm;
+	PurpleLogChatType chat_type = callback_data->chat_type;
+	GType log_type = callback_data->log_type;
+	GError *error = NULL;
+	GList *list;
+	gchar *name = callback_data->name, *ext = callback_data->ext;
 
-	path = purple_log_get_log_dir(type, name, account);
+	list = purple_log_common_lister(chat_type, name, account, ext, log_type, cancellable, &error);
 
-	if (path == NULL) {
-		g_simple_async_result_set_error(simple, G_IO_ERROR, G_IO_ERROR_FAILED,
-			_("Unable to get log directory"));
+	if (list == NULL)
+		g_simple_async_result_set_from_error(simple, error);
+	else
+		g_simple_async_result_set_op_res_gpointer(simple, list, (GDestroyNotify) g_list_free);
 
-		return;
-	}
-
-	dir = g_dir_open(path, 0, &err);
-
-	if (dir == NULL) {
-		/* Should we indicate if the directory was just empty, as we do with total_sizer?
-		 * or just let the caller figure that sort of thing out? Feels weird to be inconsistent...
-		 */
-		g_simple_async_result_set_from_error(simple, err);
-
-		g_free(path);
-		g_clear_error(&err);
-
-		return;
-	}
-
-	g_clear_error(&err);
-
-	while ((filename = g_dir_read_name(dir)) != NULL) {
-		if (g_cancellable_set_error_if_cancelled(cancellable, &err)) {
-			g_simple_async_result_set_from_error(simple, err);
-
-			g_dir_close(dir);
-			g_free(path);
-			g_clear_error(&err);
-
-			return;
-		}
-
-		if (purple_str_has_suffix(filename, ext) &&
-		    strlen(filename) >= (17 + strlen(ext))) {
-#if defined (HAVE_TM_GMTOFF) && defined (HAVE_STRUCT_TM_TM_ZONE)
-			long tz_off;
-			const gchar *rest, *end;
-			time_t stamp = purple_str_to_time(purple_unescape_filename(filename), FALSE, &tm, &tz_off, &rest);
-
-			/* As zero is a valid offset, PURPLE_NO_TZ_OFF means no offset was
-			 * provided. See util.h. Yes, it's kinda ugly. */
-			if (tz_off != PURPLE_NO_TZ_OFF)
-				tm.tm_gmtoff = tz_off - tm.tm_gmtoff;
-
-			if (stamp == 0 || rest == NULL || (end = strchr(rest, '.')) == NULL || strchr(rest, ' ') != NULL) {
-				log = purple_log_new(type, name, account, NULL, stamp, NULL);
-			} else {
-				gchar *tmp = g_strndup(rest, end - rest);
-				tm.tm_zone = tmp;
-				log = purple_log_new(type, name, account, NULL, stamp, &tm);
-				g_free(tmp);
-			}
-#else
-			time_t stamp = purple_str_to_time(filename, FALSE, &tm, NULL, NULL);
-
-			log = purple_log_new(type, name, account, NULL, stamp, (stamp != 0) ?  &tm : NULL);
-#endif
-
-			log->logger = logger;
-			log->logger_data = data = g_slice_new0(PurpleLogCommonLoggerData);
-
-			data->path = g_build_filename(path, filename, NULL);
-			list = g_list_prepend(list, log);
-		}
-	}
-
-	g_dir_close(dir);
-	g_free(path);
-
-	g_simple_async_result_set_op_res_gpointer(simple, list, NULL);
+	g_clear_error(&error);
 }
 
 void
-purple_log_common_lister_async(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	const gchar *ext, PurpleLogLogger *logger, gint io_priority, GCancellable *cancellable,
+purple_log_common_lister_async(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account,
+	const gchar *ext, GType log_type, gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata)
 {
 	_purple_logger_lister_callback_data *callback_data;
 	GSimpleAsyncResult *simple;
 
 	g_return_if_fail(name != NULL);
-	g_return_if_fail(account != NULL);
+	g_return_if_fail(account != NULL); // PURPLE_IS_ACCOUNT(account)
 	g_return_if_fail(ext != NULL);
-	g_return_if_fail(logger != NULL);
 
 	callback_data = g_new0(_purple_logger_lister_callback_data, 1);
-	callback_data->type = type;
+	callback_data->chat_type = chat_type;
 	callback_data->name = g_strdup(name);
 	callback_data->account = account;
 	callback_data->ext = g_strdup(ext);
-	callback_data->logger = logger;
+	callback_data->log_type = log_type;
 
 	simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_common_lister_async);
 
@@ -2283,106 +2932,41 @@ purple_log_common_lister_finish(GAsyncRe
 // TODO: Rather than calling this multiple times with different extensions,
 // TODO: could we somehow store up all the extensions and do the loop just
 // TODO: once?  This may be possible with the non-blocking stuff...
-gint
-purple_log_common_total_sizer(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	const gchar *ext)
+gssize
+purple_log_common_total_sizer(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account, const gchar *ext, GCancellable *cancellable, GError **error)
 {
 	_purple_logsize_user *lu;
 	GDir *dir;
-	gint size;
+	GError *err = NULL;
 	const gchar *filename;
-	gchar *tmp, *path;
-	struct stat st;
+	gchar *path;
+	gssize size = 0, total;
 	gpointer ptrsize;
 
 	g_return_val_if_fail(name != NULL, 0);
 	g_return_val_if_fail(account != NULL, 0);
 	g_return_val_if_fail(ext != NULL, 0);
 
-	path = purple_log_get_log_dir(type, name, account);
+	path = purple_log_get_log_dir(chat_type, name, account);
 
-	if (path == NULL)
-		return 0;
-
-	if (!(dir = g_dir_open(path, 0, NULL))){
-		g_free(path);
-		return 0;
-	}
-
-	size = 0;
-
-	while ((filename = g_dir_read_name(dir))) {
-		if (purple_str_has_suffix(filename, ext) &&
-		    strlen(filename) >= (17 + strlen(ext))) {
-			tmp = g_build_filename(path, filename, NULL);
-
-			if (g_stat(tmp, &st)) {
-				purple_debug_error("log", "Error stating log file: %s\n", tmp);
-				g_free(tmp);
-
-				continue;
-			}
-
-			g_free(tmp);
-			size += st.st_size;
-		}
-	}
-
-	g_dir_close(dir);
-	g_free(path);
-
-	G_LOCK(logsize_users);
-	lu = g_new(_purple_logsize_user, 1);
-	lu->name = g_strdup(purple_normalize(account, name));
-	lu->account = account; //g_object_ref?
-
-	if (g_hash_table_lookup_extended(logsize_users, lu, NULL, &ptrsize))
-		size += GPOINTER_TO_INT(ptrsize);
-
-	g_hash_table_replace(logsize_users, lu, GINT_TO_POINTER(size));
-	G_UNLOCK(logsize_users);
-
-	return size;
-}
-
-static void
-total_sizer_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
-{
-	_purple_logger_total_size_callback_data *callback_data =
-		g_simple_async_result_get_op_res_gpointer(simple);
-	_purple_logsize_user *lu;
-	PurpleAccount *account = callback_data->account;
-	PurpleLogType type = callback_data->type;
-	GDir *dir;
-	GError *err = NULL;
-	gchar *tmp, *path, *name = callback_data->name, *ext = callback_data->ext;
-	const gchar *filename;
-	struct stat st;
-	gssize size = 0, total;
-	gpointer ptrsize;
-
-	if(account == NULL) {
-		g_simple_async_result_set_error(simple, G_IO_ERROR, G_IO_ERROR_FAILED,
-			_("Account is NULL"));
-
-		return;
-	}
-
-	path = purple_log_get_log_dir(type, name, account);
-
 	if (path == NULL) {
-		g_simple_async_result_set_error(simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_FAILED,
 			_("Unable to get log directory"));
 
-		return;
+		return -1;
 	}
 
 	dir = g_dir_open(path, 0, &err);
 
 	if (dir == NULL){
+		g_free(path);
+
 		/* If the directory doesn't exist, we just don't have logs for them */
 		if (err->code == G_FILE_ERROR_NOENT) {
 			G_LOCK(logsize_users);
+
 			lu = g_new(_purple_logsize_user, 1);
 			lu->name = g_strdup(purple_normalize(account, name));
 			lu->account = account; //g_object_ref?
@@ -2392,40 +2976,35 @@ total_sizer_thread(GSimpleAsyncResult *s
 
 			G_UNLOCK(logsize_users);
 
-			g_simple_async_result_set_op_res_gssize(simple, size);
-		} else
-			g_simple_async_result_set_from_error(simple, err);
-
-		g_free(path);
-		g_clear_error(&err);
-
-		return;
+			return 0;
+		} else {
+			g_propagate_error(error, err);
+			return -1;
+		}
 	}
 
-	g_clear_error(&err);
-
 	while ((filename = g_dir_read_name(dir)) != NULL) {
-		if (g_cancellable_set_error_if_cancelled(cancellable, &err)) {
-			g_simple_async_result_set_from_error(simple, err);
-
+		if (g_cancellable_set_error_if_cancelled(cancellable, error)) {
 			g_dir_close(dir);
 			g_free(path);
-			g_clear_error(&err);
 
-			return;
+			return -1;
 		}
 
-		g_clear_error(&err);
-
 		if (purple_str_has_suffix(filename, ext) &&
 		    strlen(filename) >= 17 + strlen(ext)) {
-			tmp = g_build_filename(path, filename, NULL);
+			struct stat st;
+			gchar *tmp = g_build_filename(path, filename, NULL);
 
 			if (g_stat(tmp, &st)) {
-				purple_debug_error("log", "Error stating log file: %s\n", tmp);
 				g_free(tmp);
 
-				continue;
+				g_set_error_literal(error,
+					G_FILE_ERROR,
+					g_file_error_from_errno(errno),
+					g_strerror(errno));
+
+				return -1;
 			}
 
 			g_free(tmp);
@@ -2450,11 +3029,32 @@ total_sizer_thread(GSimpleAsyncResult *s
 	g_dir_close(dir);
 	g_free(path);
 
-	g_simple_async_result_set_op_res_gssize(simple, size);
+	return size;
 }
 
+static void
+common_total_sizer_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
+{
+	_purple_logger_total_size_callback_data *callback_data =
+		g_simple_async_result_get_op_res_gpointer(simple);
+	PurpleAccount *account = callback_data->account;
+	PurpleLogChatType chat_type = callback_data->chat_type;
+	GError *error = NULL;
+	gchar *name = callback_data->name, *ext = callback_data->ext;
+	gssize size;
+
+	size = purple_log_common_total_sizer(chat_type, name, account, ext, cancellable, &error);
+
+	if (size < 0)
+		g_simple_async_result_set_from_error(simple, error);
+	else
+		g_simple_async_result_set_op_res_gssize(simple, size);
+
+	g_clear_error(&error);
+}
+
 void
-purple_log_common_total_sizer_async(PurpleLogType type, const gchar *name,
+purple_log_common_total_sizer_async(PurpleLogChatType chat_type, const gchar *name,
 	PurpleAccount *account, const gchar *ext, gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata)
 {
@@ -2466,7 +3066,7 @@ purple_log_common_total_sizer_async(Purp
 	g_return_if_fail(ext != NULL);
 
 	callback_data = g_new0(_purple_logger_total_size_callback_data, 1);
-	callback_data->type = type;
+	callback_data->chat_type = chat_type;
 	callback_data->name = g_strdup(name);
 	callback_data->account = account;
 	callback_data->ext = g_strdup(ext);
@@ -2475,7 +3075,7 @@ purple_log_common_total_sizer_async(Purp
 
 	g_simple_async_result_set_op_res_gpointer(simple, callback_data,
 		purple_logger_total_size_callback_data_free);
-	g_simple_async_result_run_in_thread(simple, total_sizer_thread, io_priority, cancellable);
+	g_simple_async_result_run_in_thread(simple, common_total_sizer_thread, io_priority, cancellable);
 
 	g_object_unref(simple);
 }
@@ -2495,67 +3095,64 @@ purple_log_common_total_sizer_finish(GAs
 	return g_simple_async_result_get_op_res_gssize(simple);
 }
 
-gint
-purple_log_common_sizer(PurpleLog *log)
+gssize
+purple_log_common_sizer(PurpleLog *log, GCancellable *cancellable, GError **error)
 {
+	PurpleLogCommonLoggerData *data;
 	struct stat st;
-	PurpleLogCommonLoggerData *data;
 
-	g_return_val_if_fail(log != NULL, 0);
+	g_return_val_if_fail(PURPLE_IS_LOG(log), 0);
 
-	data = log->logger_data;
+	data = purple_log_get_logger_data(log);
 
-	g_return_val_if_fail(data != NULL, 0);
+	if (data == NULL || data->path == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_FAILED,
+			_("Unable to get log path"));
 
-	if (!data->path || g_stat(data->path, &st))
-		st.st_size = 0;
+		return -1;
+	} else if (g_stat(data->path, &st)) {
+		g_set_error_literal(error,
+			G_FILE_ERROR,
+			g_file_error_from_errno(errno),
+			g_strerror(errno));
 
+		return -1;
+	}
+
 	return st.st_size;
 }
 
 static void
 sizer_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
 {
-	_purple_logger_sizer_callback_data *callback_data =
-		g_simple_async_result_get_op_res_gpointer(simple);
-	PurpleLogCommonLoggerData *data = callback_data->log->logger_data;
-	struct stat st;
+	PurpleLog *log = PURPLE_LOG(object);
+	GError *error = NULL;
+	gssize size;
 
-	if (data == NULL || data->path == NULL) {
-		g_simple_async_result_set_error(simple, G_IO_ERROR, G_IO_ERROR_FAILED,
-			_("Unable to get log path"));
+	size = purple_log_common_sizer(log, cancellable, &error);
 
-		return;
-	}
+	if (size < 0)
+		g_simple_async_result_set_from_error(simple, error);
+	else
+		g_simple_async_result_set_op_res_gssize(simple, size);
 
-	if (g_stat(data->path, &st)) {
-		g_simple_async_result_set_error(simple, G_IO_ERROR, G_IO_ERROR_FAILED,
-			_("Error stating log file"));
-
-		return;
-	}
-
-	g_simple_async_result_set_op_res_gssize(simple, st.st_size);
+	g_clear_error(&error);
 }
 
 void
 purple_log_common_sizer_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata)
 {
-	_purple_logger_sizer_callback_data *callback_data;
 	GSimpleAsyncResult *simple;
 
-	g_return_if_fail(log != NULL);
+	g_return_if_fail(PURPLE_IS_LOG(log));
 
-	callback_data = g_new0(_purple_logger_sizer_callback_data, 1);
-	callback_data->log = log;
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata, purple_log_common_sizer_async);
 
-	simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_common_sizer_async);
+	g_simple_async_result_run_in_thread(simple, sizer_thread, io_priority, cancellable);
 
-	g_simple_async_result_set_op_res_gpointer(simple, callback_data, g_free);
-	g_simple_async_result_run_in_thread(simple, sizer_thread, io_priority,
-		cancellable);
-
 	g_object_unref(simple);
 }
 
@@ -2564,46 +3161,45 @@ purple_log_common_sizer_finish(PurpleLog
 {
 	GSimpleAsyncResult *simple;
 
+	g_return_val_if_fail(PURPLE_IS_LOG(log), -1);
+	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 -1;
 
-	g_return_val_if_fail(g_simple_async_result_get_source_tag(simple) == purple_log_common_sizer_async, -1);
-
 	return g_simple_async_result_get_op_res_gssize(simple);
 }
 
 /* This will build log sets for all loggers that use the common logger
  * functions because they use the same directory structure. */
-static gboolean
-log_get_log_sets_common(GHashTable *sets, GMutex *mutex, PurpleLogSetCallback set_cb, GError **error)
+static GHashTable *
+log_get_log_sets_common(GCancellable *cancel, GError **error)
 {
-	PurpleAccount *account;
-	PurplePlugin *prpl;
-	PurplePluginProtocolInfo *prpl_info;
-	GDir *log_dir, *protocol_dir, *username_dir;
-	GError *err = NULL;
-	GList *accounts, *account_iter;
-	const gchar *protocol, *prpl_protocol, *username, *username_unescaped;
-	gchar *log_path, *protocol_path, *protocol_unescaped, *username_path, *name, *tmp;
-	guint len;
+	GDir *log_dir, *username_dir;
+	GHashTable *sets;
+	const gchar *protocol;
+	gchar *log_path;
 
 	log_path = g_build_filename(purple_user_dir(), "logs", NULL);
-	log_dir = g_dir_open(log_path, 0, &err);
+	log_dir = g_dir_open(log_path, 0, error);
 
 	if (log_dir == NULL) {
-		if (error != NULL)
-			*error = err;
-
 		g_free(log_path);
-		return FALSE;
+		return NULL;
 	}
 
-	g_clear_error(&err);
+	sets = g_hash_table_new_full(log_set_hash, log_set_equal,
+		(GDestroyNotify) purple_log_set_free, NULL);
 
 	while ((protocol = g_dir_read_name(log_dir)) != NULL) {
-		protocol_path = g_build_filename(log_path, protocol, NULL);
+		GDir *protocol_dir;
+		GList *accounts = NULL, *account_iter;
+		const gchar *username;
+		gchar *protocol_path = g_build_filename(log_path, protocol, NULL);
+		gchar *protocol_unescaped;
+
 		protocol_dir = g_dir_open(protocol_path, 0, NULL);
 
 		if (protocol_dir == NULL) {
@@ -2614,13 +3210,18 @@ log_get_log_sets_common(GHashTable *sets
 		/* Using g_strdup() to cover the one-in-a-million chance that a
 		 * prpl's list_icon function uses purple_unescape_filename(). */
 		protocol_unescaped = g_strdup(purple_unescape_filename(protocol));
-		accounts = NULL;
 
 		/* Find all the accounts for protocol. */
-		for (account_iter = purple_accounts_get_all(); account_iter != NULL; account_iter = g_list_next(account_iter)) {
+		for (account_iter = purple_accounts_get_all(); account_iter != NULL;
+			account_iter = g_list_next(account_iter))
+		{
+			PurplePlugin *prpl;
+			PurplePluginProtocolInfo *prpl_info;
+			const gchar *prpl_protocol;
+
 			prpl = purple_find_prpl(purple_account_get_protocol_id((PurpleAccount *) account_iter->data));
 
-			if (!prpl)
+			if (prpl == NULL)
 				continue;
 
 			prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2633,8 +3234,10 @@ log_get_log_sets_common(GHashTable *sets
 		g_free(protocol_unescaped);
 
 		while ((username = g_dir_read_name(protocol_dir)) != NULL) {
-			username_path = g_build_filename(protocol_path, username, NULL);
-			account = NULL;
+			PurpleAccount *account = NULL;
+			const gchar *username_unescaped;
+			gchar *username_path = g_build_filename(protocol_path, username, NULL);
+			gchar *name;
 
 			if ((username_dir = g_dir_open(username_path, 0, NULL)) == NULL) {
 				g_free(username_path);
@@ -2655,6 +3258,8 @@ log_get_log_sets_common(GHashTable *sets
 			while ((name = (gchar *) g_dir_read_name(username_dir)) != NULL) {
 				/* IMPORTANT: Always initialize all members of PurpleLogSet */
 				PurpleLogSet *set = g_slice_new(PurpleLogSet);
+				gchar *tmp;
+				guint len;
 
 				/* Unescape the filename. */
 				name = g_strdup(purple_unescape_filename(name));
@@ -2693,12 +3298,7 @@ log_get_log_sets_common(GHashTable *sets
 				else
 					set->buddy = FALSE;
 
-				if (mutex != NULL) {
-					g_mutex_lock(mutex);
-					set_cb(sets, set);
-					g_mutex_unlock(mutex);
-				} else
-					set_cb(sets, set);
+				log_add_log_set_to_hash(sets, set);
 			}
 
 			g_free(username_path);
@@ -2712,1714 +3312,176 @@ log_get_log_sets_common(GHashTable *sets
 	g_free(log_path);
 	g_dir_close(log_dir);
 
-	return TRUE;
+	return sets;
 }
 
 static void
 log_get_log_sets_common_thread(GSimpleAsyncResult *simple, GObject *object,
 	GCancellable *cancellable)
 {
-	_purple_logger_get_sets_common_callback_data *callback_data =
-		g_simple_async_result_get_op_res_gpointer(simple);
-	GError *err = NULL;
-	GHashTable *sets = callback_data->sets;
+	GError *error = NULL;
+	GHashTable *sets;
 
-	if (!log_get_log_sets_common(sets, callback_data->mutex,
-		callback_data->set_cb, &err))
-		g_simple_async_result_set_from_error(simple, err);
+	sets = log_get_log_sets_common(cancellable, &error);
+
+	if (sets == NULL)
+		g_simple_async_result_set_from_error(simple, error);
 	else
-		g_simple_async_result_set_op_res_gpointer(simple, sets,
-			(GDestroyNotify) g_hash_table_unref);
+		g_simple_async_result_set_op_res_gpointer(simple, sets, (GDestroyNotify) g_hash_table_unref);
 
-	g_clear_error(&err);
+	g_clear_error(&error);
 }
 
 static void
-log_get_log_sets_common_async(GHashTable *sets, GMutex *mutex, PurpleLogSetCallback set_cb,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+log_get_log_sets_common_async(gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
 {
-	_purple_logger_get_sets_common_callback_data *callback_data;
 	GSimpleAsyncResult *simple;
 
-	callback_data = g_new0(_purple_logger_get_sets_common_callback_data, 1);
-	callback_data->sets = g_hash_table_ref(sets);
-	callback_data->mutex = mutex;
-	callback_data->set_cb = set_cb;
-
 	simple = g_simple_async_result_new(NULL, cb, userdata, log_get_log_sets_common_async);
 
-	g_simple_async_result_set_op_res_gpointer(simple, callback_data, g_free);
-	g_simple_async_result_run_in_thread(simple, log_get_log_sets_common_thread, io_priority,
-		cancellable);
-
+	g_simple_async_result_run_in_thread(simple, log_get_log_sets_common_thread, io_priority, cancellable);
 	g_object_unref(simple);
 }
 
 gboolean
-purple_log_common_deleter(PurpleLog *log)
+purple_log_common_remover(PurpleLog *log, GCancellable *cancellable, GError **error)
 {
 	PurpleLogCommonLoggerData *data;
 	GFile *file;
-	GError *err;
 	gboolean result;
 
-	g_return_val_if_fail(log != NULL, FALSE);
+	g_return_val_if_fail(PURPLE_IS_LOG(log), FALSE);
 
-	data = log->logger_data;
+	data = purple_log_get_logger_data(log);
 
-	if (data == NULL || data->path == NULL)
+	if (data == NULL || data->path == NULL) {
+		g_set_error_literal(error,
+			G_IO_ERROR,
+			G_IO_ERROR_FAILED,
+			_("Unable to get log path"));
+
 		return FALSE;
+	}
 
-	err = NULL;
 	file = g_file_new_for_path(data->path);
-	result = g_file_delete(file, NULL, &err);
+	result = g_file_delete(file, cancellable, error);
 
-	if (!result && err->code != G_IO_ERROR_CANCELLED)
-		purple_debug_error("log", "Failed to delete: %s - %s\n", data->path, err->message);
-
-	g_clear_error(&err);
-
 	return result;
 }
 
 static void
-deleter_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
+remover_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
 {
-	_purple_logger_deleter_callback_data *callback_data =
-		g_simple_async_result_get_op_res_gpointer(simple);
-	PurpleLogCommonLoggerData *data = callback_data->log->logger_data;
-	GError *err = NULL;
-	GFile *file;
+	PurpleLog *log = PURPLE_LOG(object);
+	GError *error = NULL;
 
-	if (data == NULL || data->path == NULL) {
-		g_simple_async_result_set_error(simple, G_IO_ERROR, G_IO_ERROR_FAILED,
-			_("Unable to get log path"));
-
-		return;
-	}
-
-	file = g_file_new_for_path(data->path);
-
-	if (!g_file_delete(file, cancellable, &err))
-		g_simple_async_result_set_from_error(simple, err);
+	if (!purple_log_common_remover(log, cancellable, &error))
+		g_simple_async_result_set_from_error(simple, error);
 	else
 		g_simple_async_result_set_op_res_gboolean(simple, TRUE);
 
-	g_object_unref(file);
-	g_clear_error(&err);
+	g_clear_error(&error);
 }
 
 void
-purple_log_common_deleter_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
+purple_log_common_remover_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
 	GAsyncReadyCallback cb, gpointer userdata)
 {
-	_purple_logger_deleter_callback_data *callback_data;
 	GSimpleAsyncResult *simple;
 
-	g_return_if_fail(log != NULL);
+	g_return_if_fail(PURPLE_IS_LOG(log));
 
-	callback_data = g_new0(_purple_logger_deleter_callback_data, 1);
-	callback_data->log = log;
+	simple = g_simple_async_result_new(G_OBJECT(log), cb, userdata, purple_log_common_remover_async);
 
-	simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_common_deleter_async);
-
-	g_simple_async_result_set_op_res_gpointer(simple, callback_data, g_free);
-	g_simple_async_result_run_in_thread(simple, deleter_thread, io_priority, cancellable);
-
+	g_simple_async_result_run_in_thread(simple, remover_thread, io_priority, cancellable);
 	g_object_unref(simple);
 }
 
 gboolean
-purple_log_common_deleter_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+purple_log_common_remover_finish(PurpleLog *log, GAsyncResult *res, GError **error)
 {
 	GSimpleAsyncResult *simple;
 
+	g_return_val_if_fail(PURPLE_IS_LOG(log), FALSE);
+	g_return_val_if_fail(G_IS_SIMPLE_ASYNC_RESULT(res), FALSE);
+
 	simple = G_SIMPLE_ASYNC_RESULT(res);
 
 	if (g_simple_async_result_propagate_error(simple, error))
 		return FALSE;
 
-	g_return_val_if_fail(g_simple_async_result_get_source_tag(simple) == purple_log_common_deleter_async, FALSE);
-
 	return g_simple_async_result_get_op_res_gboolean(simple);
 }
 
 gboolean
-purple_log_common_is_deletable(PurpleLog *log)
+purple_log_common_is_removable(PurpleLog *log, GCancellable *cancellable, GError **error)
 {
 	PurpleLogCommonLoggerData *data;
-
-	g_return_val_if_fail(log != NULL, FALSE);
-
-	data = log->logger_data;
-
-	if (data == NULL || data->path == NULL)
-		return FALSE;
-
-#ifndef G_OS_WIN32
-	{
-		gchar *dirname = g_path_get_dirname(data->path);
-
-		if (g_access(dirname, W_OK) == 0) {
-			g_free(dirname);
-			return TRUE;
-		}
-
-		purple_debug_info("log", "access(%s) failed: %s\n", dirname, g_strerror(errno));
-		g_free(dirname);
-
-		return FALSE;
-	}
-#else
-	/* Unless and until someone writes equivalent win32 code,
-	 * we'll assume the file is deletable. */
-	return TRUE;
-#endif
-}
-
-static gchar *
-process_txt_log(gchar *txt, gchar *to_free)
-{
-	gchar *tmp;
-
-	/* The to_free argument allows us to save a
-	 * g_strdup() in some cases. */
-
-	if (to_free == NULL)
-		to_free = txt;
-
-	/* g_markup_escape_text requires valid UTF-8 */
-	if (!g_utf8_validate(txt, -1, NULL))
-	{
-		tmp = purple_utf8_salvage(txt);
-		g_free(to_free);
-		to_free = txt = tmp;
-	}
-
-	tmp = g_markup_escape_text(txt, -1);
-	g_free(to_free);
-	txt = purple_markup_linkify(tmp);
-	g_free(tmp);
-
-	return txt;
-}
-
-#if 0 /* Maybe some other time. */
-/****************
- ** XML LOGGER **
- ****************/
-
-static const gchar *
-str_from_msg_type (PurpleMessageFlags type)
-{
-	return "";
-}
-
-static void
-xml_logger_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
-	const gchar *message)
-{
-	gchar *xhtml = NULL;
-
-	if (!log->logger_data) {
-		/* 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.
-		 */
-		struct tm *tm;
-		const gchar *tz;
-		const gchar *date;
-		gchar *dir = purple_log_get_log_dir(log->type, log->name, log->account);
-		gchar *name;
-		gchar *filename;
-
-		if (dir == NULL)
-			return;
-
-		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);
-
-		name = g_strdup_printf("%s%s%s", date, tz, ext ? ext : "");
-
-		purple_build_dir (dir, S_IRUSR | S_IWUSR | S_IXUSR);
-
-		filename = g_build_filename(dir, name, NULL);
-		g_free(dir);
-		g_free(name);
-
-		log->logger_data = g_fopen(filename, "a");
-		if (!log->logger_data) {
-			purple_debug(PURPLE_DEBUG_ERROR, "log", "Could not create log file %s\n", filename);
-			g_free(filename);
-			return;
-		}
-		g_free(filename);
-		fprintf(log->logger_data, "<?xml version='1.0' encoding='UTF-8' ?>\n"
-			"<?xml-stylesheet href='file:///usr/src/web/htdocs/log-stylesheet.xsl' type='text/xml' ?>\n");
-
-		date = purple_utf8_strftime("%Y-%m-%d %H:%M:%S", localtime(&log->time));
-		fprintf(log->logger_data, "<conversation time='%s' screenname='%s' protocol='%s'>\n",
-			date, log->name, prpl);
-	}
-
-	/* if we can't write to the file, give up before we hurt ourselves */
-	if(!data->file)
-		return;
-
-	date = log_get_timestamp(log, time);
-
-	purple_markup_html_to_xhtml(message, &xhtml, NULL);
-	if (from)
-		fprintf(log->logger_data, "<message %s %s from='%s' time='%s'>%s</message>\n",
-			str_from_msg_type(type),
-			type & PURPLE_MESSAGE_SEND ? "direction='sent'" :
-			type & PURPLE_MESSAGE_RECV ? "direction='received'" : "",
-			from, date, xhtml);
-	else
-		fprintf(log->logger_data, "<message %s %s time='%s'>%s</message>\n",
-			str_from_msg_type(type),
-			type & PURPLE_MESSAGE_SEND ? "direction='sent'" :
-			type & PURPLE_MESSAGE_RECV ? "direction='received'" : "",
-			date, xhtml):
-	fflush(log->logger_data);
-	g_free(date);
-	g_free(xhtml);
-}
-
- static void
- xml_logger_finalize(PurpleLog *log)
-{
-	if (log->logger_data) {
-		fprintf(log->logger_data, "</conversation>\n");
-		fclose(log->logger_data);
-		log->logger_data = NULL;
-	}
-}
-
-static GList *
-xml_logger_list(PurpleLogType type, const gchar *sn, PurpleAccount *account)
-{
-	return purple_log_common_lister(type, sn, account, ".xml", &xml_logger);
-}
-
-static PurpleLogLogger xml_logger =  {
-	N_("XML"), "xml",
-	NULL,
-	xml_logger_write,
-	xml_logger_finalize,
-	xml_logger_list,
-	NULL,
-	NULL,
-	NULL
-};
-#endif
-
-/****************************
- ** HTML LOGGER *************
- ****************************/
-
-static gsize
-html_logger_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
-	const gchar *message)
-{
-	PurpleLogCommonLoggerData *data;
-	PurplePlugin *plugin;
-	const gchar *date_full, *prpl;
-	gchar *date, *escaped_from, *header, *image_corrected_msg, *msg_fixed;
-	gsize written;
-
-	plugin = purple_find_prpl(purple_account_get_protocol_id(log->account));
-	data = log->logger_data;
-	written = 0;
-
-	if(!data) {
-		prpl = PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL);
-		purple_log_common_writer(log, ".html");
-
-		data = log->logger_data;
-
-		/* if we can't write to the file, give up before we hurt ourselves */
-		if(!data->file)
-			return 0;
-
-		date_full = purple_date_format_full(localtime(&log->time));
-
-		written += fprintf(data->file, "<html><head>");
-		written += fprintf(data->file, "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">");
-		written += fprintf(data->file, "<title>");
-
-		if (log->type == PURPLE_LOG_SYSTEM)
-			header = g_strdup_printf("System log for account %s (%s) connected at %s",
-				purple_account_get_username(log->account), prpl, date_full);
-		else
-			header = g_strdup_printf("Conversation with %s at %s on %s (%s)",
-				log->name, date_full, purple_account_get_username(log->account), prpl);
-
-		written += fprintf(data->file, "%s", header);
-		written += fprintf(data->file, "</title></head><body>");
-		written += fprintf(data->file, "<h3>%s</h3>\n", header);
-		g_free(header);
-	}
-
-	/* if we can't write to the file, give up before we hurt ourselves */
-	if(!data->file)
-		return 0;
-
-	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 != message)
-		g_free(image_corrected_msg);
-
-	date = log_get_timestamp(log, time);
-
-	if(log->type == PURPLE_LOG_SYSTEM){
-		written += fprintf(data->file, "---- %s @ %s ----<br/>\n", msg_fixed, date);
-	} else {
-		if (type & PURPLE_MESSAGE_SYSTEM)
-			written += fprintf(data->file, "<font size=\"2\">(%s)</font><b> %s</b><br/>\n", date, msg_fixed);
-		else if (type & PURPLE_MESSAGE_RAW)
-			written += fprintf(data->file, "<font size=\"2\">(%s)</font> %s<br/>\n", date, msg_fixed);
-		else if (type & PURPLE_MESSAGE_ERROR)
-			written += fprintf(data->file, "<font color=\"#FF0000\"><font size=\"2\">(%s)</font><b> %s</b></font><br/>\n", date, msg_fixed);
-		else if (type & PURPLE_MESSAGE_WHISPER)
-			written += fprintf(data->file, "<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)
-				written += fprintf(data->file, _("<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b></font> %s<br/>\n"), date, escaped_from, msg_fixed);
-			else if (type & PURPLE_MESSAGE_RECV)
-				written += fprintf(data->file, _("<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b></font> %s<br/>\n"), date, escaped_from, msg_fixed);
-		} else if (type & PURPLE_MESSAGE_RECV) {
-			if(purple_message_meify(msg_fixed, -1))
-				written += fprintf(data->file, "<font color=\"#062585\"><font size=\"2\">(%s)</font> <b>***%s</b></font> %s<br/>\n",
-					date, escaped_from, msg_fixed);
-			else
-				written += fprintf(data->file, "<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) {
-			if(purple_message_meify(msg_fixed, -1))
-				written += fprintf(data->file, "<font color=\"#062585\"><font size=\"2\">(%s)</font> <b>***%s</b></font> %s<br/>\n",
-					date, escaped_from, msg_fixed);
-			else
-				written += fprintf(data->file, "<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s:</b></font> %s<br/>\n",
-					date, escaped_from, msg_fixed);
-		} else {
-			purple_debug_error("log", "Unhandled message type.\n");
-			written += fprintf(data->file, "<font size=\"2\">(%s)</font><b> %s:</b></font> %s<br/>\n",
-				date, escaped_from, msg_fixed);
-		}
-	}
-
-	g_free(date);
-	g_free(msg_fixed);
-	g_free(escaped_from);
-	fflush(data->file);
-
-	return written;
-}
-
-static void
-html_logger_write_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
-{
-	_purple_log_logger_write_callback_data *callback_data =
-		g_simple_async_result_get_op_res_gpointer(simple);
-	PurpleLog *log = callback_data->log;
-	PurpleLogCommonLoggerData *data = log->logger_data;
-	PurpleLogType type = callback_data->type;
-	GError *err = NULL;
 	GFile *file;
-	GFileOutputStream *stream;
-	GOutputStream *out_stream;
-	gchar *date, *escaped_from, *message = callback_data->message, *from = callback_data->from;
-	gchar *image_corrected_msg, *msg_fixed, *line;
-	gssize written, size = 0;
-	gboolean write_header;
+	GFileInfo *info;
+	gboolean ret;
 
-	if (data == 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.
-		 */
-		purple_log_common_writer(log, ".html");
-		data = log->logger_data;
-		write_header = TRUE;
-	} else
-		write_header = FALSE;
+	g_return_val_if_fail(PURPLE_IS_LOG(log), FALSE);
 
-	/* If we can't write to the file, give up before we hurt ourselves */
-	if (data == NULL || data->path == NULL) {
-		g_simple_async_result_set_error(simple,
-			G_IO_ERROR,
-			G_IO_ERROR_FAILED,
-			_("Unable to find log path"));
+	data = purple_log_get_logger_data(log);
 
-		return;
-	}
-
-	file = g_file_new_for_path(data->path);
-	stream = g_file_append_to(file, G_FILE_CREATE_NONE, cancellable, &err);
-
-	if (stream == NULL) {
-		g_simple_async_result_set_from_error(simple, err);
-
-		g_object_unref(file);
-		g_clear_error(&err);
-
-		return;
-	}
-
-	g_clear_error(&err);
-	out_stream = G_OUTPUT_STREAM(stream);
-
-	if (write_header) {
-		PurplePlugin *plugin;
-		const gchar *prpl;
-		const gchar *date_full;
-		gchar *header;
-
-		plugin = purple_find_prpl(purple_account_get_protocol_id(log->account));
-		prpl = PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL);
-		date_full = purple_date_format_full(localtime(&log->time));
-
-		if (log->type == PURPLE_LOG_SYSTEM)
-			header = g_strdup_printf("System log for account %s (%s) connected at %s",
-				purple_account_get_username(log->account), prpl, date_full);
-		else
-			header = g_strdup_printf("Conversation with %s at %s on %s (%s)",
-				log->name, date_full,
-				purple_account_get_username(log->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",
-			header, header);
-
-		g_free(header);
-
-		written = g_output_stream_write(out_stream, line,
-			strlen(line), cancellable, &err);
-		g_free(line);
-
-		if (written < 0) {
-			g_simple_async_result_set_from_error(simple, err);
-
-			g_object_unref(file);
-			g_object_unref(stream);
-			g_clear_error(&err);
-
-			return;
-		}
-
-		g_clear_error(&err);
-		size += written;
-	}
-
-	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 != message)
-		g_free(image_corrected_msg);
-
-	date = log_get_timestamp(log, callback_data->time);
-
-	if(log->type == PURPLE_LOG_SYSTEM){
-		line = g_strdup_printf("---- %s @ %s ----<br/>\n", msg_fixed, date);
-	} else {
-		if (type & 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)
-			line = g_strdup_printf("<font size=\"2\">(%s)</font> %s<br/>\n",
-				date, msg_fixed);
-		else if (type & 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)
-			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)
-				line = g_strdup_printf(_("<font color=\"#16569E\"><font "
-					"size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b>"
-					"</font> %s<br/>\n"), date, escaped_from, msg_fixed);
-			else
-				line = g_strdup_printf(_("<font color=\"#A82F2F\"><font "
-					"size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b>"
-					"</font> %s<br/>\n"), date, escaped_from, msg_fixed);
-		} else if (type & 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",
-					date, escaped_from, msg_fixed);
-			else
-				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) {
-			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",
-					date, escaped_from, msg_fixed);
-			else
-				line = g_strdup_printf("<font color=\"#16569E\"><font "
-					"size=\"2\">(%s)</font> <b>%s:</b></font> %s<br/>\n",
-					date, escaped_from, msg_fixed);
-		} else {
-			line = g_strdup_printf("<font size=\"2\">(%s)</font>"
-				"<b> %s:</b></font> %s<br/>\n",
-				date, escaped_from, msg_fixed);
-		}
-	}
-
-	written = g_output_stream_write(out_stream, line, strlen(line),
-		cancellable, &err);
-
-	if (written < 0)
-		g_simple_async_result_set_from_error(simple, err);
-	else
-		g_simple_async_result_set_op_res_gssize(simple, size + written);
-
-	g_clear_error(&err);
-	g_free(date);
-	g_free(msg_fixed);
-	g_free(escaped_from);
-	g_object_unref(file);
-	g_object_unref(stream);
-}
-
-static void
-html_logger_write_async(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
-	const gchar *message, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb,
-	gpointer userdata)
-{
-	_purple_log_logger_write_callback_data *callback_data;
-	GSimpleAsyncResult *simple;
-
-	callback_data = g_new0(_purple_log_logger_write_callback_data, 1);
-	callback_data->log = log;
-	callback_data->type = type;
-	callback_data->from = g_strdup(from);
-	callback_data->time = time;
-	callback_data->message = g_strdup(message);
-
-	simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_write_async);
-
-	g_simple_async_result_set_op_res_gpointer(simple, callback_data,
-		purple_log_logger_write_callback_data_free);
-	g_simple_async_result_run_in_thread(simple, html_logger_write_thread,
-		io_priority, cancellable);
-
-	g_object_unref(simple);
-}
-
-static void
-html_logger_finalize(PurpleLog *log)
-{
-	PurpleLogCommonLoggerData *data = log->logger_data;
-
-	if (data != NULL) {
-		if (data->file != NULL) {
-			fprintf(data->file, "</body></html>\n");
-			fclose(data->file);
-		}
-
-		g_free(data->path);
-		g_slice_free(PurpleLogCommonLoggerData, data);
-	}
-}
-
-static GList *
-html_logger_list(PurpleLogType type, const gchar *sn, PurpleAccount *account)
-{
-	GList *list;
-
-	G_LOCK(html_logger);
-	list = purple_log_common_lister(type, sn, account, ".html", html_logger);
-	G_UNLOCK(html_logger);
-
-	return list;
-}
-
-static void
-html_logger_list_async(PurpleLogType type, const gchar *sn, PurpleAccount *account,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
-{
-	G_LOCK(html_logger);
-	purple_log_common_lister_async(type, sn, account, ".html", html_logger,
-		io_priority, cancellable, cb, userdata);
-	G_UNLOCK(html_logger);
-}
-
-static GList *
-html_logger_list_syslog(PurpleAccount *account)
-{
-	GList *list;
-
-	G_LOCK(html_logger);
-	list = purple_log_common_lister(PURPLE_LOG_SYSTEM, ".system", account, ".html", html_logger);
-	G_UNLOCK(html_logger);
-
-	return list;
-}
-
-static void
-html_logger_list_syslog_async(PurpleAccount *account, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
-{
-	G_LOCK(html_logger);
-	purple_log_common_lister_async(PURPLE_LOG_SYSTEM, ".system", account, ".html", html_logger,
-		io_priority, cancellable, cb, userdata);
-	G_UNLOCK(html_logger);
-}
-
-static gchar *
-html_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
-{
-	PurpleLogCommonLoggerData *data;
-	gchar *read, *minus_header;
-
-	data = log->logger_data;
-
-	if (flags != NULL)
-		*flags = PURPLE_LOG_READ_NO_NEWLINE;
-
-	if (!data || !data->path)
-		return g_strdup_printf("<font color=\"red\"><b>%s</b></font>",
-			_("Unable to find log path"));
-
-	if (g_file_get_contents(data->path, &read, NULL, NULL)) {
-		minus_header = strchr(read, '\n');
-
-		if (!minus_header)
-			return read;
-
-		minus_header = g_strdup(minus_header + 1);
-		g_free(read);
-
-		return minus_header;
-	}
-
-	return g_strdup_printf("<font color=\"red\"><b>%s: %s</b></font>",
-		_("Could not read file"), data->path);
-}
-
-static void
-html_logger_read_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
-{
-	_purple_log_logger_read_callback_data *callback_data =
-		g_simple_async_result_get_op_res_gpointer(simple);
-	PurpleLog *log = callback_data->log;
-	PurpleLogCommonLoggerData *data = log->logger_data;
-	PurpleLogReadFlags *flags = callback_data->flags;
-	GError *err = NULL;
-	GFile *file;
-	gchar *read, *minus_header;
-
-	if (flags != NULL)
-		*flags = PURPLE_LOG_READ_NO_NEWLINE;
-
 	if (data == NULL || data->path == NULL) {
-		g_simple_async_result_set_error(simple,
-			G_FILE_ERROR,
-			G_IO_ERROR_NOT_FOUND,
-			"<font color=\"red\"><b>%s</b></font>",
-			_("Unable to find log path"));
-
-		return;
-	}
-
-	file = g_file_new_for_path(data->path);
-
-	if (!g_file_load_contents(file, cancellable, &read, NULL, NULL, &err)) {
-		g_simple_async_result_set_error(simple,
-			err->domain,
-			err->code,
-			"<font color=\"red\"><b>%s</b></font>",
-			err->message);
-
-		g_clear_error(&err);
-
-		return;
-	}
-
-	g_clear_error(&err);
-
-	minus_header = strchr(read, '\n');
-
-	if (minus_header == NULL)
-		minus_header = read;
-	else {
-		minus_header = g_strdup(minus_header + 1);
-		g_free(read);
-	}
-
-	purple_str_strip_char(minus_header, '\r');
-
-	g_simple_async_result_set_op_res_gpointer(simple, minus_header, g_free);
-}
-
-static void
-html_logger_read_async(PurpleLog *log, PurpleLogReadFlags *flags, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
-{
-	_purple_log_logger_read_callback_data *callback_data;
-	GSimpleAsyncResult *simple;
-
-	callback_data = g_new0(_purple_log_logger_read_callback_data, 1);
-	callback_data->flags = flags;
-	callback_data->log = log;
-
-	simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_read_async);
-
-	g_simple_async_result_set_op_res_gpointer(simple, callback_data, g_free);
-	g_simple_async_result_run_in_thread(simple, html_logger_read_thread,
-		io_priority, cancellable);
-
-	g_object_unref(simple);
-}
-
-static gint
-html_logger_total_size(PurpleLogType type, const gchar *name, PurpleAccount *account)
-{
-	return purple_log_common_total_sizer(type, name, account, ".html");
-}
-
-static void
-html_logger_total_size_async(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
-{
-	purple_log_common_total_sizer_async(type, name, account, ".html",
-		io_priority, cancellable, cb, userdata);
-}
-
-
-/****************************
- ** PLAIN TEXT LOGGER *******
- ****************************/
-
-static gsize
-txt_logger_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
-	const gchar *message)
-{
-	PurplePlugin *plugin;
-	PurpleLogCommonLoggerData *data;
-	const gchar *prpl;
-	gchar *stripped, *date;
-	gsize written;
-
-	plugin = purple_find_prpl(purple_account_get_protocol_id(log->account));
-	data = log->logger_data;
-	stripped = NULL;
-	written = 0;
-
-	if (data == 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.
-		 */
-		prpl = PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL);
-		purple_log_common_writer(log, ".txt");
-
-		data = log->logger_data;
-
-		/* if we can't write to the file, give up before we hurt ourselves */
-		if(!data->file)
-			return 0;
-
-		if (log->type == PURPLE_LOG_SYSTEM)
-			written += fprintf(data->file, "System log for account %s (%s) connected at %s\n",
-				purple_account_get_username(log->account), prpl,
-				purple_date_format_full(localtime(&log->time)));
-		else
-			written += fprintf(data->file, "Conversation with %s at %s on %s (%s)\n",
-				log->name, purple_date_format_full(localtime(&log->time)),
-				purple_account_get_username(log->account), prpl);
-	}
-
-	/* if we can't write to the file, give up before we hurt ourselves */
-	if(!data->file)
-		return 0;
-
-	stripped = purple_markup_strip_html(message);
-	date = log_get_timestamp(log, time);
-
-	if(log->type == PURPLE_LOG_SYSTEM){
-		written += fprintf(data->file, "---- %s @ %s ----\n", stripped, date);
-	} else {
-		if (type & PURPLE_MESSAGE_SEND ||
-			type & PURPLE_MESSAGE_RECV) {
-			if (type & PURPLE_MESSAGE_AUTO_RESP) {
-				written += fprintf(data->file, _("(%s) %s <AUTO-REPLY>: %s\n"), date,
-					from, stripped);
-			} else {
-				if (purple_message_meify(stripped, -1))
-					written += fprintf(data->file, "(%s) ***%s %s\n", date, from, stripped);
-				else
-					written += fprintf(data->file, "(%s) %s: %s\n", date, from, stripped);
-			}
-		} else if (type & PURPLE_MESSAGE_SYSTEM ||
-			type & PURPLE_MESSAGE_ERROR ||
-			type & PURPLE_MESSAGE_RAW)
-			written += fprintf(data->file, "(%s) %s\n", date, stripped);
-		else if (type & PURPLE_MESSAGE_NO_LOG) {
-			/* This shouldn't happen */
-			g_free(stripped);
-
-			return written;
-		} else if (type & PURPLE_MESSAGE_WHISPER)
-			written += fprintf(data->file, "(%s) *%s* %s", date, from, stripped);
-		else
-			written += fprintf(data->file, "(%s) %s%s %s\n", date, from ? from : "",
-				from ? ":" : "", stripped);
-	}
-
-	g_free(date);
-	g_free(stripped);
-	fflush(data->file);
-
-	return written;
-}
-
-static void
-txt_logger_write_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
-{
-	_purple_log_logger_write_callback_data *callback_data =
-		g_simple_async_result_get_op_res_gpointer(simple);
-	PurpleLog *log = callback_data->log;
-	PurpleLogCommonLoggerData *data = log->logger_data;
-	PurpleLogType type = callback_data->type;
-	GError *err = NULL;
-	GFile *file;
-	GFileOutputStream *stream;
-	GOutputStream *out_stream;
-	const gchar *from = callback_data->from, *message = callback_data->message;
-	gchar *stripped = NULL, *date, *line;
-	gssize written, size = 0;
-	gboolean write_header;
-
-	if (type & PURPLE_MESSAGE_NO_LOG) {
-		g_simple_async_result_set_error(simple,
+		g_set_error_literal(error,
 			G_IO_ERROR,
 			G_IO_ERROR_FAILED,
-			_("Trying to log when flagged not to"));
+			_("Unable to get log path"));
 
-		return;
+		return FALSE;
 	}
 
-	if (data == 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.
-		 */
-		purple_log_common_writer(log, ".txt");
-		data = log->logger_data;
-		write_header = TRUE;
-	} else
-		write_header = FALSE;
-
-	/* If we can't write to the file, give up before we hurt ourselves */
-	if (data == NULL || data->path == NULL) {
-		g_simple_async_result_set_error(simple,
-			G_IO_ERROR,
-			G_IO_ERROR_FAILED,
-			_("Unable to find log path"));
-
-		return;
-	}
-
+	//file = data->file;
 	file = g_file_new_for_path(data->path);
-	stream = g_file_append_to(file, G_FILE_CREATE_NONE, cancellable, &err);
+	info = g_file_query_info(file, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
+		G_FILE_QUERY_INFO_NONE, cancellable, error);
 
-	if (stream == NULL) {
-		g_simple_async_result_set_from_error(simple, err);
-
+	if (info == NULL) {
 		g_object_unref(file);
-		g_clear_error(&err);
-
-		return;
+		return FALSE;
 	}
 
-	g_clear_error(&err);
-	out_stream = G_OUTPUT_STREAM(stream);
+	ret = g_file_info_get_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE);
 
-	if (write_header) {
-		PurplePlugin *plugin;
-		const gchar *prpl;
-
-		plugin = purple_find_prpl(purple_account_get_protocol_id(log->account));
-		prpl = PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL);
-
-		if (log->type == PURPLE_LOG_SYSTEM)
-			line = g_strdup_printf("System log for account %s (%s) connected at %s\n",
-				purple_account_get_username(log->account), prpl,
-				purple_date_format_full(localtime(&log->time)));
-		else
-			line = g_strdup_printf("Conversation with %s at %s on %s (%s)\n",
-				log->name, purple_date_format_full(localtime(&log->time)),
-				purple_account_get_username(log->account), prpl);
-
-		written = g_output_stream_write(out_stream, line,
-			strlen(line), cancellable, &err);
-		g_free(line);
-
-		if (written < 0) {
-			g_simple_async_result_set_from_error(simple, err);
-
-			g_object_unref(file);
-			g_object_unref(stream);
-			g_clear_error(&err);
-
-			return;
-		}
-
-		g_clear_error(&err);
-		size += written;
-	}
-
-	stripped = purple_markup_strip_html(message);
-	date = log_get_timestamp(log, callback_data->time);
-
-	if (log->type == PURPLE_LOG_SYSTEM)
-		line = g_strdup_printf("---- %s @ %s ----\n", stripped, date);
-	else {
-		if (type & PURPLE_MESSAGE_SEND ||
-			type & PURPLE_MESSAGE_RECV)
-		{
-			if (type & PURPLE_MESSAGE_AUTO_RESP)
-				line = g_strdup_printf(_("(%s) %s <AUTO-REPLY>: %s\n"), date, from, 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 (type & PURPLE_MESSAGE_SYSTEM ||
-			type & PURPLE_MESSAGE_ERROR ||
-			type & PURPLE_MESSAGE_RAW)
-			line = g_strdup_printf("(%s) %s\n", date, stripped);
-		else if (type & PURPLE_MESSAGE_WHISPER)
-			line = g_strdup_printf("(%s) *%s* %s", date, from, stripped);
-		else
-			line = g_strdup_printf("(%s) %s%s %s\n", date, from ? from : "",
-				from ? ":" : "", stripped);
-	}
-
-	written = g_output_stream_write(out_stream, line, strlen(line),
-		cancellable, &err);
-
-	if (written < 0)
-		g_simple_async_result_set_from_error(simple, err);
-	else
-		g_simple_async_result_set_op_res_gssize(simple, size + written);
-
-	g_clear_error(&err);
-	g_free(line);
-	g_free(date);
-	g_free(stripped);
+	g_object_unref(info);
 	g_object_unref(file);
-	g_object_unref(stream);
-}
 
-static void
-txt_logger_write_async(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
-	const gchar *message, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb,
-	gpointer userdata)
-{
-	_purple_log_logger_write_callback_data *callback_data;
-	GSimpleAsyncResult *simple;
-
-	callback_data = g_new0(_purple_log_logger_write_callback_data, 1);
-	callback_data->log = log;
-	callback_data->type = type;
-	callback_data->from = g_strdup(from);
-	callback_data->time = time;
-	callback_data->message = g_strdup(message);
-
-	simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_write_async);
-
-	g_simple_async_result_set_op_res_gpointer(simple, callback_data,
-		purple_log_logger_write_callback_data_free);
-	g_simple_async_result_run_in_thread(simple, txt_logger_write_thread,
-		io_priority, cancellable);
-
-	g_object_unref(simple);
+	return ret;
 }
 
 static void
-txt_logger_finalize(PurpleLog *log)
+purple_log_finalize(GObject *object)
 {
-	PurpleLogCommonLoggerData *data = log->logger_data;
+	PurpleLog *log = PURPLE_LOG(object);
+	PurpleLogPrivate *priv = PURPLE_LOG_GET_PRIVATE(log);
 
-	if (data != NULL) {
-		if (data->file != NULL)
-			fclose(data->file);
+	g_free(priv->name);
 
-		g_free(data->path);
-		g_slice_free(PurpleLogCommonLoggerData, data);
-	}
-}
-
-static GList *
-txt_logger_list(PurpleLogType type, const gchar *sn, PurpleAccount *account)
-{
-	GList *list;
-
-	G_LOCK(txt_logger);
-	list = purple_log_common_lister(type, sn, account, ".txt", txt_logger);
-	G_UNLOCK(txt_logger);
-
-	return list;
-}
-
-static void
-txt_logger_list_async(PurpleLogType type, const gchar *sn, PurpleAccount *account,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
-{
-	G_LOCK(txt_logger);
-	purple_log_common_lister_async(type, sn, account, ".txt", txt_logger,
-		io_priority, cancellable, cb, userdata);
-	G_UNLOCK(txt_logger);
-}
-
-static GList *
-txt_logger_list_syslog(PurpleAccount *account)
-{
-	GList *list;
-
-	G_LOCK(txt_logger);
-	list = purple_log_common_lister(PURPLE_LOG_SYSTEM, ".system", account, ".txt", txt_logger);
-	G_UNLOCK(txt_logger);
-
-	return list;
-}
-
-static void
-txt_logger_list_syslog_async(PurpleAccount *account, gint io_priority, GCancellable *cancellable,
-	GAsyncReadyCallback cb, gpointer userdata)
-{
-	G_LOCK(txt_logger);
-	purple_log_common_lister_async(PURPLE_LOG_SYSTEM, ".system", account, ".txt", txt_logger,
-		io_priority, cancellable, cb, userdata);
-	G_UNLOCK(txt_logger);
-}
-
-static gchar *
-txt_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
-{
-	PurpleLogCommonLoggerData *data;
-	gchar *read, *minus_header;
-
-	data = log->logger_data;
-
-	if (flags != NULL)
-		*flags = 0;
-
-	if (!data || !data->path)
-		return g_strdup_printf("<font color=\"red\"><b>%s</b></font>",
-			_("Unable to find log path"));
-
-	if (g_file_get_contents(data->path, &read, NULL, NULL)) {
-		minus_header = strchr(read, '\n');
-
-		if (minus_header)
-			return process_txt_log(minus_header + 1, read);
-		else
-			return process_txt_log(read, NULL);
-	}
-
-	return g_strdup_printf("<font color=\"red\"><b>%s: %s</b></font>",
-		_("Could not read file"), data->path);
-}
-
-static void
-txt_logger_read_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
-{
-	_purple_log_logger_read_callback_data *callback_data =
-		g_simple_async_result_get_op_res_gpointer(simple);
-	PurpleLog *log = callback_data->log;
-	PurpleLogCommonLoggerData *data = log->logger_data;
-	PurpleLogReadFlags *flags = callback_data->flags;
-	GError *err = NULL;
-	GFile *file;
-	gchar *read, *minus_header;
-
-	if (flags != NULL)
-		*flags = 0;
-
-	if (data == NULL || data->path == NULL) {
-		g_simple_async_result_set_error(simple,
-			G_FILE_ERROR,
-			G_IO_ERROR_NOT_FOUND,
-			"<font color=\"red\"><b>%s</b></font>",
-			_("Unable to find log path"));
-
-		return;
-	}
-
-	file = g_file_new_for_path(data->path);
-
-	if (!g_file_load_contents(file, cancellable, &read, NULL, NULL, &err)) {
-		g_simple_async_result_set_error(simple,
-			err->domain,
-			err->code,
-			"<font color=\"red\"><b>%s</b></font>",
-			err->message);
-
-		g_clear_error(&err);
-
-		return;
-	}
-
-	g_clear_error(&err);
-
-	minus_header = strchr(read, '\n');
-
-	if (minus_header != NULL)
-		read = process_txt_log(minus_header + 1, read);
-	else
-		read = process_txt_log(read, NULL);
-
-	purple_str_strip_char(read, '\r');
-
-	g_simple_async_result_set_op_res_gpointer(simple, read, g_free);
-}
-
-static void
-txt_logger_read_async(PurpleLog *log, PurpleLogReadFlags *flags, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
-{
-	_purple_log_logger_read_callback_data *callback_data;
-	GSimpleAsyncResult *simple;
-
-	callback_data = g_new0(_purple_log_logger_read_callback_data, 1);
-	callback_data->flags = flags;
-	callback_data->log = log;
-
-	simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_read_async);
-
-	g_simple_async_result_set_op_res_gpointer(simple, callback_data, g_free);
-	g_simple_async_result_run_in_thread(simple, txt_logger_read_thread,
-		io_priority, cancellable);
-
-	g_object_unref(simple);
-}
-
-static gint
-txt_logger_total_size(PurpleLogType type, const gchar *name, PurpleAccount *account)
-{
-	return purple_log_common_total_sizer(type, name, account, ".txt");
-}
-
-static void
-txt_logger_total_size_async(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
-{
-	purple_log_common_total_sizer_async(type, name, account, ".txt",
-		io_priority, cancellable, cb, userdata);
-}
-
-
-/****************
- * OLD LOGGER ***
- ****************/
-
-/* The old logger doesn't write logs, only reads them.  This is to include
- * old logs in the log viewer transparently.
- */
-
-typedef struct {
-	PurpleStringref *pathref;
-	gint offset;
-	gint length;
-} old_logger_data;
-
-static GList *
-old_logger_list(PurpleLogType type, const gchar *sn, PurpleAccount *account)
-{
-	old_logger_data *data;
-	PurpleStringref *pathref;
-	PurpleLog *log;
-	GList *list;
-	FILE *index, *file;
-	time_t log_last_modified, lasttime;
-	struct stat st;
-	struct tm tm;
-	gchar *logfile, *pathstr, *index_tmp, buf[BUF_LONG], month[4], convostart[32], *temp;
-	gint index_fd, logfound, lastoff, newlen, length, offset;
-	gulong idx_time;
-
-	logfile = g_strdup_printf("%s.log", purple_normalize(account, sn));
-	pathstr = g_build_filename(purple_user_dir(), "logs", logfile, NULL);
-	pathref = purple_stringref_new(pathstr);
-	g_free(logfile);
-
-	log = NULL;
-	list = NULL;
-	data = NULL;
-
-	logfound = 0;
-	lastoff = 0;
-	lasttime = 0;
-
-	if (g_stat(purple_stringref_value(pathref), &st)) {
-		purple_stringref_unref(pathref);
-		g_free(pathstr);
-
-		return NULL;
-	} else
-		log_last_modified = st.st_mtime;
-
-	/* Change the .log extension to .idx */
-	strcpy(pathstr + strlen(pathstr) - 3, "idx");
-
-	if (g_stat(pathstr, &st) == 0) {
-		if (st.st_mtime < log_last_modified) {
-			purple_debug_warning("log", "Index \"%s\" exists, but is older than the log.\n", pathstr);
-		} else {
-			/* The index file exists and is at least as new as the log, so open it. */
-			if (!(index = g_fopen(pathstr, "rb")))
-			{
-				purple_debug_error("log", "Failed to open index file \"%s\" for reading: %s\n",
-					pathstr, g_strerror(errno));
-
-				/* Fall through so that we'll parse the log file. */
-			} else {
-				purple_debug_info("log", "Using index: %s\n", pathstr);
-				g_free(pathstr);
-
-				while (fgets(buf, BUF_LONG, index)) {
-					if (sscanf(buf, "%d\t%d\t%lu", &lastoff, &newlen, &idx_time) == 3) {
-						log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, -1, NULL);
-						log->time = (time_t) idx_time;
-
-						G_LOCK(old_logger);
-						log->logger = old_logger;
-						G_UNLOCK(old_logger);
-
-						/* IMPORTANT: Always set all members of old_logger_data */
-						data = g_slice_new(old_logger_data);
-
-						data->pathref = purple_stringref_ref(pathref);
-						data->offset = lastoff;
-						data->length = newlen;
-
-						log->logger_data = data;
-						list = g_list_prepend(list, log);
-					}
-				}
-
-				fclose(index);
-				purple_stringref_unref(pathref);
-
-				return list;
-			}
-		}
-	}
-
-	if (!(file = g_fopen(purple_stringref_value(pathref), "rb"))) {
-		purple_debug_error("log", "Failed to open log file \"%s\" for reading: %s\n",
-			purple_stringref_value(pathref), g_strerror(errno));
-		purple_stringref_unref(pathref);
-		g_free(pathstr);
-
-		return NULL;
-	}
-
-	index_tmp = g_strdup_printf("%s.XXXXXX", pathstr);
-
-	if ((index_fd = g_mkstemp(index_tmp)) == -1) {
-		purple_debug_error("log", "Failed to open index temp file: %s\n",
-			g_strerror(errno));
-
-		g_free(pathstr);
-		g_free(index_tmp);
-		index = NULL;
-	} else {
-		index = fdopen(index_fd, "wb");
-
-		if (index == NULL) {
-			purple_debug_error("log", "Failed to fdopen() index temp file: %s\n",
-				g_strerror(errno));
-			close(index_fd);
-
-			if (index_tmp != NULL) {
-				g_unlink(index_tmp);
-				g_free(index_tmp);
-			}
-
-			g_free(pathstr);
-		}
-	}
-
-	while (fgets(buf, BUF_LONG, file)) {
-		if (strstr(buf, "---- New C") != NULL) {
-			temp = strchr(buf, '@');
-
-			if (temp == NULL || strlen(temp) < 2)
-				continue;
-
-			temp++;
-			length = strcspn(temp, "-");
-			if (length > 31) length = 31;
-
-			offset = ftell(file);
-
-			if (logfound) {
-				newlen = offset - lastoff - length;
-				if(strstr(buf, "----</H3><BR>")) {
-					newlen -=
-						sizeof("<HR><BR><H3 Align=Center> ---- New Conversation @ ") +
-						sizeof("----</H3><BR>") - 2;
-				} else {
-					newlen -= sizeof("---- New Conversation @ ") + sizeof("----") - 2;
-				}
-
-				if(strchr(buf, '\r'))
-					newlen--;
-
-				if (newlen != 0) {
-					log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, -1, NULL);
-					log->time = lasttime;
-
-					G_LOCK(old_logger);
-					log->logger = old_logger;
-					G_UNLOCK(old_logger);
-
-					/* IMPORTANT: Always set all members of old_logger_data */
-					data = g_slice_new(old_logger_data);
-
-					data->pathref = purple_stringref_ref(pathref);
-					data->offset = lastoff;
-					data->length = newlen;
-
-					log->logger_data = data;
-					list = g_list_prepend(list, log);
-
-					/* XXX: There is apparently Is there a proper way to print a time_t? */
-					if (index != NULL)
-						fprintf(index, "%d\t%d\t%lu\n", data->offset, data->length,
-							(unsigned long)log->time);
-				}
-			}
-
-			logfound = 1;
-			lastoff = offset;
-
-			g_snprintf(convostart, length, "%s", temp);
-			memset(&tm, 0, sizeof(tm));
-			sscanf(convostart, "%*s %s %d %d:%d:%d %d", month, &tm.tm_mday,
-				&tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tm.tm_year);
-
-			/* Ugly hack, in case current locale is not English */
-			if (purple_strequal(month, "Jan")) {
-				tm.tm_mon= 0;
-			} else if (purple_strequal(month, "Feb")) {
-				tm.tm_mon = 1;
-			} else if (purple_strequal(month, "Mar")) {
-				tm.tm_mon = 2;
-			} else if (purple_strequal(month, "Apr")) {
-				tm.tm_mon = 3;
-			} else if (purple_strequal(month, "May")) {
-				tm.tm_mon = 4;
-			} else if (purple_strequal(month, "Jun")) {
-				tm.tm_mon = 5;
-			} else if (purple_strequal(month, "Jul")) {
-				tm.tm_mon = 6;
-			} else if (purple_strequal(month, "Aug")) {
-				tm.tm_mon = 7;
-			} else if (purple_strequal(month, "Sep")) {
-				tm.tm_mon = 8;
-			} else if (purple_strequal(month, "Oct")) {
-				tm.tm_mon = 9;
-			} else if (purple_strequal(month, "Nov")) {
-				tm.tm_mon = 10;
-			} else if (purple_strequal(month, "Dec")) {
-				tm.tm_mon = 11;
-			}
-			tm.tm_year -= 1900;
-			lasttime = mktime(&tm);
-		}
-	}
-
-	if (logfound) {
-		if ((newlen = ftell(file) - lastoff) != 0) {
-			log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, -1, NULL);
-			log->time = lasttime;
-
-			G_LOCK(old_logger);
-			log->logger = old_logger;
-			G_UNLOCK(old_logger);
-
-			/* IMPORTANT: Always set all members of old_logger_data */
-			data = g_slice_new(old_logger_data);
-
-			data->pathref = purple_stringref_ref(pathref);
-			data->offset = lastoff;
-			data->length = newlen;
-
-			log->logger_data = data;
-			list = g_list_prepend(list, log);
-
-			/* XXX: Is there a proper way to print a time_t? */
-			if (index != NULL)
-				fprintf(index, "%d\t%d\t%d\n", data->offset, data->length, (gint) log->time);
-		}
-	}
-
-	purple_stringref_unref(pathref);
-	fclose(file);
-
-	if (index != NULL) {
-		fclose(index);
-
-		if (index_tmp == NULL) {
-			g_free(pathstr);
-			g_return_val_if_reached(list);
-		}
-
-		if (g_rename(index_tmp, pathstr)) {
-			purple_debug_warning("log", "Failed to rename index temp file \"%s\" to \"%s\": %s\n",
-				index_tmp, pathstr, g_strerror(errno));
-			g_unlink(index_tmp);
-		} else
-			purple_debug_info("log", "Built index: %s\n", pathstr);
-
-		g_free(index_tmp);
-		g_free(pathstr);
-	}
-
-	return list;
-}
-
-static gint
-old_logger_total_size(PurpleLogType type, const gchar *name, PurpleAccount *account)
-{
-	gchar *logfile, *pathstr;
-	gint size;
-	struct stat st;
-
-	logfile = g_strdup_printf("%s.log", purple_normalize(account, name));
-	pathstr = g_build_filename(purple_user_dir(), "logs", logfile, NULL);
-
-	if (g_stat(pathstr, &st))
-		size = 0;
-	else
-		size = st.st_size;
-
-	g_free(logfile);
-	g_free(pathstr);
-
-	return size;
-}
-
-static gchar *
-old_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
-{
-	size_t result;
-	old_logger_data *data;
-	const gchar *path;
-	FILE *file;
-	gchar *read;
-
-	data = log->logger_data;
-	path = purple_stringref_value(data->pathref);
-	file = g_fopen(path, "rb");
-	read = g_malloc(data->length + 1);
-
-	fseek(file, data->offset, SEEK_SET);
-	result = fread(read, data->length, 1, file);
-
-	if (result != 1)
-		purple_debug_error("log", "Unable to read from log file: %s\n", path);
-
-	fclose(file);
-	read[data->length] = '\0';
-
-	if (flags != NULL)
-		*flags = 0;
-
-	if (strstr(read, "<BR>")) {
-		if (flags != NULL)
-			*flags |= PURPLE_LOG_READ_NO_NEWLINE;
-
-		return read;
-	}
-
-	return process_txt_log(read, NULL);
-}
-
-static gint
-old_logger_size(PurpleLog *log)
-{
-	old_logger_data *data;
-
-	data = log->logger_data;
-
-	return data ? data->length : 0;
-}
-
-static void
-old_logger_get_log_sets(PurpleLogSetCallback cb, GHashTable *sets)
-{
-	PurpleBlistNode *gnode, *cnode, *bnode;
-	PurpleBuddy *buddy;
-	PurpleLogSet *set;
-	GDir *log_dir;
-	gchar *log_path, *name, *tmp, *ext;
-	size_t len;
-	gboolean found;
-
-	log_path = g_build_filename(purple_user_dir(), "logs", NULL);
-	log_dir = g_dir_open(log_path, 0, NULL);
-	g_free(log_path);
-
-	if (log_dir == NULL)
-		return;
-
-	/* Don't worry about the cast, name will be filled with a dynamically allocated data shortly. */
-	while ((name = (gchar *) g_dir_read_name(log_dir)) != NULL) {
-		found = FALSE;
-
-		/* Unescape the filename. */
-		name = g_strdup(purple_unescape_filename(name));
-
-		/* Get the (possibly new) length of name. */
-		len = strlen(name);
-
-		if (len < 5) {
-			g_free(name);
-			continue;
-		}
-
-		/* Make sure we're dealing with a log file. */
-		ext = &name[len - 4];
-		if (!purple_strequal(ext, ".log")) {
-			g_free(name);
-			continue;
-		}
-
-		/* IMPORTANT: Always set all members of PurpleLogSet */
-		set = g_slice_new(PurpleLogSet);
-
-		/* Chat for .chat at the end of the name to determine the type. */
-		*ext = '\0';
-		set->type = PURPLE_LOG_IM;
-
-		if (len > 9) {
-			tmp = &name[len - 9];
-			if (purple_strequal(tmp, ".chat")) {
-				set->type = PURPLE_LOG_CHAT;
-				*tmp = '\0';
-			}
-		}
-
-		set->name = set->normalized_name = name;
-
-		/* Search the buddy list to find the account and to determine if this is a buddy. */
-		for (gnode = purple_blist_get_root();
-		     !found && gnode != NULL;
-			 gnode = purple_blist_node_get_sibling_next(gnode)) {
-			if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
-				continue;
-
-			for (cnode = purple_blist_node_get_first_child(gnode);
-			     !found && cnode != NULL;
-				 cnode = purple_blist_node_get_sibling_next(cnode)) {
-				if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
-					continue;
-
-				for (bnode = purple_blist_node_get_first_child(cnode);
-				     !found && bnode != NULL;
-					 bnode = purple_blist_node_get_sibling_next(bnode)) {
-					buddy = (PurpleBuddy *) bnode;
-
-					if (purple_strequal(purple_buddy_get_name(buddy), name)) {
-						set->account = purple_buddy_get_account(buddy);
-						set->buddy = TRUE;
-						found = TRUE;
-					}
-				}
-			}
-		}
-
-		if (!found) {
-			set->account = NULL;
-			set->buddy = FALSE;
-		}
-
-		cb(sets, set);
-	}
-
-	g_dir_close(log_dir);
-}
-
-static void
-old_logger_finalize(PurpleLog *log)
-{
-	old_logger_data *data;
-
-	data = log->logger_data;
-	purple_stringref_unref(data->pathref);
-	g_slice_free(old_logger_data, data);
-}
-
-static void
-log_delete_cb(GObject *object, GAsyncResult *res, gpointer userdata)
-{
-	_purple_log_free_callback_data *callback_data = userdata;
-	PurpleLog *log = callback_data->log;
-	GError *err = NULL;
-
-	if (callback_data->cb)
-		callback_data->cb(object, res, callback_data->userdata);
-	else if (!purple_log_delete_finish(log, res, &err) && err->code != G_IO_ERROR_CANCELLED)
-		purple_debug_error("log", "Error freeing log: %s\n", err->message);
-
-	g_clear_error(&err);
-	g_free(callback_data);
-}
-
-static void
-log_free_cb(GObject *object, GAsyncResult *res, gpointer userdata)
-{
-	PurpleLog *log = userdata;
-
-	g_free(log->name);
-
-	if (log->tm != NULL) {
+	if (priv->tm != NULL) {
 #ifdef HAVE_STRUCT_TM_TM_ZONE
 		/* XXX: This is so wrong... */
-		g_free((gchar *) log->tm->tm_zone);
+		g_free((gchar *) priv->tm->tm_zone);
 #endif
-		g_slice_free(struct tm, log->tm);
+		g_slice_free(struct tm, priv->tm);
 	}
 
 	PURPLE_DBUS_UNREGISTER_POINTER(log);
-	g_slice_free(PurpleLog, log);
+
+	G_OBJECT_CLASS(purple_log_parent_class)->finalize(object);
 }
 
 static void
 log_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
-	_purple_log_logs_callback_data *callback_data = userdata;
+	_get_logs_callback_data *callback_data = userdata;
+	PurpleLog *log = PURPLE_LOG(object);
 	GSimpleAsyncResult *simple;
 	GList *list, *n;
 
-	list = purple_log_get_logs_finish(res, NULL);
+	list = purple_log_get_logs_finish(log, res, NULL);
 
 	callback_data->logs = g_list_concat(callback_data->logs, list);
 	callback_data->counter--;
@@ -4436,7 +3498,7 @@ log_list_cb(GObject *object, GAsyncResul
 			g_object_unref(simple);
 		} else {
 			for (n = callback_data->logs; n != NULL; n = g_list_next(n))
-				purple_log_free(n->data);
+				g_object_unref(n->data);
 
 			g_list_free(callback_data->logs);
 		}
@@ -4448,11 +3510,12 @@ log_system_list_cb(GObject *object, GAsy
 static void
 log_system_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
-	_purple_log_logs_callback_data *callback_data = userdata;
+	_get_logs_callback_data *callback_data = userdata;
+	PurpleLog *log = PURPLE_LOG(object);
 	GSimpleAsyncResult *simple;
 	GList *list, *n;
 
-	list = purple_log_get_logs_finish(res, NULL);
+	list = purple_log_get_system_logs_finish(log, res, NULL);
 
 	callback_data->logs = g_list_concat(callback_data->logs, list);
 	callback_data->counter--;
@@ -4469,7 +3532,7 @@ log_system_list_cb(GObject *object, GAsy
 			g_object_unref(simple);
 		} else {
 			for (n = callback_data->logs; n != NULL; n = g_list_next(n))
-				purple_log_free(n->data);
+				g_object_unref(n->data);
 
 			g_list_free(callback_data->logs);
 		}
@@ -4482,10 +3545,11 @@ log_total_size_cb(GObject *object, GAsyn
 log_total_size_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
 	_purple_log_total_size_callback_data *callback_data = userdata;
+	PurpleLog *log = PURPLE_LOG(object);
 	GError *err = NULL;
 	gssize size;
 
-	size = purple_log_get_total_size_finish(res, &err);
+	size = purple_log_get_total_size_finish(log, res, &err);
 
 	/* It's hard to pass all the errors up, so just log them :( */
 	if (size < 0 && err != NULL)
@@ -4518,38 +3582,16 @@ static void
 }
 
 static void
-log_total_size_intermediate_cb(GObject *object, GAsyncResult *res, gpointer userdata)
-{
-	GSimpleAsyncResult *simple;
-	GError *err = NULL;
-	gssize size;
-
-	size = purple_log_get_size_finish(object, res, &err);
-
-	simple = g_simple_async_result_new(NULL, log_total_size_cb, userdata,
-		purple_log_common_total_sizer_async);
-
-	if (size < 0)
-		g_simple_async_result_set_from_error(simple, err);
-	else
-		g_simple_async_result_set_op_res_gssize(simple, size);
-
-	g_simple_async_result_complete_in_idle(simple);
-
-	g_object_unref(simple);
-	g_clear_error(&err);
-}
-
-static void
 log_total_size_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
 	/* Gets the size for all the logs in the list as part of purple_log_get_total_size_async */
 	_purple_log_total_size_callback_data *callback_data = userdata;
+	PurpleLog *log = PURPLE_LOG(object);
 	GError *err = NULL;
 	GList *list, *n;
 	GSimpleAsyncResult *simple;
 
-	list = purple_log_get_logs_finish(res, &err);
+	list = purple_log_get_logs_finish(log, res, &err);
 
 	if (list == NULL && err != NULL) {
 		simple = g_simple_async_result_new(NULL, log_total_size_cb,
@@ -4584,81 +3626,6 @@ static void
 }
 
 static void
-log_write_cb(GObject *object, GAsyncResult *res, gpointer userdata)
-{
-	_purple_log_write_callback_data *callback_data = userdata;
-	_purple_logsize_user *lu = callback_data->lu;
-	PurpleLog *log = callback_data->log;
-	GError *err = NULL;
-	gpointer ptrsize;
-	gsize total = 0;
-	gssize size;
-
-	size = purple_log_write_finish(log, res, &err);
-
-	if (size > 0) {
-		G_LOCK(logsize_users);
-		if (g_hash_table_lookup_extended(logsize_users, lu, NULL, &ptrsize)) {
-			total = GPOINTER_TO_INT(ptrsize);
-			total += size;
-			g_hash_table_replace(logsize_users, lu, GINT_TO_POINTER(total));
-		} else {
-			g_free(lu->name);
-			g_free(lu);
-		}
-		G_UNLOCK(logsize_users);
-	}
-
-	if (callback_data->cb) {
-		/* Restore the error in GSimpleAsyncResult as _finish would have cleared it */
-		if (size < 0)
-			g_simple_async_result_set_from_error(G_SIMPLE_ASYNC_RESULT(res), err);
-
-		callback_data->cb(object, res, callback_data->userdata);
-	} else if (size < 0 && err->code != G_IO_ERROR_CANCELLED)
-		purple_debug_error("log", "Error writing message: %s\n", err->message);
-
-	g_clear_error(&err);
-	g_free(callback_data);
-	purple_log_unref(log);
-}
-
-static void
-log_read_cb(GObject *object, GAsyncResult *res, gpointer userdata)
-{
-	_purple_log_read_callback_data *callback_data = userdata;
-	PurpleLog *log = callback_data->log;
-	GError *err = NULL;
-
-	if (callback_data->cb)
-		callback_data->cb(object, res, callback_data->userdata);
-	else if (purple_log_read_finish(log, res, &err) == NULL  &&
-		err->code != G_IO_ERROR_CANCELLED)
-		purple_debug_error("log", "Error reading log: %s\n", err->message);
-
-	g_clear_error(&err);
-	g_free(callback_data);
-	purple_log_unref(log);
-}
-
-static void
-log_size_cb(GObject *object, GAsyncResult *res, gpointer userdata)
-{
-	_purple_log_size_callback_data *callback_data = userdata;
-	PurpleLog *log = callback_data->log;
-	GError *err = NULL;
-
-	if (callback_data->cb)
-		callback_data->cb(object, res, callback_data->userdata);
-	else if (purple_log_get_size_finish(log, res, &err) < 0  && err->code != G_IO_ERROR_CANCELLED)
-		purple_debug_error("log", "Error sizing log: %s\n", err->message);
-
-	g_clear_error(&err);
-	g_free(callback_data);
-	purple_log_unref(log);
-}
-
-static void
 log_hash_cb(GObject *object, GAsyncResult *res, gpointer userdata)
 {
 	_purple_log_sets_callback_data *callback_data = userdata;
@@ -4685,96 +3652,3 @@ log_hash_cb(GObject *object, GAsyncResul
 	}
 }
 
-static void
-log_get_activity_score_cb(GObject *object, GAsyncResult *res, gpointer userdata)
-{
-	_purple_log_activity_score_data *callback_data = userdata;
-	_purple_log_activity_score_size_data *size_data;
-	GSimpleAsyncResult *simple;
-	PurpleLog *log;
-	GError *err = NULL;
-	GList *list;
-
-	list = purple_log_get_logs_finish(res, &err);
-
-	if (list == NULL) {
-		simple = g_simple_async_result_new_from_error(object, callback_data->cb,
-			callback_data->userdata, err);
-
-		g_simple_async_result_complete_in_idle(simple);
-
-		g_object_unref(simple);
-		g_object_unref(callback_data->cancel);
-		g_free(callback_data->lu->name);
-		g_free(callback_data->lu);
-		g_free(callback_data);
-	} else {
-		callback_data->count = g_list_length(list);
-
-		for (callback_data->total = 0.0, callback_data->count = 0; list != NULL;
-			list = g_list_delete_link(list, list))
-		{
-			log = list->data;
-
-			size_data = g_new0(_purple_log_activity_score_size_data, 1);
-			size_data->score_data = callback_data;
-			size_data->log = log;
-
-			purple_log_get_size_async(log, callback_data->io_priority,
-				callback_data->cancel, log_get_activity_score_size_cb, size_data);
-		}
-	}
-
-	g_clear_error(&err);
-}
-
-static void
-log_get_activity_score_size_cb(GObject *object, GAsyncResult *res, gpointer userdata)
-{
-	_purple_log_activity_score_size_data *callback_data = userdata;
-	_purple_log_activity_score_data *score_data = callback_data->score_data;
-	GSimpleAsyncResult *simple;
-	PurpleLog *log = callback_data->log;
-	GError *err = NULL;
-	time_t now;
-	gssize size;
-	gint score;
-
-	size = purple_log_get_size_finish(log, res, &err);
-
-	if (size < 0 && err->code != G_IO_ERROR_CANCELLED) {
-		purple_debug_error("log", "Error getting log size for activity score: %s\n",
-			err->message);
-	} else if (size > 0) {
-		/* Activity score counts bytes in the log, exponentially
-		   decayed with a half-life of 14 days. */
-		time(&now);
-		score_data->total += size * pow(0.5, difftime(now, log->time) / 1209600.0);
-	}
-
-	g_clear_error(&err);
-	purple_log_free(log);
-	g_free(callback_data);
-
-	score_data->count--;
-
-	if (score_data->count < 1) {
-		score = ceil(score_data->total);
-
-		G_LOCK(logsize_users_decayed);
-		g_hash_table_replace(logsize_users_decayed, score_data->lu,
-			GINT_TO_POINTER(score));
-		G_UNLOCK(logsize_users_decayed);
-
-		simple = g_simple_async_result_new(object, score_data->cb,
-			score_data->userdata, purple_log_get_activity_score_async);
-
-		g_simple_async_result_set_op_res_gssize(simple, score);
-		g_simple_async_result_complete_in_idle(simple);
-
-		g_object_unref(simple);
-		g_object_unref(score_data->cancel);
-		g_free(score_data);
-	}
-}
-
============================================================
--- libpurple/account.c	fc325241ba835c68c655ad8fff1f3e9e5ac39033
+++ libpurple/account.c	1a164776d6aa486560a63c569619ae198d43a66f
@@ -1095,7 +1095,7 @@ purple_account_destroy(PurpleAccount *ac
 	purple_presence_destroy(account->presence);
 
 	if(account->system_log)
-		purple_log_free(account->system_log);
+		g_object_unref(account->system_log);
 
 	while (account->deny) {
 		g_free(account->deny->data);
@@ -2469,7 +2469,7 @@ purple_account_get_log(PurpleAccount *ac
 		presence = purple_account_get_presence(account);
 		login_time = purple_presence_get_login_time(presence);
 
-		account->system_log	 = purple_log_new(PURPLE_LOG_SYSTEM,
+		account->system_log	 = purple_log_new(G_TYPE_INVALID, PURPLE_LOG_SYSTEM,
 				purple_account_get_username(account), account, NULL,
 				(login_time != 0) ? login_time : time(NULL), NULL);
 	}
@@ -2483,7 +2483,7 @@ purple_account_destroy_log(PurpleAccount
 	g_return_if_fail(account != NULL);
 
 	if(account->system_log){
-		purple_log_free(account->system_log);
+		g_object_unref(account->system_log);
 		account->system_log = NULL;
 	}
 }
============================================================
--- libpurple/log.h	3c17a97c416587ac26aa4e54eddf8355a37f5270
+++ libpurple/log.h	cabb0f6e40e0e58bfc63d830436d43461cadebad
@@ -24,28 +24,45 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
+
 #ifndef _PURPLE_LOG_H_
 #define _PURPLE_LOG_H_
 
 #include <stdio.h>
-#include <glib.h>
 #include <gio/gio.h>
 
-#include "account.h"
-#include "conversation.h"
-
-
 /** @copydoc _PurpleLog */
 typedef struct _PurpleLog PurpleLog;
-/** @copydoc _PurpleLogLogger */
-typedef struct _PurpleLogLogger PurpleLogLogger;
-/** @copydoc _PurpleLogLoggerAsyncFuncs */
-typedef struct _PurpleLogLoggerAsyncFuncs PurpleLogLoggerAsyncFuncs;
+/** @copydoc _PurpleLogClass */
+typedef struct _PurpleLogClass PurpleLogClass;
 /** @copydoc _PurpleLogCommonLoggerData */
 typedef struct _PurpleLogCommonLoggerData PurpleLogCommonLoggerData;
 /** @copydoc _PurpleLogSet */
 typedef struct _PurpleLogSet PurpleLogSet;
 
+
+#include "account.h"
+#include "conversation.h"
+
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_LOG          (purple_log_get_type())
+#define PURPLE_LOG(o)            (G_TYPE_CHECK_INSTANCE_CAST ((o), PURPLE_TYPE_LOG, PurpleLog))
+#define PURPLE_LOG_CLASS(k)      (G_TYPE_CHECK_CLASS_CAST((k), PURPLE_TYPE_LOG, PurpleLogClass))
+#define PURPLE_IS_LOG(o)	     (G_TYPE_CHECK_INSTANCE_TYPE ((o), PURPLE_TYPE_LOG))
+#define PURPLE_IS_LOG_CLASS(k)   (G_TYPE_CHECK_CLASS_TYPE((k), PURPLE_TYPE_LOG))
+#define PURPLE_LOG_GET_CLASS(o)  (G_TYPE_INSTANCE_GET_CLASS((o), PURPLE_TYPE_LOG, PurpleLogClass))
+
+// Would rather have this generated by glib-mkenums
+#define PURPLE_TYPE_LOG_CHAT_TYPE     (purple_log_chat_type_get_type())
+GType purple_log_chat_type_get_type(void);
+
+// To be used until PurpleLog changes to using a GDateTime
+#define PURPLE_TYPE_STRUCT_TM (purple_struct_tm_get_type())
+GType purple_struct_tm_get_type(void);
+
+
 /**************************************************************************/
 /* Enumerations                                                           */
 /**************************************************************************/
@@ -57,7 +74,7 @@ typedef enum {
 	PURPLE_LOG_IM,     /**< Chat log with one person */
 	PURPLE_LOG_CHAT,   /**< Group chat log */
 	PURPLE_LOG_SYSTEM  /**< System log */
-} PurpleLogType;
+} PurpleLogChatType;
 
 /**
  * Flags for purple_log_read() and purple_log_read_async()
@@ -67,9 +84,6 @@ typedef enum {
 } PurpleLogReadFlags;
 
 
-typedef void (*PurpleLogSetCallback) (GHashTable *sets, PurpleLogSet *set);
-
-
 /********************************************************
  * DATA STRUCTURES **************************************
  ********************************************************/
@@ -78,65 +92,79 @@ struct _PurpleLog {
  * A log. Not the wooden type.
  */
 struct _PurpleLog {
-	PurpleLogType type;        /**< The type of log this is */
-	gchar *name;               /**< The name of this log */
-	PurpleAccount *account;    /**< The account this log is taking place on */
-	PurpleConversation *conv;  /**< The conversation being logged */
-	time_t time;               /**< The time this conversation started, converted to the local timezone */
-	PurpleLogLogger *logger;   /**< The logging mechanism this log is to use */
-	gpointer logger_data;      /**< Data used by the log logger */
+	GObject parent_instance;
+};
 
-	// TODO: On ABI break, it would be really helpful to turn this into a GDateTime!
-	struct tm *tm;             /**< The time this conversation started, saved with original timezone data,
-								*   if available and if struct tm has the BSD timezone fields, else @c %NULL.
-								*   Do NOT modify anything in this struct.
-								*/
 
-	/* IMPORTANT: Some code in log.c allocates these without zeroing them.
-	 * IMPORTANT: Update that code if you add members here. */
-};
-
 /**
- * A log logger.
- *
- * This struct gets filled out and is included in the PurpleLog.  It contains everything
+ * This struct gets filled out and is included in the PurpleLog. It contains everything
  * needed to write and read from logs.
  */
-struct _PurpleLogLogger {
-	gchar *name;       /**< The logger's name */
-	gchar *id;         /**< An identifier to refer to this logger */
+struct _PurpleLogClass {
+	GObjectClass parent_class;
 
+	/** The logger's name */
+	const gchar *logger_name;
+
+	/** An identifier to refer to this logger */
+	const gchar *logger_id;
+
 	/** This gets called when the log is first created, likely not actually needed */
-	void (*create)(PurpleLog *log);
+	gboolean (* create_fn) (PurpleLog *log, GCancellable *cancellable, GError **error);
 
+	void (* create_async) (PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+
+	gboolean (* create_finish) (PurpleLog *log, GAsyncResult *res, GError **error);
+
 	/** This is used to write to the log file */
-	gsize (*write)(PurpleLog *log,
-		     PurpleMessageFlags type,
-		     const gchar *from,
-		     time_t time,
-		     const gchar *message);
+	gssize (* write_fn) (PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time, const gchar *message, GCancellable *cancellable, GError **error);
 
-	/** Called when the log is destroyed */
-	void (*finalize)(PurpleLog *log);
+	void (* write_async) (PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time, const gchar *message, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 
+	gssize (* write_finish) (PurpleLog *log, GAsyncResult *res, GError **error);
+
 	/** This function returns a sorted #GList of available #PurpleLog<!-- -->s */
-	GList *(*list)(PurpleLogType type, const gchar *name, PurpleAccount *account);
+	GList * (* list_fn) (PurpleLog *log, PurpleLogChatType type, const gchar *name, PurpleAccount *account, GCancellable *cancellable, GError **error);
 
+	//log is a dummy, only used to keep track of the class
+	void (* list_async) (PurpleLog *log, PurpleLogChatType type, const gchar *name, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+
+	//log is a dummy, only used to keep track of the class
+	GList * (* list_finish) (PurpleLog *log, GAsyncResult *res, GError **error);
+
 	/** Given one of the logs returned by the logger's list function,
 	 *  this returns the contents of the log in GtkIMHtml markup */
-	gchar *(*read)(PurpleLog *log, PurpleLogReadFlags *flags);
+	gchar * (* read_fn) (PurpleLog *log, PurpleLogReadFlags *flags, GCancellable *cancellable, GError **error);
 
+	void (* read_async) (PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+
+	gchar * (* read_finish) (PurpleLog *log, GAsyncResult *res, PurpleLogReadFlags *flags, GError **error);
+
 	/** Given one of the logs returned by the logger's list function,
 	 *  this returns the size of the log in bytes */
-	gint (*size)(PurpleLog *log);
+	gssize (* size_fn) (PurpleLog *log, GCancellable *cancellable, GError **error);
 
+	void (* size_async) (PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+
+	gssize (* size_finish) (PurpleLog *log, GAsyncResult *res, GError **error);
+
 	/** Returns the total size of all the logs. If this is undefined a default
 	 *  implementation is used */
-	gint (*total_size)(PurpleLogType type, const gchar *name, PurpleAccount *account);
+	gssize (* total_size_fn) (PurpleLogChatType type, const gchar *name, PurpleAccount *account, GCancellable *cancellable, GError **error);
 
+	//log is a dummy, only used to keep track of the class
+	void (* total_size_async) (PurpleLog *log, PurpleLogChatType type, const gchar *name, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+
+	//log is a dummy, only used to keep track of the class
+	gssize (* total_size_finish) (PurpleLog *log, GAsyncResult *res, GError **error);
+
 	/** This function returns a sorted #GList of available system #PurpleLog<!-- -->s */
-	GList *(*list_syslog)(PurpleAccount *account);
+	GList * (* list_syslog_fn) (PurpleLog *log, PurpleAccount *account, GCancellable *cancellable, GError **error);
 
+	void (* list_syslog_async) (PurpleLog *log, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+
+	GList * (* list_syslog_finish) (PurpleLog *log, GAsyncResult *res, GError **error);
+
 	/** Adds #PurpleLogSet<!-- -->s to a #GHashTable. By passing the data in the #PurpleLogSet<!-- -->s
 	 *  to list, the caller can get every available PurpleLog from the logger.
 	 *  Loggers using purple_log_common_writer() (or otherwise storing their
@@ -145,74 +173,26 @@ struct _PurpleLogLogger {
 	 *
 	 *  Loggers which implement this function must create a #PurpleLogSet,
 	 *  then call @a cb with @a sets and the newly created #PurpleLogSet. */
-	void (*get_log_sets)(PurpleLogSetCallback cb, GHashTable *sets);
+	GHashTable * (* get_log_sets_fn) (PurpleLog *log, GCancellable *cancellable, GError **error);
 
-	/* Attempts to delete the specified log, indicating success or failure */
-	gboolean (*remove)(PurpleLog *log);
+	void (* get_log_sets_async) (PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 
-	/* Tests whether a log is deletable */
-	gboolean (*is_deletable)(PurpleLog *log);
+	GHashTable * (* get_log_sets_finish) (PurpleLog *log, GAsyncResult *res, GError **error);
 
-	/** A pointer to a #PurpleLogLoggerAsyncFuncs structure containing the non-blocking
-	 *  versions of the calls above */
-	// Note: This will only be used up until the 3.x API break, when we will create
-	// threads that directly call the blocking methods, which will be altered to accept
-	// cancellable/error objects
-	PurpleLogLoggerAsyncFuncs *async;
+	/** Attempts to remove the specified log, indicating success or failure */
+	gboolean (* remove_fn) (PurpleLog *log, GCancellable *cancellable, GError **error);
 
-	void (*_purple_reserved2)(void);
-	void (*_purple_reserved3)(void);
-	void (*_purple_reserved4)(void);
-};
+	void (* remove_async) (PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 
-/**
- * This struct contains the non-blocking versions of the #PurpleLogLogger functions
- */
-struct _PurpleLogLoggerAsyncFuncs {
-	// TODO: We could probably drop this. There's really no reason to use it.
-	// TODO: If we do, we should deprecate create at the same time.
-	/** Non-blocking version of create */
-	void (*create_async)(PurpleLog *log, gint io_priority, GCancellable *cancellable,
-		GAsyncReadyCallback cb, gpointer data);
+	gboolean (* remove_finish) (PurpleLog *log, GAsyncResult *res, GError **error);
 
-	/** Non-blocking version of write */
-	void (*write_async)(PurpleLog *log, PurpleMessageFlags type, const gchar *from,
-		time_t time, const gchar *message, gint io_priority, GCancellable *cancellable,
-		GAsyncReadyCallback cb, gpointer data);
+	/** Tests whether a log is removable */
+	gboolean (* is_removable_fn) (PurpleLog *log, GCancellable *cancellable, GError **error);
 
-	/** Non-blocking version of finalize */
-	void (*finalize_async)(PurpleLog *log, gint io_priority, GCancellable *cancellable,
-		GAsyncReadyCallback cb, gpointer data);
+	void (* is_removable_async) (PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 
-	/** Non-blocking version of list */
-	void (*list_async)(PurpleLogType type, const gchar *name, PurpleAccount *account,
-		gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer data);
+	gboolean (* is_removable_finish) (PurpleLog *log, GAsyncResult *res, GError **error);
 
-	/** Non-blocking version of read */
-	void (*read_async)(PurpleLog *log, PurpleLogReadFlags *flags, gint io_priority,
-		GCancellable *cancellable, GAsyncReadyCallback cb, gpointer data);
-
-	/** Non-blocking version of size */
-	void (*size_async)(PurpleLog *log, gint io_priority, GCancellable *cancellable,
-		GAsyncReadyCallback cb, gpointer data);
-
-	/** Non-blocking version of total_size */
-	void (*total_size_async)(PurpleLogType type, const gchar *name, PurpleAccount *account,
-		gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer data);
-
-	/** Non-blocking version of list_syslog */
-	void (*list_syslog_async)(PurpleAccount *account, gint io_priority,
-		GCancellable *cancellable, GAsyncReadyCallback cb, gpointer data);
-
-	/** Non-blocking version of get_log_sets */
-	void (*get_log_sets_async)(PurpleLogSetCallback set_cb, GHashTable *sets,
-		gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer data);
-
-	/** Non-blocking version of remove */
-	void (*remove_async)(PurpleLog *log, gint io_priority, GCancellable *cancellable,
-		GAsyncReadyCallback cb, gpointer data);
-
-
 	void (*_purple_reserved1)(void);
 	void (*_purple_reserved2)(void);
 	void (*_purple_reserved3)(void);
@@ -236,7 +216,7 @@ struct _PurpleLogSet {
  * can get all available #PurpleLog<!-- -->s.
  */
 struct _PurpleLogSet {
-	PurpleLogType type;        /**< The type of logs available */
+	PurpleLogChatType type;        /**< The type of logs available */
 	gchar *name;               /**< The name of the logs available */
 	PurpleAccount *account;    /**< The account the available logs took place on.
 								*   This will be @c %NULL if the account no longer exists.
@@ -252,18 +232,19 @@ struct _PurpleLogSet {
 	 * IMPORTANT: Update that code if you add members here. */
 };
 
-G_BEGIN_DECLS
-
 /***************************************/
 /** @name Log Functions                */
 /***************************************/
 
 /*@{*/
 
+GType purple_log_get_type(void);
+
 /**
  * Creates a new log
  *
- * @param type         The type of log this is
+ * @param log_type     The type for the log class, must extend PURPLE_LOG_TYPE
+ * @param chat_type    The type of log this is
  * @param name         The name of this conversation (buddy name, chat name, etc.)
  * @param account      The account the conversation is occurring on
  * @param conv         (allow-none): The conversation being logged
@@ -273,16 +254,39 @@ G_BEGIN_DECLS
  *
  * @return             The new log
  */
-PurpleLog *purple_log_new(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	PurpleConversation *conv, time_t time, const struct tm *tm);
+PurpleLog *purple_log_new(GType log_type, PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account, PurpleConversation *conv, time_t time, const struct tm *tm);
 
-/**
- * Frees a log
- *
- * @param log          The log to destroy
- */
-void purple_log_free(PurpleLog *log);
+//void purple_log_create?
 
+//
+void purple_log_set_logger_data(PurpleLog *log, gpointer logger_data);
+//
+PurpleLogChatType purple_log_get_chat_type(const PurpleLog *log);
+//
+G_CONST_RETURN gchar *purple_log_get_name(const PurpleLog *log);
+//
+PurpleAccount *purple_log_get_account(const PurpleLog *log);
+//
+time_t purple_log_get_time(const PurpleLog *log);
+//
+PurpleConversation *purple_log_get_conversation(const PurpleLog *log);
+//
+gpointer purple_log_get_logger_data(const PurpleLog *log);
+//
+G_CONST_RETURN struct tm *purple_log_get_tm(const PurpleLog *log);
+//
+gchar *_log_get_timestamp(PurpleLog *log, time_t when);
+//
+gchar *_convert_image_tags(const PurpleLog *log, const gchar *msg);
+
+//
+gboolean purple_log_create(PurpleLog *log, GCancellable *cancellable, GError **error);
+//
+void purple_log_create_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+//
+gboolean purple_log_create_finish(PurpleLog *log, GAsyncResult *res, GError **error);
+
+
 /**
  * Writes to a log file. Assumes you have checked preferences already.
  *
@@ -292,9 +296,10 @@ void purple_log_free(PurpleLog *log);
  *                     system messages
  * @param time         A timestamp in UNIX time
  * @param message      The message to log
+ * @param cancellable  (allow-none): #GCancellable object
+ * @param error        (out) (allow-none): a #GError location to store the error
  */
-void purple_log_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from,
-	time_t time, const gchar *message);
+gssize purple_log_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time, const gchar *message, GCancellable *cancellable, GError **error);
 
 /**
  * Writes to a log file asychronously. Assumes you have checked preferences already.
@@ -303,7 +308,7 @@ void purple_log_write(PurpleLog *log, Pu
  * @param type         The type of message being logged
  * @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 deleted in function)
+ * @param message      The message to log (will be removed in function)
  * @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
@@ -311,9 +316,7 @@ void purple_log_write(PurpleLog *log, Pu
  *
  * @since 2.8.0
  */
-void purple_log_write_async(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
-	const gchar *message, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb,
-	gpointer userdata);
+void purple_log_write_async(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time, const gchar *message, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finishing asynchronously writing to a log
@@ -333,16 +336,18 @@ gssize purple_log_write_finish(PurpleLog
  *
  * @param log          The log to read from
  * @param flags        (allow-none): Location to store the returned logging flags
+ * @param cancellable  (allow-none): #GCancellable object
+ * @param error        (out) (allow-none): a #GError location to store the error
  *
  * @return             The contents of this log in Purple Markup
  */
-gchar *purple_log_read(PurpleLog *log, PurpleLogReadFlags *flags);
+// NOTE: Error does not have HTML formatting applied
+gchar *purple_log_read(PurpleLog *log, PurpleLogReadFlags *flags, GCancellable *cancellable, GError **error);
 
 /**
  * Reads from a log asynchronously
  *
  * @param log          The log to read from
- * @param flags        (allow-none): Location to store the returned logging flags
  * @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
@@ -350,8 +355,7 @@ gchar *purple_log_read(PurpleLog *log, P
  *
  * @since 2.8.0
  */
-void purple_log_read_async(PurpleLog *log, PurpleLogReadFlags *flags, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+void purple_log_read_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finishes asynchronously reading a log
@@ -360,13 +364,14 @@ void purple_log_read_async(PurpleLog *lo
  *
  * @param log          The #PurpleLog that was read
  * @param res          A #GAsyncResult
+ * @param flags        (out) (allow-none): Location to store the returned logging flags
  * @param error        (out) (allow-none): a #GError location to store the error
  *
  * @return pointer to contents of file or %NULL on error
  *
  * @since 2.8.0
  */
-gchar *purple_log_read_finish(PurpleLog *log, GAsyncResult *res, GError **error);
+gchar *purple_log_read_finish(PurpleLog *log, GAsyncResult *res, PurpleLogReadFlags *flags, GError **error);
 
 /**
  * Returns a list of all available logs
@@ -377,7 +382,8 @@ gchar *purple_log_read_finish(PurpleLog 
  *
  * @return             A sorted list of #PurpleLog<!-- -->s
  */
-GList *purple_log_get_logs(PurpleLogType type, const gchar *name, PurpleAccount *account);
+GList *purple_log_get_logs(PurpleLog *log, PurpleLogChatType type, const gchar *name, PurpleAccount *account, GCancellable *cancellable, GError **error);
+GList *purple_logs_get_logs(PurpleLogChatType type, const gchar *name, PurpleAccount *account, GCancellable *cancellable, GError **error);
 
 /**
  * Asynchronously gets a list of all available logs
@@ -392,8 +398,8 @@ GList *purple_log_get_logs(PurpleLogType
  *
  * @since 2.8.0
  */
-void purple_log_get_logs_async(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+void purple_log_get_logs_async(PurpleLog *log, PurpleLogChatType type, const gchar *name, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+void purple_logs_get_logs_async(PurpleLogChatType type, const gchar *name, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finishes asynchronously getting a list of all available logs
@@ -408,7 +414,7 @@ void purple_log_get_logs_async(PurpleLog
  *
  * @since 2.8.0
  */
-GList *purple_log_get_logs_finish(GAsyncResult *res, GError **error);
+GList *purple_log_get_logs_finish(PurpleLog *log, GAsyncResult *res, GError **error);
 
 /**
  * Returns a GHashTable of #PurpleLogSet<!-- -->s.
@@ -426,7 +432,8 @@ GList *purple_log_get_logs_finish(GAsync
  *
  * @return             A GHashTable of all available unique #PurpleLogSet<!-- -->s
  */
-GHashTable *purple_log_get_log_sets(void);
+GHashTable *purple_log_get_log_sets(PurpleLog *, GCancellable *cancellable, GError **error);
+GHashTable *purple_logs_get_log_sets(GCancellable *cancellable, GError **error);
 
 /**
  * Asychronously gets a #GHashTable of #PurpleLogSet<!-- -->s.
@@ -441,9 +448,9 @@ GHashTable *purple_log_get_log_sets(void
  *
  * @since 2.8.0
  */
-void purple_log_get_log_sets_async(gint io_priority, GCancellable *cancellable,
-	GAsyncReadyCallback cb, gpointer userdata);
+void purple_logs_get_log_sets_async(gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
+void purple_log_get_log_sets_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 /**
  * Finishes asynchronously getting a hash table of log sets
  *
@@ -454,7 +461,7 @@ void purple_log_get_log_sets_async(gint 
  *
  * @since 2.8.0
  */
-GHashTable *purple_log_get_log_sets_finish(GAsyncResult *res, GError **error);
+GHashTable *purple_log_get_log_sets_finish(PurpleLog *log, GAsyncResult *res, GError **error);
 
 /**
  * Returns a list of all available system logs
@@ -463,7 +470,8 @@ GHashTable *purple_log_get_log_sets_fini
  *
  * @return             A sorted list of #PurpleLog<!-- -->s
  */
-GList *purple_log_get_system_logs(PurpleAccount *account);
+GList *purple_log_get_system_logs(PurpleLog *log, PurpleAccount *account, GCancellable *cancellable, GError **error);
+GList *purple_logs_get_system_logs(PurpleAccount *account, GCancellable *cancellable, GError **error);
 
 /**
  * Asychronously gets a list of all available system logs
@@ -476,8 +484,8 @@ GList *purple_log_get_system_logs(Purple
  *
  * @since 2.8.0
  */
-void purple_log_get_system_logs_async(PurpleAccount *account, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+void purple_log_get_system_logs_async(PurpleLog *log, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+void purple_logs_get_system_logs_async(PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finishes asychronously getting a list of all available system logs
@@ -492,7 +500,7 @@ void purple_log_get_system_logs_async(Pu
  *
  * @since 2.8.0
  */
-GList *purple_log_get_system_logs_finish(GAsyncResult *res, GError **error);
+GList *purple_log_get_system_logs_finish(PurpleLog *log, GAsyncResult *res, GError **error);
 
 /**
  * Returns the size of a log
@@ -501,7 +509,7 @@ GList *purple_log_get_system_logs_finish
  *
  * @return             The size of the log, in bytes
  */
-gint purple_log_get_size(PurpleLog *log);
+gint purple_log_get_size(PurpleLog *log, GCancellable *cancellable, GError **error);
 
 /**
  * Asychronously gets the size of a log
@@ -514,8 +522,7 @@ gint purple_log_get_size(PurpleLog *log)
  *
  * @since 2.8.0
  */
-void purple_log_get_size_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
-	GAsyncReadyCallback cb, gpointer userdata);
+void purple_log_get_size_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finishes asynchronously getting the size of a log
@@ -539,7 +546,7 @@ gssize purple_log_get_size_finish(Purple
  *
  * @return             The size in bytes
  */
-gint purple_log_get_total_size(PurpleLogType type, const gchar *name, PurpleAccount *account);
+gssize purple_logs_get_total_size(PurpleLogChatType type, const gchar *name, PurpleAccount *account, GCancellable *cancellable, GError **error);
 
 /**
  * Asychronously gets the size, in bytes, of all available logs in this conversation
@@ -554,8 +561,7 @@ gint purple_log_get_total_size(PurpleLog
  *
  * @since 2.8.0
  */
-void purple_log_get_total_size_async(PurpleLogType type, const gchar *name, PurpleAccount *account, 
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+void purple_logs_get_total_size_async(PurpleLogChatType type, const gchar *name, PurpleAccount *account,  gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finishes asynchronously getting the total size of a conversation
@@ -567,7 +573,7 @@ void purple_log_get_total_size_async(Pur
  *
  * @since 2.8.0
  */
-gssize purple_log_get_total_size_finish(GAsyncResult *res, GError **error);
+gssize purple_log_get_total_size_finish(PurpleLog *log, GAsyncResult *res, GError **error);
 
 /**
  * Returns the activity score of a log, based on total size in bytes,
@@ -581,7 +587,7 @@ gssize purple_log_get_total_size_finish(
  *
  * @since 2.6.0
  */
-gint purple_log_get_activity_score(PurpleLogType type, const gchar *name, PurpleAccount *account);
+gssize purple_log_get_activity_score(PurpleLogChatType type, const gchar *name, PurpleAccount *account, GCancellable *cancellable, GError **error);
 
 /**
  * Asychronously gets the activity score of a log
@@ -596,8 +602,7 @@ gint purple_log_get_activity_score(Purpl
  *
  * @since 2.8.0
  */
-void purple_log_get_activity_score_async(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+void purple_log_get_activity_score_async(PurpleLogChatType type, const gchar *name, PurpleAccount *account, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finishes asynchronously getting the activity score of a log
@@ -612,29 +617,33 @@ gint purple_log_get_activity_score_finis
 gint purple_log_get_activity_score_finish(GAsyncResult *res, GError **error);
 
 /**
- * Tests whether a log is deletable
+ * Tests whether a log is removable
  *
- * A return value of @c FALSE indicates that purple_log_delete() will fail on this
+ * A return value of @c FALSE indicates that purple_log_remove() will fail on this
  * log, unless something changes between the two calls.  A return value of @c TRUE,
- * however, does not guarantee the log can be deleted.
+ * however, does not guarantee the log can be removed.
  *
  * @param log          The log
  *
- * @return             %TRUE if the log is deletable, %FALSE otherwise
+ * @return             %TRUE if the log is removable, %FALSE otherwise
  */
-gboolean purple_log_is_deletable(PurpleLog *log);
+gboolean purple_log_is_removable(PurpleLog *log, GCancellable *cancellable, GError **error);
 
+void purple_log_is_removable_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+
+gboolean purple_log_is_removable_finish(PurpleLog *log, GAsyncResult *res, GError **error);
+
 /**
- * Deletes a log
+ * Removes a log
  *
  * @param log          The log
  *
- * @return             %TRUE if the log was deleted, %FALSE otherwise
+ * @return             %TRUE if the log was removed, %FALSE otherwise
  */
-gboolean purple_log_delete(PurpleLog *log);
+gboolean purple_log_remove(PurpleLog *log, GCancellable *cancellable, GError **error);
 
 /**
- * Asychronously deletes a log
+ * Asychronously removes a log
  *
  * @param log          The log
  * @param io_priority  The io priority of the request
@@ -644,21 +653,20 @@ gboolean purple_log_delete(PurpleLog *lo
  *
  * @since 2.8.0
  */
-void purple_log_delete_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
-	GAsyncReadyCallback cb, gpointer userdata);
+void purple_log_remove_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finishes asynchronously deleting a log
  *
- * @param log          The #PurpleLog that was to be deleted
+ * @param log          The #PurpleLog that was to be removed
  * @param res          A #GAsyncResult
  * @param error        (out) (allow-none): a #GError location to store the error
  *
- * @return             %TRUE if the log was deleted, %FALSE otherwise
+ * @return             %TRUE if the log was removed, %FALSE otherwise
  *
  * @since 2.8.0
  */
-gboolean purple_log_delete_finish(PurpleLog *log, GAsyncResult *res, GError **error);
+gboolean purple_log_remove_finish(PurpleLog *log, GAsyncResult *res, GError **error);
 
 /**
  * Returns the default logger directory Purple uses for a given account
@@ -671,7 +679,7 @@ gboolean purple_log_delete_finish(Purple
  *
  * @return             The default logger directory for Purple
  */
-gchar *purple_log_get_log_dir(PurpleLogType type, const gchar *name, PurpleAccount *account);
+gchar *purple_log_get_log_dir(PurpleLogChatType type, const gchar *name, PurpleAccount *account);
 
 /**
  * Implements GCompareFunc for #PurpleLog<!-- -->s
@@ -724,7 +732,7 @@ void purple_log_set_free(PurpleLogSet *s
  * @param log          The log to write to
  * @param ext          (allow-none): The file extension to give to this log file
  */
-void purple_log_common_writer(PurpleLog *log, const gchar *ext);
+gboolean purple_log_common_writer(PurpleLog *log, const gchar *ext, GCancellable *cancellable, GError **error);
 
 /**
  * Asynchronously opens a new log in the standard log location
@@ -739,8 +747,7 @@ void purple_log_common_writer(PurpleLog 
  * @param cb           (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
  * @param userdata     (allow-none): The data to pass to callback function
  */
-void purple_log_common_writer_async(PurpleLog *log, const gchar *ext, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+void purple_log_common_writer_async(PurpleLog *log, const gchar *ext, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Asynchronously opens a new log in the standard log location
@@ -769,12 +776,11 @@ gboolean purple_log_common_writer_finish
  * @param name         The name of the log
  * @param account      The account of the log
  * @param ext          The file extension this log format uses
- * @param logger       A reference to the logger struct for this log
+ * @param log_type     The log type to create the new logs with, or G_TYPE_INVALID to use the default type
  *
  * @return             A sorted #GList of #PurpleLog<!-- -->s matching the parameters
  */
-GList *purple_log_common_lister(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	const gchar *ext, PurpleLogLogger *logger);
+GList *purple_log_common_lister(PurpleLogChatType type, const gchar *name, PurpleAccount *account, const gchar *ext, GType log_type, GCancellable *cancellable, GError **error);
 
 /**
  * Asynchronously gets a sorted #GList of #PurpleLog<!-- -->s of the requested type
@@ -786,7 +792,7 @@ GList *purple_log_common_lister(PurpleLo
  * @param name         The name of the log
  * @param account      The account of the log
  * @param ext          The file extension this log format uses
- * @param logger       A reference to the logger struct for this log
+ * @param log_type     The log type to create the new logs with, or G_TYPE_INVALID to use the default type
  * @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
@@ -794,9 +800,7 @@ GList *purple_log_common_lister(PurpleLo
  *
  * @since 2.8.0
  */
-void purple_log_common_lister_async(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	const gchar *ext, PurpleLogLogger *logger, gint io_priority,
-	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+void purple_log_common_lister_async(PurpleLogChatType type, const gchar *name, PurpleAccount *account, const gchar *ext, GType log_type, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finishes asynchronously lister listing the #PurpleLog<!-- -->s of a requested type.
@@ -833,8 +837,7 @@ GList *purple_log_common_lister_finish(G
  * @return             The size of all the logs with the specified extension
  *                     for the specified user
  */
-gint purple_log_common_total_sizer(PurpleLogType type, const gchar *name, PurpleAccount *account,
-	const gchar *ext);
+gssize purple_log_common_total_sizer(PurpleLogChatType purple_log_common_total_sizer_async, const gchar *name, PurpleAccount *account, const gchar *ext, GCancellable *cancellable, GError **error);
 
 /**
  * Asychronously gets the total size of all the logs for a given user, with
@@ -854,9 +857,7 @@ gint purple_log_common_total_sizer(Purpl
  *
  * @since 2.8.0
  */
-void purple_log_common_total_sizer_async(PurpleLogType type, const gchar *name,
-	PurpleAccount *account, const gchar *ext, gint io_priority, GCancellable *cancellable,
-	GAsyncReadyCallback cb, gpointer userdata);
+void purple_log_common_total_sizer_async(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account, const gchar *ext, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finishes asynchronously getting the total size of logs for a given user
@@ -883,7 +884,7 @@ gssize purple_log_common_total_sizer_fin
  *
  * @return             An integer indicating the size of the log in bytes
  */
-gint purple_log_common_sizer(PurpleLog *log);
+gssize purple_log_common_sizer(PurpleLog *log, GCancellable *cancellable, GError **error);
 
 /**
  * Asychronously gets the size of a given #PurpleLog
@@ -899,8 +900,7 @@ gint purple_log_common_sizer(PurpleLog *
  *
  * @since 2.8.0
  */
-void purple_log_common_sizer_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
-	GAsyncReadyCallback cb, gpointer userdata);
+void purple_log_common_sizer_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finishes asynchronously getting the size of a log
@@ -916,30 +916,30 @@ gssize purple_log_common_sizer_finish(Pu
 gssize purple_log_common_sizer_finish(PurpleLog *log, GAsyncResult *res, GError **error);
 
 /**
- * Deletes a log
+ * Removes a log
  *
  * This function should only be used with logs that are written
  * with purple_log_common_writer().  It's intended to be used as
- * a "common" implementation of a logger's @c delete function.
+ * a "common" implementation of a logger's @c remove function.
  * It should only be passed to purple_log_logger_new() and never
  * called directly.
  *
- * @param log          The #PurpleLog to delete
+ * @param log          The #PurpleLog to remove
  *
- * @return             %TRUE if the log was deleted, %FALSE otherwise
+ * @return             %TRUE if the log was removed, %FALSE otherwise
  */
-gboolean purple_log_common_deleter(PurpleLog *log);
+gboolean purple_log_common_remover(PurpleLog *log, GCancellable *cancellable, GError **error);
 
 /**
- * Asynchronously deletes a log
+ * Asynchronously removes a log
  *
  * This function should only be used with logs that are written
  * with purple_log_common_writer().  It's intended to be used as
- * a "common" implementation of a logger's @c delete function.
+ * a "common" implementation of a logger's @c remove function.
  * It should only be passed to purple_log_logger_new() and never
  * called directly.
  *
- * @param log          The #PurpleLog to delete
+ * @param log          The #PurpleLog to remove
  * @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
@@ -947,37 +947,39 @@ gboolean purple_log_common_deleter(Purpl
  *
  * @since 2.8.0
  */
-void purple_log_common_deleter_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
-	GAsyncReadyCallback cb, gpointer userdata);
+void purple_log_common_remover_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
 
 /**
  * Finish asynchronously deleting a log
  *
- * @param log          The #PurpleLog to delete
+ * @param log          The #PurpleLog to remove
  * @param res          A #GAsyncResult
  * @param error        (out) (allow-none): a #GError location to store the error
  *
- * @return             %TRUE if the log was deleted, %FALSE otherwise
+ * @return             %TRUE if the log was removed, %FALSE otherwise
  *
  * @since 2.8.0
  */
-gboolean purple_log_common_deleter_finish(PurpleLog *log, GAsyncResult *res, GError **error);
+gboolean purple_log_common_remover_finish(PurpleLog *log, GAsyncResult *res, GError **error);
 
 /**
- * Checks to see if a log is deletable
+ * Checks to see if a log is removable
  *
  * This function should only be used with logs that are written
  * with purple_log_common_writer().  It's intended to be used as
- * a "common" implementation of a logger's @c is_deletable function.
+ * a "common" implementation of a logger's @c is_removable function.
  * It should only be passed to purple_log_logger_new() and never
  * called directly.
  *
  * @param log         The #PurpleLog to check
  *
- * @return            %TRUE if the log is deletable, %FALSE otherwise
+ * @return            %TRUE if the log is removable, %FALSE otherwise
  */
-gboolean purple_log_common_is_deletable(PurpleLog *log);
+gboolean purple_log_common_is_removable(PurpleLog *log, GCancellable *cancellable, GError **error);
 
+void purple_log_common_is_removable_async(PurpleLog *log, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+gboolean purple_log_common_is_removable_finish(PurpleLog *log, GAsyncResult *res, GError **error);
+
 /*@}*/
 
 /******************************************/
@@ -987,70 +989,41 @@ gboolean purple_log_common_is_deletable(
 /*@{*/
 
 /**
- * Creates a new logger
- *
- * @param id          The logger's id
- * @param name        The logger's name
- * @param functions   The number of functions being passed. The following
- *                    functions are currently available (in order): @c create,
- *                    @c write, @c finalize, @c list, @c read, @c size,
- *                    @c total_size, @c list_syslog, @c get_log_sets,
- *                    @c remove, @c is_deletable.
- *                    For details on these functions, see PurpleLogLogger.
- *                    Functions may not be skipped. For example, passing
- *                    @c create and @c write is acceptable (for a total of
- *                    two functions). Passing @c create and @c finalize,
- *                    however, is not. To accomplish that, the caller must
- *                    pass @c create, @c %NULL (a placeholder for @c write),
- *                    and @c finalize (for a total of 3 functions).
- *
- * @return            The new logger
- */
-PurpleLogLogger *purple_log_logger_new(const gchar *id, const gchar *name, gint functions, ...);
-
-/**
- * Frees a logger
- *
- * @param logger      The logger to free
- */
-void purple_log_logger_free(PurpleLogLogger *logger);
-
-/**
  * Adds a new logger
  *
- * @param logger      The new logger to add
+ * @param log_type    The new logger to add
  */
-void purple_log_logger_add(PurpleLogLogger *logger);
+void purple_log_logger_add(GType log_type);
 
 /**
  * Removes a logger
  *
- * @param logger      The logger to remove
+ * @param log_type    The logger to remove
  */
-void purple_log_logger_remove(PurpleLogLogger *logger);
+void purple_log_logger_remove(GType log_type);
 
 /**
  * Sets the current logger
  *
- * @param logger      The logger to set
+ * @param log_type    The logger to set
  */
-void purple_log_logger_set(PurpleLogLogger *logger);
+void purple_log_logger_set(GType log_type);
 
 /**
- * Returns the current logger
+ * Gets the current default logger's type
  *
- * @return logger     The current logger
+ * @return            The current default logger type
  */
-PurpleLogLogger *purple_log_logger_get(void);
+GType purple_log_logger_get_default(void);
 
 /**
- * Returns a list of all the available loggers
+ * Gets a list of all the available loggers
  *
- * @return loggers    All available loggers
+ * @return            All available loggers
  *
  * @since 2.8.0
  */
-GSList *purple_log_logger_get_all(void);
+GArray *purple_log_logger_get_all(void);
 
 /**
  * Gets a #GList containing the IDs and names of the registered loggers.
@@ -1070,19 +1043,19 @@ GList *purple_log_logger_get_options(voi
 /**
  * Initializes the log subsystem
  */
-void purple_log_init(void);
+void purple_log_system_init(void);
 
 /**
  * Returns the log subsystem handle
  *
  * @return            The log subsystem handle
  */
-void *purple_log_get_handle(void);
+void *purple_log_system_get_handle(void);
 
 /**
  * Uninitializes the log subsystem
  */
-void purple_log_uninit(void);
+void purple_log_system_uninit(void);
 
 /*@}*/
 
============================================================
--- pidgin/gtklog.c	1a7b27749c5c448a40a40c92cbf832f0501b9171
+++ pidgin/gtklog.c	379ee451d8409fda74f354bbe91633f34da2f4f3
@@ -131,44 +131,6 @@ static PidginLogViewerEx *syslog_viewer 
 static GHashTable *log_viewers = NULL;
 static PidginLogViewerEx *syslog_viewer = NULL;
 
-// To be used until PurpleLog changes to using a GDateTime
-GType purple_struct_tm_get_type(void);
-static gpointer purple_struct_tm_copy(gpointer);
-
-#define PURPLE_STRUCT_TM_GET_TYPE (purple_struct_tm_get_type())
-
-#if GLIB_CHECK_VERSION(2, 26, 0)
-
-typedef struct tm PurpleStructTM;
-G_DEFINE_BOXED_TYPE (PurpleStructTM, purple_struct_tm, purple_struct_tm_copy, g_free);
-
-#else
-
-GType
-purple_struct_tm_get_type(void)
-{
-	static GType object_type = 0;
-
-	if (G_UNLIKELY (!object_type))
-		object_type = g_boxed_type_register_static(g_intern_static_string("PurpleStructTm"),
-			purple_struct_tm_copy, g_free);
-
-	return object_type;
-}
-
-#endif
-
-static gpointer
-purple_struct_tm_copy(gpointer original)
-{
-	struct tm *copy;
-
-	copy = g_new(struct tm, 1);
-	memcpy(copy, original, sizeof(struct tm));
-
-	return copy;
-}
-
 static void
 pidgin_log_data_free(_pidgin_log_data *data)
 {
@@ -984,7 +946,7 @@ display_log_viewer_nonblocking(log_viewe
 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
 	gtk_paned_add1(GTK_PANED(pane), sw);
 
-	lv->treestore = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, PURPLE_STRUCT_TM_GET_TYPE);
+	lv->treestore = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, PURPLE_TYPE_STRUCT_TM);
 	lv->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(lv->treestore));
 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(lv->treeview), FALSE);
 	pidgin_set_accessible_label(lv->treeview, lv->label);
============================================================
--- libpurple/Makefile.am	c1f7e9c3096c60434341bff05ce1d99f4b42527f
+++ libpurple/Makefile.am	1e30217909679956cd9e2c39368d08d00a98aa19
@@ -50,6 +50,7 @@ purple_coresources = \
 	desktopitem.c \
 	eventloop.c \
 	ft.c \
+	htmllog.c \
 	idle.c \
 	imgstore.c \
 	log.c \
@@ -90,6 +91,7 @@ purple_coresources = \
 	theme.c \
 	theme-loader.c \
 	theme-manager.c \
+	txtlog.c \
 	upnp.c \
 	util.c \
 	value.c \
@@ -118,6 +120,7 @@ purple_coreheaders = \
 	eventloop.h \
 	ft.h \
 	gaim-compat.h \
+	htmllog.h \
 	idle.h \
 	imgstore.h \
 	log.h \
@@ -154,6 +157,7 @@ purple_coreheaders = \
 	theme.h \
 	theme-loader.h \
 	theme-manager.h \
+	txtlog.h \
 	upnp.h \
 	util.h \
 	valgrind.h \
============================================================
--- libpurple/Makefile.mingw	e15167af874d0b95fb51b36f35d9748cbf8320b2
+++ libpurple/Makefile.mingw	dee33a1b0727bdc19f53839d9f495cba8504b0f0
@@ -45,6 +45,7 @@ C_SRC =	\
 			dnssrv.c \
 			eventloop.c \
 			ft.c \
+			htmllog.c \
 			idle.c \
 			imgstore.c \
 			log.c \
@@ -78,6 +79,7 @@ C_SRC =	\
 			theme-loader.c \
 			theme-manager.c \
 			theme.c \
+			txtlog.c \
 			upnp.c \
 			util.c \
 			value.c \
============================================================
--- /dev/null	
+++ libpurple/htmllog.c	a253d66d3d8720b4318defa9bc816e79ea5eafb3
@@ -0,0 +1,348 @@
+/**
+ * @file htmllog.c HTML Logger API
+ * @ingroup core
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include "internal.h"
+#include "htmllog.h"
+#include "log.h"
+#include "util.h"
+
+G_DEFINE_TYPE (PurpleHtmlLog, purple_html_log, PURPLE_TYPE_LOG)
+
+static gssize purple_html_log_create(PurpleLog *, GCancellable *, GError **);
+static gssize purple_html_log_write(PurpleLog *, PurpleMessageFlags, const gchar *, time_t, const gchar *, GCancellable *, GError **);
+static GList *purple_html_log_list(PurpleLog *, PurpleLogChatType, const gchar *, PurpleAccount *, GCancellable *, GError **);
+static gchar *purple_html_log_read(PurpleLog *, PurpleLogReadFlags *, GCancellable *, GError **);
+static gssize purple_html_log_total_size(PurpleLogChatType, const gchar *, PurpleAccount *, GCancellable *, GError **);
+static GList *purple_html_log_list_syslog(PurpleLog *, PurpleAccount *, GCancellable *, GError **);
+static void purple_html_log_finalize(GObject *);
+
+static void
+purple_html_log_class_init(PurpleHtmlLogClass *class)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+	PurpleLogClass *log_class = PURPLE_LOG_CLASS(class);
+
+	log_class->logger_name = _("HTML");
+	log_class->logger_id = "html";
+
+	log_class->create_fn = purple_html_log_create;
+	log_class->write_fn = purple_html_log_write;
+	log_class->list_fn = purple_html_log_list;
+	log_class->read_fn = purple_html_log_read;
+	log_class->size_fn = purple_log_common_sizer;
+	log_class->total_size_fn = purple_html_log_total_size;
+	log_class->list_syslog_fn = purple_html_log_list_syslog;
+	log_class->is_removable_fn = purple_log_common_is_removable;
+	log_class->remove_fn = purple_log_common_remover;
+
+	gobject_class->finalize = purple_html_log_finalize;
+}
+
+static void
+purple_html_log_init(PurpleHtmlLog *html_log)
+{
+}
+
+PurpleLog *
+purple_html_log_new(PurpleLogChatType type, const gchar *name, PurpleAccount *account,
+	PurpleConversation *conv, time_t time, const struct tm *tm)
+{
+	return purple_log_new(PURPLE_TYPE_HTML_LOG, type, name, account, conv, time, tm);
+}
+
+static gssize
+purple_html_log_create(PurpleLog *log, GCancellable *cancellable, GError **error)
+{
+	return purple_log_common_writer(log, ".html", cancellable, error);
+}
+
+static gssize
+purple_html_log_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time, const gchar *message, GCancellable *cancellable, GError **error)
+{
+	PurpleLogCommonLoggerData *data = purple_log_get_logger_data(log);
+	GFile *file;
+	GFileOutputStream *stream;
+	GOutputStream *out_stream;
+	gchar *date, *escaped_from;
+	gchar *image_corrected_msg, *msg_fixed, *line;
+	gssize written, size = 0;
+	gboolean write_header;
+
+	if (data == 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.
+		 */
+		//Should we be doing this?
+		if (!purple_html_log_create(log, cancellable, error))
+			return -1;
+
+		data = purple_log_get_logger_data(log);
+		write_header = TRUE;
+	} else
+		write_header = FALSE;
+
+	/* If we can't write to the file, give up before we hurt ourselves */
+	if (data == NULL || data->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(data->path);
+	stream = g_file_append_to(file, G_FILE_CREATE_NONE, cancellable, error);
+
+	if (stream == NULL) {
+		g_object_unref(file);
+
+		return -1;
+	}
+
+	out_stream = G_OUTPUT_STREAM(stream);
+
+	if (write_header) {
+		PurpleAccount *account = purple_log_get_account(log);
+		PurplePlugin *plugin;
+		const gchar *prpl;
+		const gchar *date_full;
+		gchar *header;
+		time_t log_time = purple_log_get_time(log);
+
+		plugin = purple_find_prpl(purple_account_get_protocol_id(account));
+		prpl = PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(account, NULL);
+		date_full = purple_date_format_full(localtime(&log_time));
+
+		if (purple_log_get_chat_type(log) == PURPLE_LOG_SYSTEM)
+			header = g_strdup_printf("System log for account %s (%s) connected at %s",
+				purple_account_get_username(account), prpl, date_full);
+		else
+			header = g_strdup_printf("Conversation with %s at %s on %s (%s)",
+				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",
+			header, header);
+
+		g_free(header);
+
+		written = g_output_stream_write(out_stream, line,
+			strlen(line), cancellable, error);
+		g_free(line);
+
+		if (written < 0) {
+			g_object_unref(file);
+			g_object_unref(stream);
+
+			return -1;
+		}
+
+		size += written;
+	}
+
+	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 != message)
+		g_free(image_corrected_msg);
+
+	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 (type & 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)
+			line = g_strdup_printf("<font size=\"2\">(%s)</font> %s<br/>\n",
+				date, msg_fixed);
+		else if (type & 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)
+			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)
+				line = g_strdup_printf(_("<font color=\"#16569E\"><font "
+					"size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b>"
+					"</font> %s<br/>\n"), date, escaped_from, msg_fixed);
+			else
+				line = g_strdup_printf(_("<font color=\"#A82F2F\"><font "
+					"size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b>"
+					"</font> %s<br/>\n"), date, escaped_from, msg_fixed);
+		} else if (type & 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",
+					date, escaped_from, msg_fixed);
+			else
+				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) {
+			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",
+					date, escaped_from, msg_fixed);
+			else
+				line = g_strdup_printf("<font color=\"#16569E\"><font "
+					"size=\"2\">(%s)</font> <b>%s:</b></font> %s<br/>\n",
+					date, escaped_from, msg_fixed);
+		} else {
+			line = g_strdup_printf("<font size=\"2\">(%s)</font>"
+				"<b> %s:</b></font> %s<br/>\n",
+				date, escaped_from, msg_fixed);
+		}
+	}
+
+	written = g_output_stream_write(out_stream, line, strlen(line),
+		cancellable, error);
+
+	if (written < 0)
+		size = -1;
+	else
+		size += written;
+
+	g_free(date);
+	g_free(msg_fixed);
+	g_free(escaped_from);
+	g_object_unref(file);
+	g_object_unref(stream);
+
+	return size;
+}
+
+static GList *
+purple_html_log_list(PurpleLog *log, PurpleLogChatType type, const gchar *sn, PurpleAccount *account, GCancellable *cancellable, GError **error)
+{
+	return purple_log_common_lister(type, sn, account, ".html", PURPLE_TYPE_HTML_LOG, cancellable, error);
+}
+
+static GList *
+purple_html_log_list_syslog(PurpleLog *log, PurpleAccount *account, GCancellable *cancellable, GError **error)
+{
+	return purple_log_common_lister(PURPLE_LOG_SYSTEM, ".system", account, ".html", PURPLE_TYPE_HTML_LOG, cancellable, error);
+}
+
+static gchar *
+purple_html_log_read(PurpleLog *log, PurpleLogReadFlags *flags, GCancellable *cancellable, GError **error)
+{
+	PurpleLogCommonLoggerData *data = purple_log_get_logger_data(log);
+	GFile *file;
+	gchar *read, *minus_header;
+
+	if (flags != NULL)
+		*flags = PURPLE_LOG_READ_NO_NEWLINE;
+
+	if (data == NULL || data->path == NULL) {
+		g_set_error_literal(error,
+			G_FILE_ERROR,
+			G_IO_ERROR_NOT_FOUND,
+			_("Unable to find log path"));
+
+		return NULL;
+	}
+
+	file = g_file_new_for_path(data->path);
+
+	if (!g_file_load_contents(file, cancellable, &read, NULL, NULL, error)) {
+		g_object_unref(file);
+
+		return NULL;
+	}
+
+	minus_header = strchr(read, '\n');
+
+	if (minus_header == NULL)
+		minus_header = read;
+	else {
+		minus_header = g_strdup(minus_header + 1);
+		g_free(read);
+	}
+
+	purple_str_strip_char(minus_header, '\r');
+
+	return minus_header;
+}
+
+static gssize
+purple_html_log_total_size(PurpleLogChatType type, const gchar *name, PurpleAccount *account, GCancellable *cancellable, GError **error)
+{
+	return purple_log_common_total_sizer(type, name, account, ".html", cancellable, error);
+}
+
+static void
+purple_html_log_finalize(GObject *object)
+{
+	PurpleLog *log = PURPLE_LOG(object);
+	PurpleLogCommonLoggerData *data = purple_log_get_logger_data(log);
+
+	if (data != NULL) {
+		if (data->file != NULL) {
+			fprintf(data->file, "</body></html>\n");
+			fclose(data->file);
+		}
+
+		g_free(data->path);
+		g_slice_free(PurpleLogCommonLoggerData, data);
+	}
+
+	G_OBJECT_CLASS(purple_html_log_parent_class)->finalize(object);
+}
+
+void
+purple_html_log_system_init(void)
+{
+	purple_prefs_add_string("/purple/logging/format", "html");
+}
+
+void *
+purple_html_log_system_get_handle(void)
+{
+	static gint handle;
+
+	return &handle;
+}
+
+void
+purple_html_log_system_uninit(void)
+{
+	purple_signals_unregister_by_instance(purple_html_log_system_get_handle());
+}
============================================================
--- /dev/null	
+++ libpurple/htmllog.h	49ed99ca59a6f1f6ca3b030589a1a79fe25a2916
@@ -0,0 +1,71 @@
+/**
+ * @file htmllog.h Logging API
+ * @ingroup core
+ * @see @ref log-signals
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef _PURPLE_HTML_LOG_H_
+#define _PURPLE_HTML_LOG_H_
+
+#include <gio/gio.h>
+
+#include "log.h"
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_HTML_LOG         (purple_html_log_get_type())
+#define PURPLE_HTML_LOG(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), PURPLE_TYPE_HTML_LOG, PurpleHtmlLog))
+#define PURPLE_HTML_LOG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), PURPLE_TYPE_HTML_LOG, PurpleHtmlLogClass))
+#define PURPLE_IS_HTML_LOG(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), PURPLE_TYPE_HTML_LOG))
+#define PURPLE_IS_HTML_LOG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), PURPLE_TYPE_HTML_LOG))
+#define PURPLE_HTML_LOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), PURPLE_TYPE_HTML_LOG, PurpleHtmlLogClass))
+
+typedef struct _PurpleHtmlLog        PurpleHtmlLog;
+typedef struct _PurpleHtmlLogClass   PurpleHtmlLogClass;
+
+struct _PurpleHtmlLog {
+	PurpleLog parent_instance;
+};
+
+struct _PurpleHtmlLogClass {
+	PurpleLogClass parent_class;
+
+	void (*_purple_reserved1)(void);
+	void (*_purple_reserved2)(void);
+	void (*_purple_reserved3)(void);
+	void (*_purple_reserved4)(void);
+};
+
+GType purple_html_log_get_type(void);
+
+PurpleLog *purple_html_log_new(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account,
+	PurpleConversation *conv, time_t time, const struct tm *tm);
+
+void purple_html_log_system_init(void);
+void *purple_html_log_system_get_handle(void);
+void purple_html_log_system_uninit(void);
+
+G_END_DECLS
+
+#endif /* _PURPLE_HTML_LOG_H_ */
============================================================
--- /dev/null	
+++ libpurple/oldlog.c	d6149b5e029a1fc0b246ba3b1a30ea9ee80ba73f
@@ -0,0 +1,480 @@
+/**
+ * @file oldlog.c Old Logger API
+ * @ingroup core
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+
+static GList *old_logger_list(PurpleLogType, const gchar *, PurpleAccount *);
+static gint old_logger_total_size(PurpleLogType, const gchar *, PurpleAccount *);
+static gchar *old_logger_read(PurpleLog *, PurpleLogReadFlags *);
+static gint old_logger_size(PurpleLog *);
+static void old_logger_get_log_sets(PurpleLogSetCallback, GHashTable *);
+static void old_logger_finalize(PurpleLog *);
+
+
+/****************
+ * OLD LOGGER ***
+ ****************/
+
+/* The old logger doesn't write logs, only reads them.  This is to include
+ * old logs in the log viewer transparently.
+ */
+
+typedef struct {
+	PurpleStringref *pathref;
+	gint offset;
+	gint length;
+} old_logger_data;
+
+static GList *
+old_logger_list(PurpleLogType type, const gchar *sn, PurpleAccount *account)
+{
+	old_logger_data *data;
+	PurpleStringref *pathref;
+	PurpleLog *log;
+	GList *list;
+	FILE *index, *file;
+	time_t log_last_modified, lasttime;
+	struct stat st;
+	struct tm tm;
+	gchar *logfile, *pathstr, *index_tmp, buf[BUF_LONG], month[4], convostart[32], *temp;
+	gint index_fd, logfound, lastoff, newlen, length, offset;
+	gulong idx_time;
+
+	logfile = g_strdup_printf("%s.log", purple_normalize(account, sn));
+	pathstr = g_build_filename(purple_user_dir(), "logs", logfile, NULL);
+	pathref = purple_stringref_new(pathstr);
+	g_free(logfile);
+
+	log = NULL;
+	list = NULL;
+	data = NULL;
+
+	logfound = 0;
+	lastoff = 0;
+	lasttime = 0;
+
+	if (g_stat(purple_stringref_value(pathref), &st)) {
+		purple_stringref_unref(pathref);
+		g_free(pathstr);
+
+		return NULL;
+	} else
+		log_last_modified = st.st_mtime;
+
+	/* Change the .log extension to .idx */
+	strcpy(pathstr + strlen(pathstr) - 3, "idx");
+
+	if (g_stat(pathstr, &st) == 0) {
+		if (st.st_mtime < log_last_modified) {
+			purple_debug_warning("log", "Index \"%s\" exists, but is older than the log.\n", pathstr);
+		} else {
+			/* The index file exists and is at least as new as the log, so open it. */
+			if (!(index = g_fopen(pathstr, "rb")))
+			{
+				purple_debug_error("log", "Failed to open index file \"%s\" for reading: %s\n",
+					pathstr, g_strerror(errno));
+
+				/* Fall through so that we'll parse the log file. */
+			} else {
+				purple_debug_info("log", "Using index: %s\n", pathstr);
+				g_free(pathstr);
+
+				while (fgets(buf, BUF_LONG, index)) {
+					if (sscanf(buf, "%d\t%d\t%lu", &lastoff, &newlen, &idx_time) == 3) {
+						log = purple_log_new(PURPLE_TYPE_OLD_LOG, PURPLE_LOG_IM, sn, account, NULL, -1, NULL);
+						log->time = (time_t) idx_time;
+
+						G_LOCK(old_logger);
+						log->logger = old_logger;
+						G_UNLOCK(old_logger);
+
+						/* IMPORTANT: Always set all members of old_logger_data */
+						data = g_slice_new(old_logger_data);
+
+						data->pathref = purple_stringref_ref(pathref);
+						data->offset = lastoff;
+						data->length = newlen;
+
+						log->logger_data = data;
+						list = g_list_prepend(list, log);
+					}
+				}
+
+				fclose(index);
+				purple_stringref_unref(pathref);
+
+				return list;
+			}
+		}
+	}
+
+	if (!(file = g_fopen(purple_stringref_value(pathref), "rb"))) {
+		purple_debug_error("log", "Failed to open log file \"%s\" for reading: %s\n",
+			purple_stringref_value(pathref), g_strerror(errno));
+		purple_stringref_unref(pathref);
+		g_free(pathstr);
+
+		return NULL;
+	}
+
+	index_tmp = g_strdup_printf("%s.XXXXXX", pathstr);
+
+	if ((index_fd = g_mkstemp(index_tmp)) == -1) {
+		purple_debug_error("log", "Failed to open index temp file: %s\n",
+			g_strerror(errno));
+
+		g_free(pathstr);
+		g_free(index_tmp);
+		index = NULL;
+	} else {
+		index = fdopen(index_fd, "wb");
+
+		if (index == NULL) {
+			purple_debug_error("log", "Failed to fdopen() index temp file: %s\n",
+				g_strerror(errno));
+			close(index_fd);
+
+			if (index_tmp != NULL) {
+				g_unlink(index_tmp);
+				g_free(index_tmp);
+			}
+
+			g_free(pathstr);
+		}
+	}
+
+	while (fgets(buf, BUF_LONG, file)) {
+		if (strstr(buf, "---- New C") != NULL) {
+			temp = strchr(buf, '@');
+
+			if (temp == NULL || strlen(temp) < 2)
+				continue;
+
+			temp++;
+			length = strcspn(temp, "-");
+			if (length > 31) length = 31;
+
+			offset = ftell(file);
+
+			if (logfound) {
+				newlen = offset - lastoff - length;
+				if(strstr(buf, "----</H3><BR>")) {
+					newlen -=
+						sizeof("<HR><BR><H3 Align=Center> ---- New Conversation @ ") +
+						sizeof("----</H3><BR>") - 2;
+				} else {
+					newlen -= sizeof("---- New Conversation @ ") + sizeof("----") - 2;
+				}
+
+				if(strchr(buf, '\r'))
+					newlen--;
+
+				if (newlen != 0) {
+					log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, -1, NULL);
+					log->time = lasttime;
+
+					G_LOCK(old_logger);
+					log->logger = old_logger;
+					G_UNLOCK(old_logger);
+
+					/* IMPORTANT: Always set all members of old_logger_data */
+					data = g_slice_new(old_logger_data);
+
+					data->pathref = purple_stringref_ref(pathref);
+					data->offset = lastoff;
+					data->length = newlen;
+
+					log->logger_data = data;
+					list = g_list_prepend(list, log);
+
+					/* XXX: There is apparently Is there a proper way to print a time_t? */
+					if (index != NULL)
+						fprintf(index, "%d\t%d\t%lu\n", data->offset, data->length,
+							(unsigned long)log->time);
+				}
+			}
+
+			logfound = 1;
+			lastoff = offset;
+
+			g_snprintf(convostart, length, "%s", temp);
+			memset(&tm, 0, sizeof(tm));
+			sscanf(convostart, "%*s %s %d %d:%d:%d %d", month, &tm.tm_mday,
+				&tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tm.tm_year);
+
+			/* Ugly hack, in case current locale is not English */
+			if (purple_strequal(month, "Jan")) {
+				tm.tm_mon= 0;
+			} else if (purple_strequal(month, "Feb")) {
+				tm.tm_mon = 1;
+			} else if (purple_strequal(month, "Mar")) {
+				tm.tm_mon = 2;
+			} else if (purple_strequal(month, "Apr")) {
+				tm.tm_mon = 3;
+			} else if (purple_strequal(month, "May")) {
+				tm.tm_mon = 4;
+			} else if (purple_strequal(month, "Jun")) {
+				tm.tm_mon = 5;
+			} else if (purple_strequal(month, "Jul")) {
+				tm.tm_mon = 6;
+			} else if (purple_strequal(month, "Aug")) {
+				tm.tm_mon = 7;
+			} else if (purple_strequal(month, "Sep")) {
+				tm.tm_mon = 8;
+			} else if (purple_strequal(month, "Oct")) {
+				tm.tm_mon = 9;
+			} else if (purple_strequal(month, "Nov")) {
+				tm.tm_mon = 10;
+			} else if (purple_strequal(month, "Dec")) {
+				tm.tm_mon = 11;
+			}
+			tm.tm_year -= 1900;
+			lasttime = mktime(&tm);
+		}
+	}
+
+	if (logfound) {
+		if ((newlen = ftell(file) - lastoff) != 0) {
+			log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, -1, NULL);
+			log->time = lasttime;
+
+			G_LOCK(old_logger);
+			log->logger = old_logger;
+			G_UNLOCK(old_logger);
+
+			/* IMPORTANT: Always set all members of old_logger_data */
+			data = g_slice_new(old_logger_data);
+
+			data->pathref = purple_stringref_ref(pathref);
+			data->offset = lastoff;
+			data->length = newlen;
+
+			log->logger_data = data;
+			list = g_list_prepend(list, log);
+
+			/* XXX: Is there a proper way to print a time_t? */
+			if (index != NULL)
+				fprintf(index, "%d\t%d\t%d\n", data->offset, data->length, (gint) log->time);
+		}
+	}
+
+	purple_stringref_unref(pathref);
+	fclose(file);
+
+	if (index != NULL) {
+		fclose(index);
+
+		if (index_tmp == NULL) {
+			g_free(pathstr);
+			g_return_val_if_reached(list);
+		}
+
+		if (g_rename(index_tmp, pathstr)) {
+			purple_debug_warning("log", "Failed to rename index temp file \"%s\" to \"%s\": %s\n",
+				index_tmp, pathstr, g_strerror(errno));
+			g_unlink(index_tmp);
+		} else
+			purple_debug_info("log", "Built index: %s\n", pathstr);
+
+		g_free(index_tmp);
+		g_free(pathstr);
+	}
+
+	return list;
+}
+
+static gint
+old_logger_total_size(PurpleLogType type, const gchar *name, PurpleAccount *account)
+{
+	gchar *logfile, *pathstr;
+	gint size;
+	struct stat st;
+
+	logfile = g_strdup_printf("%s.log", purple_normalize(account, name));
+	pathstr = g_build_filename(purple_user_dir(), "logs", logfile, NULL);
+
+	if (g_stat(pathstr, &st))
+		size = 0;
+	else
+		size = st.st_size;
+
+	g_free(logfile);
+	g_free(pathstr);
+
+	return size;
+}
+
+static gchar *
+old_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
+{
+	size_t result;
+	old_logger_data *data;
+	const gchar *path;
+	FILE *file;
+	gchar *read;
+
+	data = log->logger_data;
+	path = purple_stringref_value(data->pathref);
+	file = g_fopen(path, "rb");
+	read = g_malloc(data->length + 1);
+
+	fseek(file, data->offset, SEEK_SET);
+	result = fread(read, data->length, 1, file);
+
+	if (result != 1)
+		purple_debug_error("log", "Unable to read from log file: %s\n", path);
+
+	fclose(file);
+	read[data->length] = '\0';
+
+	if (flags != NULL)
+		*flags = 0;
+
+	if (strstr(read, "<BR>")) {
+		if (flags != NULL)
+			*flags |= PURPLE_LOG_READ_NO_NEWLINE;
+
+		return read;
+	}
+
+	purple_str_strip_char(read, '\r');
+
+	return process_txt_log(read, NULL);
+}
+
+static gint
+old_logger_size(PurpleLog *log)
+{
+	old_logger_data *data;
+
+	data = log->logger_data;
+
+	return data ? data->length : 0;
+}
+
+static void
+old_logger_get_log_sets(PurpleLogSetCallback cb, GHashTable *sets)
+{
+	PurpleBlistNode *gnode, *cnode, *bnode;
+	PurpleBuddy *buddy;
+	PurpleLogSet *set;
+	GDir *log_dir;
+	gchar *log_path, *name, *tmp, *ext;
+	size_t len;
+	gboolean found;
+
+	log_path = g_build_filename(purple_user_dir(), "logs", NULL);
+	log_dir = g_dir_open(log_path, 0, NULL);
+	g_free(log_path);
+
+	if (log_dir == NULL)
+		return;
+
+	/* Don't worry about the cast, name will be filled with a dynamically allocated data shortly. */
+	while ((name = (gchar *) g_dir_read_name(log_dir)) != NULL) {
+		found = FALSE;
+
+		/* Unescape the filename. */
+		name = g_strdup(purple_unescape_filename(name));
+
+		/* Get the (possibly new) length of name. */
+		len = strlen(name);
+
+		if (len < 5) {
+			g_free(name);
+			continue;
+		}
+
+		/* Make sure we're dealing with a log file. */
+		ext = &name[len - 4];
+		if (!purple_strequal(ext, ".log")) {
+			g_free(name);
+			continue;
+		}
+
+		/* IMPORTANT: Always set all members of PurpleLogSet */
+		set = g_slice_new(PurpleLogSet);
+
+		/* Chat for .chat at the end of the name to determine the type. */
+		*ext = '\0';
+		set->type = PURPLE_LOG_IM;
+
+		if (len > 9) {
+			tmp = &name[len - 9];
+			if (purple_strequal(tmp, ".chat")) {
+				set->type = PURPLE_LOG_CHAT;
+				*tmp = '\0';
+			}
+		}
+
+		set->name = set->normalized_name = name;
+
+		/* Search the buddy list to find the account and to determine if this is a buddy. */
+		for (gnode = purple_blist_get_root();
+		     !found && gnode != NULL;
+			 gnode = purple_blist_node_get_sibling_next(gnode)) {
+			if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+				continue;
+
+			for (cnode = purple_blist_node_get_first_child(gnode);
+			     !found && cnode != NULL;
+				 cnode = purple_blist_node_get_sibling_next(cnode)) {
+				if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+					continue;
+
+				for (bnode = purple_blist_node_get_first_child(cnode);
+				     !found && bnode != NULL;
+					 bnode = purple_blist_node_get_sibling_next(bnode)) {
+					buddy = (PurpleBuddy *) bnode;
+
+					if (purple_strequal(purple_buddy_get_name(buddy), name)) {
+						set->account = purple_buddy_get_account(buddy);
+						set->buddy = TRUE;
+						found = TRUE;
+					}
+				}
+			}
+		}
+
+		if (!found) {
+			set->account = NULL;
+			set->buddy = FALSE;
+		}
+
+		cb(sets, set);
+	}
+
+	g_dir_close(log_dir);
+}
+
+static void
+old_logger_finalize(PurpleLog *log)
+{
+	old_logger_data *data = log->logger_data;
+
+	purple_stringref_unref(data->pathref);
+	g_slice_free(old_logger_data, data);
+}
+
============================================================
--- /dev/null	
+++ libpurple/oldlog.h	83c5a5b2951acd1273c990d0bfd090fe94af2442
@@ -0,0 +1,71 @@
+/**
+ * @file oldlog.h Logging API
+ * @ingroup core
+ * @see @ref log-signals
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef _PURPLE_OLD_LOG_H_
+#define _PURPLE_OLD_LOG_H_
+
+#include <gio/gio.h>
+
+#include "log.h"
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_OLD_LOG         (purple_old_log_get_type())
+#define PURPLE_OLD_LOG(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), PURPLE_TYPE_OLD_LOG, PurpleOldLog))
+#define PURPLE_OLD_LOG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), PURPLE_TYPE_OLD_LOG, PurpleOldLogClass))
+#define PURPLE_IS_OLD_LOG(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), PURPLE_TYPE_OLD_LOG))
+#define PURPLE_IS_OLD_LOG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), PURPLE_TYPE_OLD_LOG))
+#define PURPLE_OLD_LOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), PURPLE_TYPE_OLD_LOG, PurpleOldLogClass))
+
+typedef struct _PurpleOldLog        PurpleOldLog;
+typedef struct _PurpleOldLogClass   PurpleOldLogClass;
+
+struct _PurpleOldLog {
+	PurpleLog parent_instance;
+};
+
+struct _PurpleOldLogClass {
+	PurpleLogClass parent_class;
+
+	void (*_purple_reserved1)(void);
+	void (*_purple_reserved2)(void);
+	void (*_purple_reserved3)(void);
+	void (*_purple_reserved4)(void);
+};
+
+GType purple_old_log_get_type(void);
+
+PurpleLog *purple_old_log_new(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account,
+	PurpleConversation *conv, time_t time, const struct tm *tm);
+
+void purple_old_log_system_init(void);
+void *purple_old_log_system_get_handle(void);
+void purple_old_log_system_uninit(void);
+
+G_END_DECLS
+
+#endif /* _PURPLE_OLD_LOG_H_ */
============================================================
--- /dev/null	
+++ libpurple/txtlog.c	10c77fb598b266e130a52a4fee474f0515ffbf82
@@ -0,0 +1,516 @@
+
+
+static gchar *process_txt_log(gchar *, gchar *);
+
+
+static gsize txt_logger_write(PurpleLog *, PurpleMessageFlags, const gchar *, time_t,
+	const gchar *);
+static void txt_logger_write_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+static void txt_logger_write_async(PurpleLog *, PurpleMessageFlags, const gchar *, time_t,
+	const gchar *, gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static void txt_logger_finalize(PurpleLog *);
+static GList *txt_logger_list(PurpleLogType, const gchar *, PurpleAccount *);
+static void txt_logger_list_async(PurpleLogType, const gchar *, PurpleAccount *,
+	gint, GCancellable *, GAsyncReadyCallback, gpointer);
+static GList *txt_logger_list_syslog(PurpleAccount *);
+static void txt_logger_list_syslog_async(PurpleAccount *, gint, GCancellable *,
+	GAsyncReadyCallback, gpointer);
+static gchar *txt_logger_read(PurpleLog *, PurpleLogReadFlags *);
+static void txt_logger_read_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+static void txt_logger_read_async(PurpleLog *, PurpleLogReadFlags *, gint, GCancellable *,
+	GAsyncReadyCallback, gpointer);
+static gint txt_logger_total_size(PurpleLogType, const gchar *, PurpleAccount *);
+static void txt_logger_total_size_async(PurpleLogType, const gchar *, PurpleAccount *,
+	gint, GCancellable *, GAsyncReadyCallback, gpointer);
+
+
+
+
+
+
+
+
+
+
+
+
+static gchar *
+process_txt_log(gchar *txt, gchar *to_free)
+{
+	gchar *tmp;
+
+	/* The to_free argument allows us to save a
+	 * g_strdup() in some cases. */
+
+	if (to_free == NULL)
+		to_free = txt;
+
+	/* g_markup_escape_text requires valid UTF-8 */
+	if (!g_utf8_validate(txt, -1, NULL))
+	{
+		tmp = purple_utf8_salvage(txt);
+		g_free(to_free);
+		to_free = txt = tmp;
+	}
+
+	tmp = g_markup_escape_text(txt, -1);
+	g_free(to_free);
+	txt = purple_markup_linkify(tmp);
+	g_free(tmp);
+
+	return txt;
+}
+
+
+
+
+
+
+/****************************
+ ** PLAIN TEXT LOGGER *******
+ ****************************/
+
+static gsize
+txt_logger_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
+	const gchar *message)
+{
+	PurplePlugin *plugin;
+	PurpleLogCommonLoggerData *data;
+	const gchar *prpl;
+	gchar *stripped, *date;
+	gsize written;
+
+	plugin = purple_find_prpl(purple_account_get_protocol_id(log->account));
+	data = log->logger_data;
+	stripped = NULL;
+	written = 0;
+
+	if (data == 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.
+		 */
+		prpl = PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL);
+		purple_log_common_writer(log, ".txt");
+
+		data = log->logger_data;
+
+		/* if we can't write to the file, give up before we hurt ourselves */
+		if(!data->file)
+			return 0;
+
+		if (log->type == PURPLE_LOG_SYSTEM)
+			written += fprintf(data->file, "System log for account %s (%s) connected at %s\n",
+				purple_account_get_username(log->account), prpl,
+				purple_date_format_full(localtime(&log->time)));
+		else
+			written += fprintf(data->file, "Conversation with %s at %s on %s (%s)\n",
+				log->name, purple_date_format_full(localtime(&log->time)),
+				purple_account_get_username(log->account), prpl);
+	}
+
+	/* if we can't write to the file, give up before we hurt ourselves */
+	if(!data->file)
+		return 0;
+
+	stripped = purple_markup_strip_html(message);
+	date = log_get_timestamp(log, time);
+
+	if(log->type == PURPLE_LOG_SYSTEM){
+		written += fprintf(data->file, "---- %s @ %s ----\n", stripped, date);
+	} else {
+		if (type & PURPLE_MESSAGE_SEND ||
+			type & PURPLE_MESSAGE_RECV) {
+			if (type & PURPLE_MESSAGE_AUTO_RESP) {
+				written += fprintf(data->file, _("(%s) %s <AUTO-REPLY>: %s\n"), date,
+					from, stripped);
+			} else {
+				if (purple_message_meify(stripped, -1))
+					written += fprintf(data->file, "(%s) ***%s %s\n", date, from, stripped);
+				else
+					written += fprintf(data->file, "(%s) %s: %s\n", date, from, stripped);
+			}
+		} else if (type & PURPLE_MESSAGE_SYSTEM ||
+			type & PURPLE_MESSAGE_ERROR ||
+			type & PURPLE_MESSAGE_RAW)
+			written += fprintf(data->file, "(%s) %s\n", date, stripped);
+		else if (type & PURPLE_MESSAGE_NO_LOG) {
+			/* This shouldn't happen */
+			g_free(stripped);
+
+			return written;
+		} else if (type & PURPLE_MESSAGE_WHISPER)
+			written += fprintf(data->file, "(%s) *%s* %s", date, from, stripped);
+		else
+			written += fprintf(data->file, "(%s) %s%s %s\n", date, from ? from : "",
+				from ? ":" : "", stripped);
+	}
+
+	g_free(date);
+	g_free(stripped);
+	fflush(data->file);
+
+	return written;
+}
+
+static void
+txt_logger_write_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
+{
+	_purple_log_logger_write_callback_data *callback_data =
+		g_simple_async_result_get_op_res_gpointer(simple);
+	PurpleLog *log = callback_data->log;
+	PurpleLogCommonLoggerData *data = log->logger_data;
+	PurpleLogType type = callback_data->type;
+	GError *err = NULL;
+	GFile *file;
+	GFileOutputStream *stream;
+	GOutputStream *out_stream;
+	const gchar *from = callback_data->from, *message = callback_data->message;
+	gchar *stripped = NULL, *date, *line;
+	gssize written, size = 0;
+	gboolean write_header;
+
+	if (type & PURPLE_MESSAGE_NO_LOG) {
+		g_simple_async_result_set_error(simple,
+			G_IO_ERROR,
+			G_IO_ERROR_FAILED,
+			_("Trying to log when flagged not to"));
+
+		return;
+	}
+
+	if (data == 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.
+		 */
+		purple_log_common_writer(log, ".txt");
+		data = log->logger_data;
+		write_header = TRUE;
+	} else
+		write_header = FALSE;
+
+	/* If we can't write to the file, give up before we hurt ourselves */
+	if (data == NULL || data->path == NULL) {
+		g_simple_async_result_set_error(simple,
+			G_IO_ERROR,
+			G_IO_ERROR_FAILED,
+			_("Unable to find log path"));
+
+		return;
+	}
+
+	file = g_file_new_for_path(data->path);
+	stream = g_file_append_to(file, G_FILE_CREATE_NONE, cancellable, &err);
+
+	if (stream == NULL) {
+		g_simple_async_result_set_from_error(simple, err);
+
+		g_object_unref(file);
+		g_clear_error(&err);
+
+		return;
+	}
+
+	g_clear_error(&err);
+	out_stream = G_OUTPUT_STREAM(stream);
+
+	if (write_header) {
+		PurplePlugin *plugin;
+		const gchar *prpl;
+
+		plugin = purple_find_prpl(purple_account_get_protocol_id(log->account));
+		prpl = PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL);
+
+		if (log->type == PURPLE_LOG_SYSTEM)
+			line = g_strdup_printf("System log for account %s (%s) connected at %s\n",
+				purple_account_get_username(log->account), prpl,
+				purple_date_format_full(localtime(&log->time)));
+		else
+			line = g_strdup_printf("Conversation with %s at %s on %s (%s)\n",
+				log->name, purple_date_format_full(localtime(&log->time)),
+				purple_account_get_username(log->account), prpl);
+
+		written = g_output_stream_write(out_stream, line,
+			strlen(line), cancellable, &err);
+		g_free(line);
+
+		if (written < 0) {
+			g_simple_async_result_set_from_error(simple, err);
+
+			g_object_unref(file);
+			g_object_unref(stream);
+			g_clear_error(&err);
+
+			return;
+		}
+
+		g_clear_error(&err);
+		size += written;
+	}
+
+	stripped = purple_markup_strip_html(message);
+	date = log_get_timestamp(log, callback_data->time);
+
+	if (log->type == PURPLE_LOG_SYSTEM)
+		line = g_strdup_printf("---- %s @ %s ----\n", stripped, date);
+	else {
+		if (type & PURPLE_MESSAGE_SEND ||
+			type & PURPLE_MESSAGE_RECV)
+		{
+			if (type & PURPLE_MESSAGE_AUTO_RESP)
+				line = g_strdup_printf(_("(%s) %s <AUTO-REPLY>: %s\n"), date, from, 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 (type & PURPLE_MESSAGE_SYSTEM ||
+			type & PURPLE_MESSAGE_ERROR ||
+			type & PURPLE_MESSAGE_RAW)
+			line = g_strdup_printf("(%s) %s\n", date, stripped);
+		else if (type & PURPLE_MESSAGE_WHISPER)
+			line = g_strdup_printf("(%s) *%s* %s", date, from, stripped);
+		else
+			line = g_strdup_printf("(%s) %s%s %s\n", date, from ? from : "",
+				from ? ":" : "", stripped);
+	}
+
+	written = g_output_stream_write(out_stream, line, strlen(line),
+		cancellable, &err);
+
+	if (written < 0)
+		g_simple_async_result_set_from_error(simple, err);
+	else
+		g_simple_async_result_set_op_res_gssize(simple, size + written);
+
+	g_clear_error(&err);
+	g_free(line);
+	g_free(date);
+	g_free(stripped);
+	g_object_unref(file);
+	g_object_unref(stream);
+}
+
+static void
+txt_logger_write_async(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
+	const gchar *message, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb,
+	gpointer userdata)
+{
+	_purple_log_logger_write_callback_data *callback_data;
+	GSimpleAsyncResult *simple;
+
+	callback_data = g_new0(_purple_log_logger_write_callback_data, 1);
+	callback_data->log = log;
+	callback_data->type = type;
+	callback_data->from = g_strdup(from);
+	callback_data->time = time;
+	callback_data->message = g_strdup(message);
+
+	simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_write_async);
+
+	g_simple_async_result_set_op_res_gpointer(simple, callback_data,
+		purple_log_logger_write_callback_data_free);
+	g_simple_async_result_run_in_thread(simple, txt_logger_write_thread,
+		io_priority, cancellable);
+
+	g_object_unref(simple);
+}
+
+static void
+txt_logger_finalize(PurpleLog *log)
+{
+	PurpleLogCommonLoggerData *data = log->logger_data;
+
+	if (data != NULL) {
+		if (data->file != NULL)
+			fclose(data->file);
+
+		g_free(data->path);
+		g_slice_free(PurpleLogCommonLoggerData, data);
+	}
+}
+
+static GList *
+txt_logger_list(PurpleLogType type, const gchar *sn, PurpleAccount *account)
+{
+	GList *list;
+
+	G_LOCK(txt_logger);
+	list = purple_log_common_lister(type, sn, account, ".txt", txt_logger);
+	G_UNLOCK(txt_logger);
+
+	return list;
+}
+
+static void
+txt_logger_list_async(PurpleLogType type, const gchar *sn, PurpleAccount *account,
+	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	G_LOCK(txt_logger);
+	purple_log_common_lister_async(type, sn, account, ".txt", txt_logger,
+		io_priority, cancellable, cb, userdata);
+	G_UNLOCK(txt_logger);
+}
+
+static GList *
+txt_logger_list_syslog(PurpleAccount *account)
+{
+	GList *list;
+
+	G_LOCK(txt_logger);
+	list = purple_log_common_lister(PURPLE_LOG_SYSTEM, ".system", account, ".txt", txt_logger);
+	G_UNLOCK(txt_logger);
+
+	return list;
+}
+
+static void
+txt_logger_list_syslog_async(PurpleAccount *account, gint io_priority, GCancellable *cancellable,
+	GAsyncReadyCallback cb, gpointer userdata)
+{
+	G_LOCK(txt_logger);
+	purple_log_common_lister_async(PURPLE_LOG_SYSTEM, ".system", account, ".txt", txt_logger,
+		io_priority, cancellable, cb, userdata);
+	G_UNLOCK(txt_logger);
+}
+
+static gchar *
+txt_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
+{
+	PurpleLogCommonLoggerData *data;
+	gchar *read, *minus_header;
+
+	data = log->logger_data;
+
+	if (flags != NULL)
+		*flags = 0;
+
+	if (!data || !data->path)
+		return g_strdup_printf("<font color=\"red\"><b>%s</b></font>",
+			_("Unable to find log path"));
+
+	if (g_file_get_contents(data->path, &read, NULL, NULL)) {
+		minus_header = strchr(read, '\n');
+
+		if (minus_header)
+			return process_txt_log(minus_header + 1, read);
+		else
+			return process_txt_log(read, NULL);
+	}
+
+	return g_strdup_printf("<font color=\"red\"><b>%s: %s</b></font>",
+		_("Could not read file"), data->path);
+}
+
+static void
+txt_logger_read_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
+{
+	_purple_log_logger_read_callback_data *callback_data =
+		g_simple_async_result_get_op_res_gpointer(simple);
+	PurpleLog *log = callback_data->log;
+	PurpleLogCommonLoggerData *data = log->logger_data;
+	PurpleLogReadFlags *flags = callback_data->flags;
+	GError *err = NULL;
+	GFile *file;
+	gchar *read, *minus_header;
+
+	if (flags != NULL)
+		*flags = 0;
+
+	if (data == NULL || data->path == NULL) {
+		g_simple_async_result_set_error(simple,
+			G_FILE_ERROR,
+			G_IO_ERROR_NOT_FOUND,
+			"<font color=\"red\"><b>%s</b></font>",
+			_("Unable to find log path"));
+
+		return;
+	}
+
+	file = g_file_new_for_path(data->path);
+
+	if (!g_file_load_contents(file, cancellable, &read, NULL, NULL, &err)) {
+		g_simple_async_result_set_error(simple,
+			err->domain,
+			err->code,
+			"<font color=\"red\"><b>%s</b></font>",
+			err->message);
+
+		g_clear_error(&err);
+
+		return;
+	}
+
+	g_clear_error(&err);
+
+	minus_header = strchr(read, '\n');
+
+	if (minus_header != NULL)
+		read = process_txt_log(minus_header + 1, read);
+	else
+		read = process_txt_log(read, NULL);
+
+	purple_str_strip_char(read, '\r');
+
+	g_simple_async_result_set_op_res_gpointer(simple, read, g_free);
+}
+
+static void
+txt_logger_read_async(PurpleLog *log, PurpleLogReadFlags *flags, gint io_priority,
+	GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	_purple_log_logger_read_callback_data *callback_data;
+	GSimpleAsyncResult *simple;
+
+	callback_data = g_new0(_purple_log_logger_read_callback_data, 1);
+	callback_data->flags = flags;
+	callback_data->log = log;
+
+	simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_read_async);
+
+	g_simple_async_result_set_op_res_gpointer(simple, callback_data, g_free);
+	g_simple_async_result_run_in_thread(simple, txt_logger_read_thread,
+		io_priority, cancellable);
+
+	g_object_unref(simple);
+}
+
+static gint
+txt_logger_total_size(PurpleLogType type, const gchar *name, PurpleAccount *account)
+{
+	return purple_log_common_total_sizer(type, name, account, ".txt");
+}
+
+static void
+txt_logger_total_size_async(PurpleLogType type, const gchar *name, PurpleAccount *account,
+	gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
+{
+	purple_log_common_total_sizer_async(type, name, account, ".txt",
+		io_priority, cancellable, cb, userdata);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
============================================================
--- /dev/null	
+++ libpurple/txtlog.h	01bea99cc3956da3d332ca41cc98bb3e8e90f703
@@ -0,0 +1,71 @@
+/**
+ * @file txtlog.h Logging API
+ * @ingroup core
+ * @see @ref log-signals
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef _PURPLE_TXT_LOG_H_
+#define _PURPLE_TXT_LOG_H_
+
+#include <gio/gio.h>
+
+#include "log.h"
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_TXT_LOG         (purple_txt_log_get_type())
+#define PURPLE_TXT_LOG(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), PURPLE_TYPE_TXT_LOG, PurpleTxtLog))
+#define PURPLE_TXT_LOG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), PURPLE_TYPE_TXT_LOG, PurpleTxtLogClass))
+#define PURPLE_IS_TXT_LOG(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), PURPLE_TYPE_TXT_LOG))
+#define PURPLE_IS_TXT_LOG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), PURPLE_TYPE_TXT_LOG))
+#define PURPLE_TXT_LOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), PURPLE_TYPE_TXT_LOG, PurpleTxtLogClass))
+
+typedef struct _PurpleTxtLog        PurpleTxtLog;
+typedef struct _PurpleTxtLogClass   PurpleTxtLogClass;
+
+struct _PurpleTxtLog {
+	PurpleLog parent_instance;
+};
+
+struct _PurpleTxtLogClass {
+	PurpleLogClass parent_class;
+
+	void (*_purple_reserved1)(void);
+	void (*_purple_reserved2)(void);
+	void (*_purple_reserved3)(void);
+	void (*_purple_reserved4)(void);
+};
+
+GType purple_txt_log_get_type(void);
+
+PurpleLog *purple_txt_log_new(PurpleLogChatType chat_type, const gchar *name, PurpleAccount *account,
+	PurpleConversation *conv, time_t time, const struct tm *tm);
+
+void purple_txt_log_system_init(void);
+void *purple_txt_log_system_get_handle(void);
+void purple_txt_log_system_uninit(void);
+
+G_END_DECLS
+
+#endif /* _PURPLE_TXT_LOG_H_ */
============================================================
--- /dev/null	
+++ libpurple/xmllog.c	c44388ba6b4d07929fe5e59ff5a574fa4c9d6d2d
@@ -0,0 +1,111 @@
+#if 0 /* Maybe some other time. */
+/****************
+ ** XML LOGGER **
+ ****************/
+
+static const gchar *
+str_from_msg_type (PurpleMessageFlags type)
+{
+	return "";
+}
+
+static void
+xml_logger_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
+	const gchar *message)
+{
+	gchar *xhtml = NULL;
+
+	if (!log->logger_data) {
+		/* 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.
+		 */
+		struct tm *tm;
+		const gchar *tz;
+		const gchar *date;
+		gchar *dir = purple_log_get_log_dir(log->type, log->name, log->account);
+		gchar *name;
+		gchar *filename;
+
+		if (dir == NULL)
+			return;
+
+		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);
+
+		name = g_strdup_printf("%s%s%s", date, tz, ext ? ext : "");
+
+		purple_build_dir (dir, S_IRUSR | S_IWUSR | S_IXUSR);
+
+		filename = g_build_filename(dir, name, NULL);
+		g_free(dir);
+		g_free(name);
+
+		log->logger_data = g_fopen(filename, "a");
+		if (!log->logger_data) {
+			purple_debug(PURPLE_DEBUG_ERROR, "log", "Could not create log file %s\n", filename);
+			g_free(filename);
+			return;
+		}
+		g_free(filename);
+		fprintf(log->logger_data, "<?xml version='1.0' encoding='UTF-8' ?>\n"
+			"<?xml-stylesheet href='file:///usr/src/web/htdocs/log-stylesheet.xsl' type='text/xml' ?>\n");
+
+		date = purple_utf8_strftime("%Y-%m-%d %H:%M:%S", localtime(&log->time));
+		fprintf(log->logger_data, "<conversation time='%s' screenname='%s' protocol='%s'>\n",
+			date, log->name, prpl);
+	}
+
+	/* if we can't write to the file, give up before we hurt ourselves */
+	if(!data->file)
+		return;
+
+	date = log_get_timestamp(log, time);
+
+	purple_markup_html_to_xhtml(message, &xhtml, NULL);
+	if (from)
+		fprintf(log->logger_data, "<message %s %s from='%s' time='%s'>%s</message>\n",
+			str_from_msg_type(type),
+			type & PURPLE_MESSAGE_SEND ? "direction='sent'" :
+			type & PURPLE_MESSAGE_RECV ? "direction='received'" : "",
+			from, date, xhtml);
+	else
+		fprintf(log->logger_data, "<message %s %s time='%s'>%s</message>\n",
+			str_from_msg_type(type),
+			type & PURPLE_MESSAGE_SEND ? "direction='sent'" :
+			type & PURPLE_MESSAGE_RECV ? "direction='received'" : "",
+			date, xhtml):
+	fflush(log->logger_data);
+	g_free(date);
+	g_free(xhtml);
+}
+
+ static void
+ xml_logger_finalize(PurpleLog *log)
+{
+	if (log->logger_data) {
+		fprintf(log->logger_data, "</conversation>\n");
+		fclose(log->logger_data);
+		log->logger_data = NULL;
+	}
+}
+
+static GList *
+xml_logger_list(PurpleLogType type, const gchar *sn, PurpleAccount *account)
+{
+	return purple_log_common_lister(type, sn, account, ".xml", &xml_logger);
+}
+
+static PurpleLogLogger xml_logger =  {
+	N_("XML"), "xml",
+	NULL,
+	xml_logger_write,
+	xml_logger_finalize,
+	xml_logger_list,
+	NULL,
+	NULL,
+	NULL
+};
+#endif
+
============================================================
--- /dev/null	
+++ libpurple/xmllog.h	da39a3ee5e6b4b0d3255bfef95601890afd80709


More information about the Commits mailing list