cpw.nader.asynclogging: 8caaf0ca: Brought over local changes into a new br...
morshed.nader at gmail.com
morshed.nader at gmail.com
Mon Jan 10 12:45:27 EST 2011
----------------------------------------------------------------------
Revision: 8caaf0ca0385ed083a5f8e05073a89225efadeb1
Parent: 538b851ae7c6466621d0c3caa2e13a8a9097cf02
Author: morshed.nader at gmail.com
Date: 01/10/11 01:31:21
Branch: im.pidgin.cpw.nader.asynclogging
URL: http://d.pidgin.im/viewmtn/revision/info/8caaf0ca0385ed083a5f8e05073a89225efadeb1
Changelog:
Brought over local changes into a new branch, clearing up all the messy merge absurdity on the old branch
Changes against parent 538b851ae7c6466621d0c3caa2e13a8a9097cf02
patched ChangeLog
patched ChangeLog.API
patched configure.ac
patched finch/gntlog.c
patched finch/gntlog.h
patched finch/plugins/gnthistory.c
patched libpurple/Makefile.mingw
patched libpurple/connection.c
patched libpurple/conversation.c
patched libpurple/log.c
patched libpurple/log.h
patched libpurple/status.c
patched pidgin/Makefile.mingw
patched pidgin/gtklog.c
patched pidgin/gtklog.h
patched pidgin/gtkutils.c
patched pidgin/plugins/Makefile.mingw
patched pidgin/plugins/history.c
-------------- next part --------------
============================================================
--- ChangeLog da38619317d5f22efd76a0e65d96f4d4c1e8c442
+++ ChangeLog 089643e3d2629a73b99338f26884e09c42c4dc54
@@ -1,5 +1,19 @@ Pidgin and Finch: The Pimpin' Penguin IM
Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
+version 2.8.0 (??/??/????):
+ libpurple:
+ * Added a non-blocking API (#2280)
+ * Added a dependency for gio-2.0, which is packaged with glib-2.0 >= 2.16
+
+ Finch:
+ * Updated various methods to make use of the non-blocking API (#2280)
+
+ Pidgin:
+ * Updated various methods to make use of the non-blocking API (#2280)
+
+ Plugins:
+ * Updated the History and GntHistory plugins to make use of the non-blocking API (#2280)
+
version 2.7.10 (??/??/????):
General:
* Force video sources to all have the same capabilities. This reduces the
============================================================
--- libpurple/conversation.c ee99c716bc1eee1b506e868d1ba00cbaf5ff511a
+++ libpurple/conversation.c 5189741e8d77163db36db82793df9a1f5a37693b
@@ -40,6 +40,8 @@ static PurpleConversationUiOps *default_
static GList *chats = NULL;
static PurpleConversationUiOps *default_ops = NULL;
+static GCancellable *write_conv_cancel = NULL;
+
/**
* A hash table used for efficient lookups of conversations by name.
* struct _purple_hconv => PurpleConversation*
@@ -992,11 +994,9 @@ purple_conversation_write(PurpleConversa
if (conv->logs == NULL)
open_log(conv);
- log = conv->logs;
- while (log != NULL) {
- purple_log_write((PurpleLog *)log->data, flags, alias, mtime, displayed);
- log = log->next;
- }
+ for (log = conv->logs; log != NULL; log = g_list_next(log))
+ purple_log_write_async((PurpleLog *) log->data, flags, alias, mtime, displayed,
+ G_PRIORITY_DEFAULT, write_conv_cancel, NULL, NULL);
}
if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
@@ -1010,6 +1010,7 @@ purple_conversation_write(PurpleConversa
add_message_to_history(conv, who, alias, message, flags, mtime);
+ // We should have a "wrote-log-msg" signal!
purple_signal_emit(purple_conversations_get_handle(),
(type == PURPLE_CONV_TYPE_IM ? "wrote-im-msg" : "wrote-chat-msg"),
account, who, displayed, conv, flags);
@@ -2254,6 +2255,8 @@ purple_conversations_init(void)
{
void *handle = purple_conversations_get_handle();
+ write_conv_cancel = g_cancellable_new();
+
conversation_cache = g_hash_table_new_full((GHashFunc)_purple_conversations_hconv_hash,
(GEqualFunc)_purple_conversations_hconv_equal,
(GDestroyNotify)_purple_conversations_hconv_free_key, NULL);
@@ -2565,8 +2568,15 @@ purple_conversations_uninit(void)
void
purple_conversations_uninit(void)
{
+ if (write_conv_cancel != NULL) {
+ g_cancellable_cancel(write_conv_cancel);
+ g_object_unref(write_conv_cancel);
+ write_conv_cancel = NULL;
+ }
+
while (conversations)
- purple_conversation_destroy((PurpleConversation*)conversations->data);
+ purple_conversation_destroy((PurpleConversation *) conversations->data);
+
g_hash_table_destroy(conversation_cache);
purple_signals_unregister_by_instance(purple_conversations_get_handle());
}
============================================================
--- configure.ac 0376c94d79e039d1fa217e953c9d52973027e108
+++ configure.ac 1e0cdc95231419582ac72f78df13f2615ee6e7ca
@@ -327,9 +327,9 @@ dnl ####################################
AM_CONDITIONAL(INSTALL_I18N, test "x$enable_i18n" = "xyes")
dnl #######################################################################
-dnl # Check for GLib 2.12 (required)
+dnl # Check for GLib 2.16 (required)
dnl #######################################################################
-PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.12.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [
+PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.16 gio-2.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [
AC_MSG_RESULT(no)
AC_MSG_ERROR([
============================================================
--- pidgin/plugins/history.c 651d7f4b1f6c8ac7691e335a36e7d7616ddd2916
+++ pidgin/plugins/history.c 4eb504f741acefd38c410540e6dbea15e7d31a40
@@ -21,170 +21,376 @@
#define HISTORY_SIZE (4 * 1024)
-static gboolean _scroll_imhtml_to_end(gpointer data)
-{
- GtkIMHtml *imhtml = data;
- gtk_imhtml_scroll_to_end(GTK_IMHTML(imhtml), FALSE);
- g_object_unref(G_OBJECT(imhtml));
- return FALSE;
-}
+typedef struct {
+ PurpleAccount *account;
+ PurpleConversation *conv;
+ PurpleLog *log;
+ PurpleLogReadFlags flags;
+ GCancellable *cancel;
+ const gchar *name;
+ const gchar *alias;
+ gulong delete_handle;
+ guint count;
+} _historize_callback_data;
-static void historize(PurpleConversation *c)
+static void historize(PurpleConversation *);
+static gboolean _scroll_imhtml_to_end(gpointer);
+static PurpleLog *get_last_log(GList *, PurpleLog *);
+static void historize_log_read_cb(GObject *, GAsyncResult *, gpointer);
+static void historize_log_list_cb(GObject *, GAsyncResult *, gpointer);
+static void historize_log_list_cb_done(_historize_callback_data *);
+static void historize_log_collector_cb(GObject *, GAsyncResult *, gpointer);
+static void historize_log_collector_cb_done(_historize_callback_data *);
+static void historize_stop(PurpleConversation *, gpointer);
+static void historize_log_free(_historize_callback_data *);
+static void history_prefs_check(PurplePlugin *);
+static void history_prefs_cb(const gchar *, PurplePrefType, gconstpointer, gpointer);
+static gboolean plugin_load(PurplePlugin *);
+static void init_plugin(PurplePlugin *);
+
+
+static void
+historize(PurpleConversation *c)
{
+ _historize_callback_data *callback_data;
PurpleAccount *account = purple_conversation_get_account(c);
- const char *name = purple_conversation_get_name(c);
- PurpleConversationType convtype;
- GList *logs = NULL;
- const char *alias = name;
- guint flags;
- char *history;
- PidginConversation *gtkconv;
- GtkIMHtmlOptions options = GTK_IMHTML_NO_COLOURS;
- char *header;
- char *protocol;
- char *escaped_alias;
- const char *header_date;
+ PurpleConversationType convtype = purple_conversation_get_type(c);
+ PidginConversation *gtkconv = PIDGIN_CONVERSATION(c);
+ const gchar *name = purple_conversation_get_name(c), *alias = name;
- convtype = purple_conversation_get_type(c);
- gtkconv = PIDGIN_CONVERSATION(c);
g_return_if_fail(gtkconv != NULL);
+ g_return_if_fail(gtkconv->convs != NULL);
- /* An IM which is the first active conversation. */
- g_return_if_fail(gtkconv->convs != NULL);
- if (convtype == PURPLE_CONV_TYPE_IM && !gtkconv->convs->next)
- {
- GSList *buddies;
- GSList *cur;
+ callback_data = g_new0(_historize_callback_data, 1);
+ callback_data->conv = c;
+ callback_data->alias = alias;
+ callback_data->name = name;
+ callback_data->account = account;
+ callback_data->cancel = g_cancellable_new();
+ if (convtype == PURPLE_CONV_TYPE_IM && !gtkconv->convs->next) {
+ GSList *buddies, *cur;
+
/* If we're not logging, don't show anything.
* Otherwise, we might show a very old log. */
- if (!purple_prefs_get_bool("/purple/logging/log_ims"))
+ if (!purple_prefs_get_bool("/purple/logging/log_ims")) {
+ g_object_unref(callback_data->cancel);
+ g_free(callback_data);
+
return;
+ }
/* Find buddies for this conversation. */
buddies = purple_find_buddies(account, name);
/* If we found at least one buddy, save the first buddy's alias. */
if (buddies != NULL)
- alias = purple_buddy_get_contact_alias((PurpleBuddy *)buddies->data);
+ alias = purple_buddy_get_contact_alias(PURPLE_BUDDY(buddies->data));
- for (cur = buddies; cur != NULL; cur = cur->next)
- {
+ for (cur = buddies; cur != NULL; cur = g_slist_next(cur)) {
PurpleBlistNode *node = cur->data;
- PurpleBlistNode *prev = purple_blist_node_get_sibling_prev(node);
- PurpleBlistNode *next = purple_blist_node_get_sibling_next(node);
- if ((node != NULL) && ((prev != NULL) || (next != NULL)))
+
+ if (node != NULL &&
+ (purple_blist_node_get_sibling_prev(node) != NULL ||
+ purple_blist_node_get_sibling_next(node) != NULL))
{
PurpleBlistNode *node2;
PurpleBlistNode *parent = purple_blist_node_get_parent(node);
PurpleBlistNode *child = purple_blist_node_get_first_child(parent);
- alias = purple_buddy_get_contact_alias((PurpleBuddy *)node);
+ alias = purple_buddy_get_contact_alias(PURPLE_BUDDY(node));
/* We've found a buddy that matches this conversation. It's part of a
* PurpleContact with more than one PurpleBuddy. Loop through the PurpleBuddies
* in the contact and get all the logs. */
- for (node2 = child ; node2 != NULL ; node2 = purple_blist_node_get_sibling_next(node2))
+ for (node2 = child; node2 != NULL; node2 = purple_blist_node_get_sibling_next(node2))
{
- logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM,
- purple_buddy_get_name((PurpleBuddy *)node2),
- purple_buddy_get_account((PurpleBuddy *)node2)),
- logs);
+ PurpleBuddy *buddy = PURPLE_BUDDY(node2);
+
+ callback_data->count++;
+
+ purple_log_get_logs_async(PURPLE_LOG_IM, purple_buddy_get_name(buddy),
+ purple_buddy_get_account(buddy), G_PRIORITY_DEFAULT,
+ callback_data->cancel, historize_log_collector_cb, callback_data);
}
+
break;
}
}
- g_slist_free(buddies);
- if (logs == NULL)
- logs = purple_log_get_logs(PURPLE_LOG_IM, name, account);
- else
- logs = g_list_sort(logs, purple_log_compare);
- }
- else if (convtype == PURPLE_CONV_TYPE_CHAT)
- {
+ g_slist_free(buddies);
+ } else if (convtype == PURPLE_CONV_TYPE_CHAT) {
/* If we're not logging, don't show anything.
* Otherwise, we might show a very old log. */
- if (!purple_prefs_get_bool("/purple/logging/log_chats"))
+ if (!purple_prefs_get_bool("/purple/logging/log_chats")) {
+ g_object_unref(callback_data->cancel);
+ g_free(callback_data);
+
return;
+ }
- logs = purple_log_get_logs(PURPLE_LOG_CHAT, name, account);
+ callback_data->count = 1;
+
+ purple_log_get_logs_async(PURPLE_LOG_CHAT, name, account, G_PRIORITY_DEFAULT,
+ callback_data->cancel, historize_log_list_cb, callback_data);
}
- if (logs == NULL)
+ callback_data->delete_handle = purple_signal_connect(purple_conversations_get_handle(),
+ "deleting-conversation", c, PURPLE_CALLBACK(historize_stop), callback_data);
+}
+
+static gboolean
+_scroll_imhtml_to_end(gpointer userdata)
+{
+ GtkIMHtml *imhtml = GTK_IMHTML(userdata);
+
+ gtk_imhtml_scroll_to_end(imhtml, FALSE);
+ g_object_unref(imhtml);
+
+ return FALSE;
+}
+
+static PurpleLog *
+get_last_log(GList *list, PurpleLog *last_log)
+{
+ GList *node;
+
+ if (last_log == NULL) {
+ last_log = list->data;
+ node = g_list_next(list);
+ } else
+ node = list;
+
+ for( ; node; node = g_list_next(node))
+ if (purple_log_compare(last_log, node->data) > 0) {
+ purple_log_free(last_log);
+ last_log = node->data;
+ } else
+ purple_log_free(node->data);
+
+ g_list_free(list);
+
+ return last_log;
+}
+
+static void
+historize_log_read_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _historize_callback_data *callback_data = userdata;
+ PurpleLog *log = callback_data->log;
+ PidginConversation *gtkconv = PIDGIN_CONVERSATION(callback_data->conv);
+ GtkIMHtmlOptions options;
+ GtkIMHtml *imhtml = GTK_IMHTML(gtkconv->imhtml);
+ GError *err = NULL;
+ const gchar *header_date;
+ gchar *text, *escaped_alias, *header, *full_header, *text_backup, *protocol, *imhtml_text;
+
+ if (res == NULL) {
+ callback_data->count--;
+
+ if (callback_data->count < 1)
+ historize_log_free(callback_data);
+
return;
+ }
- history = purple_log_read((PurpleLog*)logs->data, &flags);
- gtkconv = PIDGIN_CONVERSATION(c);
- if (flags & PURPLE_LOG_READ_NO_NEWLINE)
- options |= GTK_IMHTML_NO_NEWLINE;
+ options = GTK_IMHTML_NO_COLOURS;
+ imhtml_text = gtk_imhtml_get_markup(imhtml);
- protocol = g_strdup(gtk_imhtml_get_protocol_name(GTK_IMHTML(gtkconv->imhtml)));
- gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml),
- purple_account_get_protocol_name(((PurpleLog*)logs->data)->account));
+ if (imhtml_text != NULL && *imhtml_text) {
+ text_backup = strdup(imhtml_text);
+ gtk_imhtml_clear(imhtml);
+ } else
+ text_backup = NULL;
- if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml))))
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", options);
+ protocol = g_strdup(gtk_imhtml_get_protocol_name(imhtml));
+ gtk_imhtml_set_protocol_name(imhtml,
+ purple_account_get_protocol_name(callback_data->log->account));
- escaped_alias = g_markup_escape_text(alias, -1);
+ if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml))))
+ gtk_imhtml_append_text(imhtml, "<br>", options);
- if (((PurpleLog *)logs->data)->tm)
- header_date = purple_date_format_full(((PurpleLog *)logs->data)->tm);
+ if (log->tm)
+ header_date = purple_date_format_full(log->tm);
else
- header_date = purple_date_format_full(localtime(&((PurpleLog *)logs->data)->time));
+ header_date = purple_date_format_full(localtime(&log->time));
- header = g_strdup_printf(_("<b>Conversation with %s on %s:</b><br>"), escaped_alias, header_date);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), header, options);
+ escaped_alias = g_markup_escape_text(callback_data->alias, -1);
+ full_header = g_strdup_printf(_("Conversation with %s on %s"), escaped_alias, header_date);
+ header = g_strdup_printf("<b>%s:</b><br>", full_header);
+
+ gtk_imhtml_append_text(imhtml, header, options);
+
g_free(header);
g_free(escaped_alias);
+ g_free(full_header);
- g_strchomp(history);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), history, options);
- g_free(history);
+ text = purple_log_read_finish(log, res, &err);
- gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<hr>", options);
+ if (text == NULL)
+ text = err->message;
+ else
+ g_strchomp(text);
- gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), protocol);
+ if (callback_data->flags & PURPLE_LOG_READ_NO_NEWLINE)
+ options |= GTK_IMHTML_NO_NEWLINE;
+
+ //purple_conversation_write?
+ gtk_imhtml_append_text(imhtml, text, options);
+ gtk_imhtml_append_text(imhtml, "<hr>", options);
+ gtk_imhtml_set_protocol_name(imhtml, protocol);
+
+ if (text_backup != NULL) {
+ gtk_imhtml_append_text(imhtml, text_backup, GTK_IMHTML_NO_NEWLINE);
+ g_free(text_backup);
+ }
+
+ g_idle_add(_scroll_imhtml_to_end, g_object_ref(imhtml));
+
g_free(protocol);
+ g_clear_error(&err);
- g_object_ref(G_OBJECT(gtkconv->imhtml));
- g_idle_add(_scroll_imhtml_to_end, gtkconv->imhtml);
+ callback_data->count--;
- g_list_foreach(logs, (GFunc)purple_log_free, NULL);
- g_list_free(logs);
+ if (callback_data->count < 1)
+ historize_log_free(callback_data);
}
static void
+historize_log_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _historize_callback_data *callback_data = userdata;
+ GList *list;
+ GError *err = NULL;
+
+ if (res == NULL) {
+ callback_data->count--;
+
+ if (callback_data->count < 1)
+ historize_log_list_cb_done(callback_data);
+
+ return;
+ }
+
+ list = purple_log_get_logs_finish(res, &err);
+
+ if (list != NULL)
+ callback_data->log = get_last_log(list, callback_data->log);
+ else if (err != NULL && err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("history", "%s\n", err->message);
+
+ g_clear_error(&err);
+
+ callback_data->count--;
+
+ if (callback_data->count < 1)
+ historize_log_list_cb_done(callback_data);
+}
+
+static void
+historize_log_list_cb_done(_historize_callback_data *callback_data)
+{
+ callback_data->count++;
+
+ if (callback_data->log != NULL)
+ purple_log_read_async(callback_data->log, &callback_data->flags,
+ G_PRIORITY_DEFAULT, callback_data->cancel, historize_log_read_cb,
+ callback_data);
+ else
+ historize_log_read_cb(NULL, NULL, callback_data);
+}
+
+static void
+historize_log_collector_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _historize_callback_data *callback_data = userdata;
+ GList *list;
+ GError *err = NULL;
+
+ list = purple_log_get_logs_finish(res, &err);
+
+ if (list != NULL)
+ callback_data->log = get_last_log(list, callback_data->log);
+ else if (err != NULL && err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("history", "%s\n", err->message);
+
+ g_clear_error(&err);
+
+ callback_data->count--;
+
+ if (callback_data->count < 1)
+ historize_log_collector_cb_done(userdata);
+}
+
+static void
+historize_log_collector_cb_done(_historize_callback_data *callback_data)
+{
+ callback_data->count++;
+
+ if (callback_data->log == NULL)
+ purple_log_get_logs_async(PURPLE_LOG_IM, callback_data->name,
+ callback_data->account, G_PRIORITY_DEFAULT, callback_data->cancel,
+ historize_log_list_cb, callback_data);
+ else
+ historize_log_list_cb(NULL, NULL, callback_data);
+}
+
+static void
+historize_stop(PurpleConversation *c, gpointer userdata)
+{
+ _historize_callback_data *callback_data = userdata;
+
+ if (callback_data->cancel != NULL)
+ g_cancellable_cancel(callback_data->cancel);
+
+ purple_signal_disconnect(purple_conversations_get_handle(), "deleting-conversation",
+ c, PURPLE_CALLBACK(historize_stop));
+}
+
+static void
+historize_log_free(_historize_callback_data *callback_data)
+{
+ if (callback_data->log != NULL)
+ purple_log_free(callback_data->log);
+
+ /* Why on earth does pidgin give us gulong to refer to our connected signal if it
+ offers no API to disconnect it with!? */
+ purple_signal_disconnect(purple_conversations_get_handle(), "deleting-conversation",
+ callback_data->conv, PURPLE_CALLBACK(historize_stop));
+
+ g_object_unref(callback_data->cancel);
+ g_free(callback_data);
+}
+
+static void
history_prefs_check(PurplePlugin *plugin)
{
if (!purple_prefs_get_bool("/purple/logging/log_ims") &&
- !purple_prefs_get_bool("/purple/logging/log_chats"))
- {
+ !purple_prefs_get_bool("/purple/logging/log_chats")) {
purple_notify_warning(plugin, NULL, _("History Plugin Requires Logging"),
- _("Logging can be enabled from Tools -> Preferences -> Logging.\n\n"
- "Enabling logs for instant messages and/or chats will activate "
- "history for the same conversation type(s)."));
+ _("Logging can be enabled from Tools -> Preferences -> Logging.\n\n"
+ "Enabling logs for instant messages and/or chats will activate "
+ "history for the same conversation type(s)."));
}
}
-static void history_prefs_cb(const char *name, PurplePrefType type,
- gconstpointer val, gpointer data)
+static void
+history_prefs_cb(const gchar *name, PurplePrefType type, gconstpointer val, gpointer data)
{
- history_prefs_check((PurplePlugin *)data);
+ history_prefs_check((PurplePlugin *) data);
}
static gboolean
plugin_load(PurplePlugin *plugin)
{
purple_signal_connect(purple_conversations_get_handle(),
- "conversation-created",
- plugin, PURPLE_CALLBACK(historize), NULL);
+ "conversation-created", plugin, PURPLE_CALLBACK(historize), NULL);
/* XXX: Do we want to listen to pidgin's "conversation-displayed" signal? */
purple_prefs_connect_callback(plugin, "/purple/logging/log_ims",
- history_prefs_cb, plugin);
+ history_prefs_cb, plugin);
purple_prefs_connect_callback(plugin, "/purple/logging/log_chats",
- history_prefs_cb, plugin);
+ history_prefs_cb, plugin);
history_prefs_check(plugin);
============================================================
--- libpurple/log.c d44c76dbd3a39f8d4b783ef535eb06c60f0368de
+++ libpurple/log.c a190fb11d41f65733874f305d45724048e02f66a
@@ -36,56 +36,299 @@
#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
+
+#define g_static_mutex_get_mutex_impl_shortcut(mutex) \
+ (g_atomic_pointer_get (mutex) ? *(mutex) : \
+ g_static_mutex_get_mutex_impl (mutex))
+
+#endif
+
+
+typedef struct {
+ PurpleAccount *account;
+ gchar *name;
+} _purple_logsize_user;
+
+typedef struct {
+ PurpleLog *log;
+ GAsyncReadyCallback cb;
+ gpointer userdata;
+} _purple_log_free_callback_data;
+
+typedef struct {
+ PurpleLog *log;
+ GAsyncReadyCallback cb;
+ gpointer userdata;
+} _purple_log_delete_callback_data;
+
+typedef struct {
+ _purple_logsize_user *lu;
+ PurpleLog *log;
+ GAsyncReadyCallback cb;
+ gpointer userdata;
+} _purple_log_write_callback_data;
+
+typedef struct {
+ PurpleLog *log;
+ GAsyncReadyCallback cb;
+ gpointer userdata;
+} _purple_log_read_callback_data;
+
+typedef struct {
+ PurpleLog *log;
+ GAsyncReadyCallback cb;
+ gpointer userdata;
+} _purple_log_size_callback_data;
+
+typedef struct {
+ PurpleLog *log;
+ PurpleMessageFlags type;
+ gchar *from;
+ gchar *message;
+ time_t time;
+} _purple_log_logger_write_callback_data;
+
+typedef struct {
+ PurpleLog *log;
+ PurpleLogReadFlags *flags;
+} _purple_log_logger_read_callback_data;
+
+typedef struct {
+ PurpleAccount *account;
+ PurpleLogType type;
+ gchar *name;
+ gchar *ext;
+} _purple_logger_total_size_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;
+ gchar *ext;
+} _purple_logger_writer_callback_data;
+
+typedef struct {
+ PurpleAccount *account;
+ PurpleLogLogger *logger;
+ PurpleLogType 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;
+
+typedef struct {
+ _purple_logsize_user *lu;
+ GAsyncReadyCallback cb;
+ GCancellable *cancel;
+ gpointer userdata;
+ gdouble total;
+ guint count;
+ gint io_priority;
+} _purple_log_activity_score_data;
+
+typedef struct {
+ _purple_log_activity_score_data *score_data;
+ PurpleLog *log;
+} _purple_log_activity_score_size_data;
+
+typedef struct {
+ GAsyncReadyCallback cb;
+ GList *logs;
+ gpointer userdata;
+ guint counter;
+} _purple_log_logs_callback_data;
+
+typedef struct {
+ GAsyncReadyCallback cb;
+ GHashTable *sets;
+ GMutex *mutex;
+ gpointer userdata;
+ 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 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 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 deleter_thread(GSimpleAsyncResult *, GObject *, GCancellable *);
+
+static gchar *process_txt_log(gchar *, gchar *);
+
+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 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 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 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 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 PurpleLogLogger *html_logger;
static PurpleLogLogger *txt_logger;
static PurpleLogLogger *old_logger;
-struct _purple_logsize_user {
- char *name;
- PurpleAccount *account;
-};
static GHashTable *logsize_users = NULL;
static GHashTable *logsize_users_decayed = NULL;
+static GHashTable *log_ref_table = NULL;
-static void log_get_log_sets_common(GHashTable *sets);
-static gsize html_logger_write(PurpleLog *log, PurpleMessageFlags type,
- const char *from, time_t time, const char *message);
-static void html_logger_finalize(PurpleLog *log);
-static GList *html_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account);
-static GList *html_logger_list_syslog(PurpleAccount *account);
-static char *html_logger_read(PurpleLog *log, PurpleLogReadFlags *flags);
-static int html_logger_total_size(PurpleLogType type, const char *name, PurpleAccount *account);
+static void
+purple_log_logger_write_callback_data_free(gpointer userdata)
+{
+ _purple_log_logger_write_callback_data *callback_data = userdata;
-static GList *old_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account);
-static int old_logger_total_size(PurpleLogType type, const char *name, PurpleAccount *account);
-static char * old_logger_read (PurpleLog *log, PurpleLogReadFlags *flags);
-static int old_logger_size (PurpleLog *log);
-static void old_logger_get_log_sets(PurpleLogSetCallback cb, GHashTable *sets);
-static void old_logger_finalize(PurpleLog *log);
+ g_free(callback_data->from);
+ g_free(callback_data->message);
+ g_free(callback_data);
+}
-static gsize txt_logger_write(PurpleLog *log,
- PurpleMessageFlags type,
- const char *from, time_t time, const char *message);
-static void txt_logger_finalize(PurpleLog *log);
-static GList *txt_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account);
-static GList *txt_logger_list_syslog(PurpleAccount *account);
-static char *txt_logger_read(PurpleLog *log, PurpleLogReadFlags *flags);
-static int txt_logger_total_size(PurpleLogType type, const char *name, PurpleAccount *account);
+static void
+purple_logger_writer_callback_data_free(gpointer userdata)
+{
+ _purple_logger_writer_callback_data *callback_data = userdata;
+ g_free(callback_data->ext);
+ g_free(callback_data);
+}
+
+static void
+purple_logger_lister_callback_data_free(gpointer userdata)
+{
+ _purple_logger_lister_callback_data *callback_data = userdata;
+
+ g_free(callback_data->name);
+ g_free(callback_data->ext);
+ g_free(callback_data);
+}
+
+static void
+purple_logger_total_size_callback_data_free(gpointer userdata)
+{
+ _purple_logger_total_size_callback_data *callback_data = userdata;
+
+ g_free(callback_data->name);
+ g_free(callback_data->ext);
+ g_free(callback_data);
+}
+
+
/**************************************************************************
* PUBLIC LOGGING FUNCTIONS ***********************************************
**************************************************************************/
-PurpleLog *purple_log_new(PurpleLogType type, const char *name, PurpleAccount *account,
- PurpleConversation *conv, time_t time, const struct tm *tm)
+PurpleLog *
+purple_log_new(PurpleLogType type, const gchar *name, PurpleAccount *account,
+ PurpleConversation *conv, time_t time, const struct tm *tm)
{
PurpleLog *log;
+ g_return_val_if_fail(name != NULL, NULL);
+ g_return_val_if_fail(account != NULL, NULL);
+
+ log = purple_log_ref(g_slice_new(PurpleLog));
/* IMPORTANT: Make sure to initialize all the members of PurpleLog */
- log = g_slice_new(PurpleLog);
PURPLE_DBUS_REGISTER_POINTER(log, PurpleLog);
log->type = type;
@@ -98,8 +341,7 @@ PurpleLog *purple_log_new(PurpleLogType
if (tm == NULL)
log->tm = NULL;
- else
- {
+ else {
/* There's no need to zero this as we immediately do a direct copy. */
log->tm = g_slice_new(struct tm);
@@ -107,9 +349,9 @@ PurpleLog *purple_log_new(PurpleLogType
#ifdef HAVE_STRUCT_TM_TM_ZONE
/* XXX: This is so wrong... */
- if (log->tm->tm_zone != NULL)
- {
- char *tmp = g_locale_from_utf8(log->tm->tm_zone, -1, NULL, NULL, NULL);
+ 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
@@ -119,63 +361,115 @@ PurpleLog *purple_log_new(PurpleLogType
#endif
}
+ // TODO: Non-blocking create?
if (log->logger && log->logger->create)
- log->logger->create(log);
+ (log->logger->create)(log);
+
return log;
}
-void purple_log_free(PurpleLog *log)
+static PurpleLog *
+purple_log_ref(PurpleLog *log)
{
- g_return_if_fail(log);
- if (log->logger && log->logger->finalize)
- log->logger->finalize(log);
- g_free(log->name);
+ guint ref_count;
+ gpointer value;
- if (log->tm != NULL)
- {
-#ifdef HAVE_STRUCT_TM_TM_ZONE
- /* XXX: This is so wrong... */
- g_free((char *)log->tm->tm_zone);
-#endif
- g_slice_free(struct tm, log->tm);
+ G_LOCK(log_ref_table);
+
+ 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_hash_table_insert(log_ref_table, log, GUINT_TO_POINTER(ref_count));
+
+ G_UNLOCK(log_ref_table);
+
+ return log;
+}
+
+static void
+purple_log_unref(PurpleLog *log)
+{
+ guint ref_count;
+ gpointer value;
+
+ G_LOCK(log_ref_table);
+
+ 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_UNLOCK(log_ref_table);
+
+ 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;
+
+ log->logger->finalize(log);
+
+ 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);
+ }
}
+}
- PURPLE_DBUS_UNREGISTER_POINTER(log);
- g_slice_free(PurpleLog, log);
+void
+purple_log_free(PurpleLog *log)
+{
+ g_return_if_fail(log != NULL);
+
+ purple_log_unref(log);
}
-void purple_log_write(PurpleLog *log, PurpleMessageFlags type,
- const char *from, time_t time, const char *message)
+void
+purple_log_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
+ const gchar *message)
{
- struct _purple_logsize_user *lu;
- gsize written, total = 0;
+ _purple_logsize_user *lu;
gpointer ptrsize;
+ gsize written, total;
- g_return_if_fail(log);
- g_return_if_fail(log->logger);
- g_return_if_fail(log->logger->write);
+ 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);
written = (log->logger->write)(log, type, from, time, message);
- lu = g_new(struct _purple_logsize_user, 1);
+ lu = g_new(_purple_logsize_user, 1);
+ total = 0;
lu->name = g_strdup(purple_normalize(log->account, log->name));
lu->account = log->account;
+ G_LOCK(logsize_users);
if(g_hash_table_lookup_extended(logsize_users, lu, NULL, &ptrsize)) {
- char *tmp = lu->name;
+ gchar *tmp = lu->name;
total = GPOINTER_TO_INT(ptrsize);
total += written;
g_hash_table_replace(logsize_users, lu, GINT_TO_POINTER(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(struct _purple_logsize_user, 1);
+ lu = g_new(_purple_logsize_user, 1);
lu->name = g_strdup(tmp);
lu->account = log->account;
}
+ 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;
@@ -184,114 +478,437 @@ void purple_log_write(PurpleLog *log, Pu
g_free(lu->name);
g_free(lu);
}
+ G_UNLOCK(logsize_users_decayed);
}
-char *purple_log_read(PurpleLog *log, PurpleLogReadFlags *flags)
+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)
{
- PurpleLogReadFlags mflags;
- g_return_val_if_fail(log && log->logger, NULL);
+ _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);
+
+ lu = g_new(_purple_logsize_user, 1);
+
+ lu->name = g_strdup(purple_normalize(log->account, log->name));
+ lu->account = log->account;
+
+ 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;
+
+ purple_log_ref(log);
+
+ 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);
+
+ simple = g_simple_async_result_new(NULL, log_write_cb, callback_data,
+ purple_log_write_async);
+
+ g_simple_async_result_set_op_res_gssize(simple, size);
+ g_simple_async_result_complete_in_idle(simple);
+
+ 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_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+ }
+}
+
+gssize
+purple_log_write_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ 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_write_async, -1);
+
+ return g_simple_async_result_get_op_res_gssize(simple);
+}
+
+gchar *
+purple_log_read(PurpleLog *log, PurpleLogReadFlags *flags)
+{
+ gchar *ret;
+
+ g_return_val_if_fail(log != NULL, NULL);
+ g_return_val_if_fail(log->logger != NULL, NULL);
+
if (log->logger->read) {
- char *ret = (log->logger->read)(log, flags ? flags : &mflags);
+ ret = (log->logger->read)(log, flags);
purple_str_strip_char(ret, '\r');
+
return ret;
}
- return g_strdup(_("<b><font color=\"red\">The logger has no read function</font></b>"));
+
+ return g_strdup_printf("<b><font color=\"red\">%s</font></b>",
+ _("The logger has no read function"));
}
-int purple_log_get_size(PurpleLog *log)
+void
+purple_log_read_async(PurpleLog *log, PurpleLogReadFlags *flags, gint io_priority,
+ GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
{
- g_return_val_if_fail(log && log->logger, 0);
+ _purple_log_read_callback_data *callback_data;
+ GSimpleAsyncResult *simple;
+ gchar *read;
+ g_return_if_fail(log != NULL);
+ g_return_if_fail(log->logger != NULL);
+
+ callback_data = g_new0(_purple_log_read_callback_data, 1);
+ callback_data->cb = cb;
+ callback_data->userdata = userdata;
+ callback_data->log = log;
+
+ purple_log_ref(log);
+
+ 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);
+
+ simple = g_simple_async_result_new(NULL, log_read_cb, callback_data,
+ purple_log_read_async);
+
+ g_simple_async_result_set_op_res_gpointer(simple, read, g_free);
+ g_simple_async_result_complete_in_idle(simple);
+
+ 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"));
+
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+ }
+}
+
+gchar *
+purple_log_read_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ simple = G_SIMPLE_ASYNC_RESULT(res);
+
+ 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_read_async, NULL);
+
+ return g_simple_async_result_get_op_res_gpointer(simple);
+}
+
+gint
+purple_log_get_size(PurpleLog *log)
+{
+ g_return_val_if_fail(log != NULL, 0);
+ g_return_val_if_fail(log->logger != NULL, 0);
+
if (log->logger->size)
- return log->logger->size(log);
+ return (log->logger->size)(log);
+
return 0;
}
-static guint _purple_logsize_user_hash(struct _purple_logsize_user *lu)
+void
+purple_log_get_size_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
+ GAsyncReadyCallback cb, gpointer userdata)
{
+ _purple_log_size_callback_data *callback_data;
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail(log != NULL);
+ g_return_if_fail(log->logger != NULL);
+
+ callback_data = g_new0(_purple_log_size_callback_data, 1);
+ callback_data->cb = cb;
+ callback_data->userdata = userdata;
+ callback_data->log = log;
+
+ purple_log_ref(log);
+
+ 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);
+
+ simple = g_simple_async_result_new(NULL, log_size_cb, callback_data,
+ purple_log_get_size_async);
+
+ g_simple_async_result_set_op_res_gssize(simple, size);
+ g_simple_async_result_complete_in_idle(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"));
+
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+ }
+}
+
+gssize
+purple_log_get_size_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+ return purple_log_common_sizer_finish(log, res, error);
+}
+
+static guint
+_purple_logsize_user_hash(_purple_logsize_user *lu)
+{
return g_str_hash(lu->name);
}
-static guint _purple_logsize_user_equal(struct _purple_logsize_user *lu1,
- struct _purple_logsize_user *lu2)
+static guint
+_purple_logsize_user_equal(_purple_logsize_user *lu1, _purple_logsize_user *lu2)
{
- return (lu1->account == lu2->account && purple_strequal(lu1->name, lu2->name));
+ return lu1->account == lu2->account && purple_strequal(lu1->name, lu2->name);
}
-static void _purple_logsize_user_free_key(struct _purple_logsize_user *lu)
+static void
+_purple_logsize_user_free_key(_purple_logsize_user *lu)
{
g_free(lu->name);
g_free(lu);
}
-int purple_log_get_total_size(PurpleLogType type, const char *name, PurpleAccount *account)
+gint
+purple_log_get_total_size(PurpleLogType type, const gchar *name, PurpleAccount *account)
{
+ _purple_logsize_user *lu;
+ GSList *n;
gpointer ptrsize;
- int size = 0;
- GSList *n;
- struct _purple_logsize_user *lu;
+ gboolean result;
+ gint size = 0;
- lu = g_new(struct _purple_logsize_user, 1);
+ g_return_val_if_fail(name != NULL, 0);
+ g_return_val_if_fail(account != NULL, 0);
+
+ lu = g_new(_purple_logsize_user, 1);
lu->name = g_strdup(purple_normalize(account, name));
- lu->account = account;
+ lu->account = account; //g_object_ref?
- if(g_hash_table_lookup_extended(logsize_users, lu, NULL, &ptrsize)) {
+ G_LOCK(logsize_users);
+ 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);
+
g_free(lu->name);
g_free(lu);
} else {
- for (n = loggers; n; n = n->next) {
+ for (n = purple_log_logger_get_all(); n; n = g_slist_next(n)) {
PurpleLogLogger *logger = n->data;
if(logger->total_size){
size += (logger->total_size)(type, name, account);
} else if(logger->list) {
GList *logs = (logger->list)(type, name, account);
- int this_size = 0;
- while (logs) {
- PurpleLog *log = (PurpleLog*)(logs->data);
- this_size += purple_log_get_size(log);
+ for ( ; logs != NULL; logs = g_list_delete_link(logs, logs)) {
+ PurpleLog *log = logs->data;
+ size += purple_log_get_size(log);
purple_log_free(log);
- logs = g_list_delete_link(logs, logs);
}
-
- size += this_size;
}
}
+ purple_debug_info("log", "HASH(purple_log_get_total_size): write size to hash %i {name = \"%s\"\n}", size, name);
+
+ G_LOCK(logsize_users);
g_hash_table_replace(logsize_users, lu, GINT_TO_POINTER(size));
+ G_LOCK(logsize_users);
}
+
return size;
}
-gint purple_log_get_activity_score(PurpleLogType type, const char *name, PurpleAccount *account)
+// 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: 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
+// TODO: where the logger had a size function (but no total_size function).
+// TODO: We could size just those with something like this (this ignores
+// TODO: the blocking vs. non-blocking distinction for simplicity):
+// TODO: for (...) {
+// 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)
{
- gpointer ptrscore;
- int score;
+ _purple_log_total_size_callback_data *callback_data;
+ _purple_logsize_user *lu;
+ GSimpleAsyncResult *simple;
GSList *n;
- struct _purple_logsize_user *lu;
+ gpointer ptrsize;
+ gboolean result;
+ gssize size = 0;
+
+ g_return_if_fail(name != NULL);
+ g_return_if_fail(account != NULL);
+
+ lu = g_new(_purple_logsize_user, 1);
+ lu->name = g_strdup(purple_normalize(account, name));
+ lu->account = account; //g_object_ref?
+
+ 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);
+
+ if (result) {
+ size = GPOINTER_TO_INT(ptrsize);
+
+ g_free(lu->name);
+ g_free(lu);
+
+ callback_data->counter = 1;
+
+ simple = g_simple_async_result_new(NULL, log_total_size_list_cb,
+ callback_data, purple_log_common_total_sizer_async);
+
+ g_simple_async_result_set_op_res_gssize(simple, size);
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+
+ return;
+ }
+
+ 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;
+
+ for ( ; n; n = g_slist_next(n)) {
+ PurpleLogLogger *logger = n->data;
+
+ 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);
+
+ 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);
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+ } else if (logger->list) {
+ /* List the logs and manually size them all up */
+ GList *logs = (logger->list)(type, name, account);
+
+ simple = g_simple_async_result_new(NULL, log_total_size_list_cb, callback_data,
+ purple_log_common_lister_async);
+
+ 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);
+ }
+ }
+}
+
+gssize
+purple_log_get_total_size_finish(GAsyncResult *res, GError **error)
+{
+ return purple_log_common_total_sizer_finish(res, &err);
+}
+
+gint
+purple_log_get_activity_score(PurpleLogType type, const gchar *name, PurpleAccount *account)
+{
+ _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);
+
time(&now);
- lu = g_new(struct _purple_logsize_user, 1);
+ lu = g_new(_purple_logsize_user, 1);
lu->name = g_strdup(purple_normalize(account, name));
- lu->account = account;
+ lu->account = account; //g_object_ref?
- if(g_hash_table_lookup_extended(logsize_users_decayed, lu, NULL, &ptrscore)) {
+ 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);
} else {
- double score_double = 0.0;
- for (n = loggers; n; n = n->next) {
- PurpleLogLogger *logger = n->data;
+ score_double = 0.0;
+ for (n = purple_log_logger_get_all(); n; n = g_slist_next(n)) {
+ logger = n->data;
+
if(logger->list) {
- GList *logs = (logger->list)(type, name, account);
+ logs = (logger->list)(type, name, account);
while (logs) {
- PurpleLog *log = (PurpleLog*)(logs->data);
+ 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) *
@@ -303,17 +920,90 @@ gint purple_log_get_activity_score(Purpl
}
score = (gint) ceil(score_double);
+
+ G_LOCK(logsize_users_decayed);
g_hash_table_replace(logsize_users_decayed, lu, GINT_TO_POINTER(score));
+ G_UNLOCK(logsize_users_decayed);
}
+
return score;
}
-gboolean purple_log_is_deletable(PurpleLog *log)
+void
+purple_log_get_activity_score_async(PurpleLogType type, const gchar *name, PurpleAccount *account,
+ gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
{
+ _purple_log_activity_score_data *callback_data;
+ _purple_logsize_user *lu;
+ GSimpleAsyncResult *simple;
+ gpointer ptrscore;
+ gint score;
+ gboolean result;
+
+ g_return_if_fail(name != NULL);
+ g_return_if_fail(account != NULL);
+
+ 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);
+
+ g_simple_async_result_set_op_res_gssize(simple, score);
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+
+ return;
+ }
+
+ 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;
+
+ if (cancellable != NULL)
+ g_object_ref(cancellable);
+
+ purple_log_get_logs_async(type, name, account, io_priority, cancellable,
+ log_get_activity_score_cb, callback_data);
+}
+
+gint
+purple_log_get_activity_score_finish(GAsyncResult *res, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ 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_get_activity_score_async, -1);
+
+ return g_simple_async_result_get_op_res_gssize(simple);
+}
+
+gboolean
+purple_log_is_deletable(PurpleLog *log)
+{
g_return_val_if_fail(log != NULL, FALSE);
g_return_val_if_fail(log->logger != NULL, FALSE);
- if (log->logger->remove == NULL)
+ if (log->logger->remove == NULL && log->logger->async->remove_async == NULL)
return FALSE;
if (log->logger->is_deletable != NULL)
@@ -322,7 +1012,8 @@ gboolean purple_log_is_deletable(PurpleL
return TRUE;
}
-gboolean purple_log_delete(PurpleLog *log)
+gboolean
+purple_log_delete(PurpleLog *log)
{
g_return_val_if_fail(log != NULL, FALSE);
g_return_val_if_fail(log->logger != NULL, FALSE);
@@ -333,27 +1024,81 @@ gboolean purple_log_delete(PurpleLog *lo
return FALSE;
}
-char *
-purple_log_get_log_dir(PurpleLogType type, const char *name, PurpleAccount *account)
+void
+purple_log_delete_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
+ GAsyncReadyCallback cb, gpointer userdata)
{
+ _purple_log_delete_callback_data *callback_data;
+ GSimpleAsyncResult *simple;
+ gboolean result;
+
+ g_return_if_fail(log != NULL);
+ g_return_if_fail(log->logger != NULL);
+
+ callback_data = g_new0(_purple_log_delete_callback_data, 1);
+ callback_data->cb = cb;
+ callback_data->userdata = userdata;
+ callback_data->log = log;
+
+ 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;
+
+ if (log->logger->remove != NULL)
+ result = (log->logger->remove)(log);
+
+ simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_delete_async);
+
+ g_simple_async_result_set_op_res_gboolean(simple, result);
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+ }
+}
+
+gboolean
+purple_log_delete_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ simple = G_SIMPLE_ASYNC_RESULT(res);
+
+ if (g_simple_async_result_propagate_error(simple, error))
+ return FALSE;
+
+ 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);
+}
+
+gchar *
+purple_log_get_log_dir(PurpleLogType type, const gchar *name, PurpleAccount *account)
+{
PurplePlugin *prpl;
PurplePluginProtocolInfo *prpl_info;
- const char *prpl_name;
- char *acct_name;
- const char *target;
- char *dir;
+ const gchar *prpl_name, *target;
+ gchar *acct_name, *dir, *temp;
+ g_return_val_if_fail(name != NULL, NULL);
+ g_return_val_if_fail(account != NULL, NULL);
+
prpl = purple_find_prpl(purple_account_get_protocol_id(account));
+
if (!prpl)
return NULL;
+
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
prpl_name = prpl_info->list_icon(account, NULL);
acct_name = g_strdup(purple_escape_filename(purple_normalize(account,
- purple_account_get_username(account))));
+ purple_account_get_username(account))));
if (type == PURPLE_LOG_CHAT) {
- char *temp = g_strdup_printf("%s.chat", purple_normalize(account, name));
+ temp = g_strdup_printf("%s.chat", purple_normalize(account, name));
target = purple_escape_filename(temp);
g_free(temp);
} else if(type == PURPLE_LOG_SYSTEM) {
@@ -362,7 +1107,10 @@ purple_log_get_log_dir(PurpleLogType typ
target = purple_escape_filename(purple_normalize(account, name));
}
- dir = g_build_filename(purple_user_dir(), "logs", prpl_name, acct_name, target, NULL);
+ if (strlen(target) > 0)
+ dir = g_build_filename(purple_user_dir(), "logs", prpl_name, acct_name, target, NULL);
+ else
+ dir = NULL;
g_free(acct_name);
@@ -373,40 +1121,30 @@ purple_log_get_log_dir(PurpleLogType typ
* LOGGER FUNCTIONS *********************************************************
****************************************************************************/
-static PurpleLogLogger *current_logger = NULL;
-
-static void logger_pref_cb(const char *name, PurplePrefType type,
- gconstpointer value, gpointer data)
+static void
+logger_pref_cb(const gchar *name, PurplePrefType type, gconstpointer value, gpointer data)
{
PurpleLogLogger *logger;
- GSList *l = loggers;
- while (l) {
+ GSList *l;
+
+ for (l = purple_log_logger_get_all(); l; l = g_slist_next(l)) {
logger = l->data;
+
if (purple_strequal(logger->id, value)) {
purple_log_logger_set(logger);
return;
}
- l = l->next;
}
+
+ G_LOCK(txt_logger);
purple_log_logger_set(txt_logger);
+ G_UNLOCK(txt_logger);
}
-PurpleLogLogger *purple_log_logger_new(const char *id, const char *name, int functions, ...)
+PurpleLogLogger *
+purple_log_logger_new(const gchar *id, const gchar *name, gint functions, ...)
{
-#if 0
- void(*create)(PurpleLog *),
- gsize(*write)(PurpleLog *, PurpleMessageFlags, const char *, time_t, const char *),
- void(*finalize)(PurpleLog *),
- GList*(*list)(PurpleLogType type, const char*, PurpleAccount*),
- char*(*read)(PurpleLog*, PurpleLogReadFlags*),
- int(*size)(PurpleLog*),
- int(*total_size)(PurpleLogType type, const char *name, PurpleAccount *account),
- GList*(*list_syslog)(PurpleAccount *account),
- void(*get_log_sets)(PurpleLogSetCallback cb, GHashTable *sets),
- gboolean(*remove)(PurpleLog *log),
- gboolean(*is_deletable)(PurpleLog *log))
-#endif
PurpleLogLogger *logger;
va_list args;
@@ -443,7 +1181,31 @@ PurpleLogLogger *purple_log_logger_new(c
if (functions >= 11)
logger->is_deletable = va_arg(args, void *);
+ 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);
@@ -451,51 +1213,89 @@ PurpleLogLogger *purple_log_logger_new(c
return logger;
}
-void purple_log_logger_free(PurpleLogLogger *logger)
+void
+purple_log_logger_free(PurpleLogLogger *logger)
{
+ g_return_if_fail(logger != NULL);
+
g_free(logger->name);
g_free(logger->id);
+ g_free(logger->async);
g_free(logger);
}
-void purple_log_logger_add (PurpleLogLogger *logger)
+void
+purple_log_logger_add(PurpleLogLogger *logger)
{
- g_return_if_fail(logger);
+ g_return_if_fail(logger != NULL);
+
+ G_LOCK(loggers);
if (g_slist_find(loggers, logger))
return;
+
loggers = g_slist_append(loggers, logger);
- if (purple_strequal(purple_prefs_get_string("/purple/logging/format"), logger->id)) {
+ G_UNLOCK(loggers);
+
+ if (purple_strequal(purple_prefs_get_string("/purple/logging/format"), logger->id))
purple_prefs_trigger_callback("/purple/logging/format");
- }
}
-void purple_log_logger_remove (PurpleLogLogger *logger)
+void
+purple_log_logger_remove(PurpleLogLogger *logger)
{
- g_return_if_fail(logger);
+ g_return_if_fail(logger != NULL);
+
+ G_LOCK(loggers);
loggers = g_slist_remove(loggers, logger);
+ G_UNLOCK(loggers);
}
-void purple_log_logger_set (PurpleLogLogger *logger)
+void
+purple_log_logger_set(PurpleLogLogger *logger)
{
- g_return_if_fail(logger);
+ g_return_if_fail(logger != NULL);
+
+ G_LOCK(current_logger);
current_logger = logger;
+ G_UNLOCK(current_logger);
}
-PurpleLogLogger *purple_log_logger_get()
+PurpleLogLogger *
+purple_log_logger_get(void)
{
- return current_logger;
+ PurpleLogLogger *logger;
+
+ G_LOCK(current_logger);
+ logger = current_logger;
+ G_UNLOCK(current_logger);
+
+ return logger;
}
-GList *purple_log_logger_get_options(void)
+GSList *
+purple_log_logger_get_all(void)
{
+ GSList *list;
+
+ G_LOCK(loggers);
+ list = loggers;
+ G_UNLOCK(loggers);
+
+ return list;
+}
+
+GList *
+purple_log_logger_get_options(void)
+{
GSList *n;
GList *list = NULL;
- PurpleLogLogger *data;
- for (n = loggers; n; n = n->next) {
- data = n->data;
- if (!data->write)
+ for (n = purple_log_logger_get_all(); n; n = g_slist_next(n)) {
+ PurpleLogLogger *data = n->data;
+
+ if (!data->write && !data->async->write_async)
continue;
+
list = g_list_append(list, data->name);
list = g_list_append(list, data->id);
}
@@ -503,32 +1303,106 @@ GList *purple_log_logger_get_options(voi
return list;
}
-gint purple_log_compare(gconstpointer y, gconstpointer z)
+gint
+purple_log_compare(gconstpointer y, gconstpointer z)
{
- const PurpleLog *a = y;
- const PurpleLog *b = z;
+ const PurpleLog *a = y, *b = z;
return b->time - a->time;
}
-GList *purple_log_get_logs(PurpleLogType type, const char *name, PurpleAccount *account)
+GList *
+purple_log_get_logs(PurpleLogType type, const gchar *name, PurpleAccount *account)
{
GList *logs = NULL;
GSList *n;
- for (n = loggers; n; n = n->next) {
+
+ g_return_val_if_fail(name != NULL, NULL);
+ 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;
+
if (!logger->list)
continue;
+
logs = g_list_concat(logger->list(type, name, account), logs);
}
return g_list_sort(logs, purple_log_compare);
}
-gint purple_log_set_compare(gconstpointer y, gconstpointer z)
+void
+purple_log_get_logs_async(PurpleLogType type, const gchar *name, PurpleAccount *account,
+ gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
{
- const PurpleLogSet *a = y;
- const PurpleLogSet *b = z;
+ _purple_log_logs_callback_data *callback_data;
+ GSimpleAsyncResult *simple;
+ GSList *n;
+
+ g_return_if_fail(name != NULL);
+ g_return_if_fail(account != NULL);
+
+ n = purple_log_logger_get_all();
+
+ 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;
+
+ for ( ; n; n = g_slist_next(n)) {
+ PurpleLogLogger *logger = n->data;
+
+ 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);
+
+ 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);
+
+ g_simple_async_result_complete_in_idle(simple);
+
+ 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"));
+
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+ }
+ }
+}
+
+GList *
+purple_log_get_logs_finish(GAsyncResult *res, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ simple = G_SIMPLE_ASYNC_RESULT(res);
+
+ 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);
+}
+
+gint
+purple_log_set_compare(gconstpointer y, gconstpointer z)
+{
+ const PurpleLogSet *a = y, *b = z;
gint ret = 0;
/* This logic seems weird at first...
@@ -538,15 +1412,17 @@ gint purple_log_set_compare(gconstpointe
* doesn't. */
if (a->account != NULL && b->account != NULL) {
ret = strcmp(purple_account_get_username(a->account), purple_account_get_username(b->account));
+
if (ret != 0)
return ret;
}
ret = strcmp(a->normalized_name, b->normalized_name);
+
if (ret != 0)
return ret;
- return (gint)b->type - (gint)a->type;
+ return (gint) b->type - (gint) a->type;
}
static guint
@@ -582,14 +1458,17 @@ log_add_log_set_to_hash(GHashTable *sets
purple_log_set_free(set);
}
-GHashTable *purple_log_get_log_sets(void)
+GHashTable *
+purple_log_get_log_sets(void)
{
+ GHashTable *sets;
GSList *n;
- GHashTable *sets = g_hash_table_new_full(log_set_hash, log_set_equal,
- (GDestroyNotify)purple_log_set_free, NULL);
+ 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 = loggers; n; n = n->next) {
+ for (n = purple_log_logger_get_all(); n; n = g_slist_next(n)) {
PurpleLogLogger *logger = n->data;
if (!logger->get_log_sets)
@@ -598,52 +1477,180 @@ GHashTable *purple_log_get_log_sets(void
logger->get_log_sets(log_add_log_set_to_hash, sets);
}
- log_get_log_sets_common(sets);
+ log_get_log_sets_common(sets, NULL, log_add_log_set_to_hash, NULL);
/* Return the GHashTable of unique PurpleLogSets. */
return sets;
}
-void purple_log_set_free(PurpleLogSet *set)
+void
+purple_log_get_log_sets_async(gint io_priority, GCancellable *cancellable,
+ GAsyncReadyCallback cb, gpointer userdata)
{
+ _purple_log_sets_callback_data *callback_data;
+ GSList *n;
+
+ 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;
+
+ /* Get the log sets from all the loggers. */
+ for ( ; n; n = g_slist_next(n)) {
+ PurpleLogLogger *logger = n->data;
+
+ 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;
+
+ /* 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);
+
+ simple = g_simple_async_result_new(NULL, log_hash_cb, callback_data,
+ log_get_log_sets_common_async);
+
+ 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);
+}
+
+GHashTable *
+purple_log_get_log_sets_finish(GAsyncResult *res, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ simple = G_SIMPLE_ASYNC_RESULT(res);
+
+ 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));
+}
+
+void
+purple_log_set_free(PurpleLogSet *set)
+{
g_return_if_fail(set != NULL);
g_free(set->name);
+
if (set->normalized_name != set->name)
g_free(set->normalized_name);
g_slice_free(PurpleLogSet, set);
}
-GList *purple_log_get_system_logs(PurpleAccount *account)
+GList *
+purple_log_get_system_logs(PurpleAccount *account)
{
GList *logs = NULL;
GSList *n;
- for (n = loggers; n; n = n->next) {
+
+ 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;
+
if (!logger->list_syslog)
continue;
+
logs = g_list_concat(logger->list_syslog(account), logs);
}
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)
+{
+ _purple_log_logs_callback_data *callback_data;
+ GSimpleAsyncResult *simple;
+ GSList *n;
+
+ g_return_if_fail(account != NULL);
+
+ n = purple_log_logger_get_all();
+
+ 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;
+
+ for ( ; n; n = g_slist_next(n)) {
+ PurpleLogLogger *logger = n->data;
+
+ 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);
+
+ simple = g_simple_async_result_new(NULL,
+ log_system_list_cb,
+ callback_data,
+ purple_log_common_lister_async);
+
+ g_simple_async_result_set_op_res_gpointer(simple, logs, NULL);
+ g_simple_async_result_complete_in_idle(simple);
+
+ 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"));
+
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+ }
+ }
+}
+
+GList *
+purple_log_get_system_logs_finish(GAsyncResult *res, GError **error)
+{
+ return purple_log_common_lister_finish(res, error);
+}
+
/****************************************************************************
* LOG SUBSYSTEM ************************************************************
****************************************************************************/
-void *
-purple_log_get_handle(void)
+void
+purple_log_init(void)
{
- static int handle;
+ void *handle;
- return &handle;
-}
+ if (!g_thread_supported())
+ g_thread_init(NULL);
-void purple_log_init(void)
-{
- void *handle = purple_log_get_handle();
+ handle = purple_log_get_handle();
purple_prefs_add_none("/purple/logging");
purple_prefs_add_bool("/purple/logging/log_ims", TRUE);
@@ -652,118 +1659,176 @@ void purple_log_init(void)
purple_prefs_add_string("/purple/logging/format", "html");
- html_logger = purple_log_logger_new("html", _("HTML"), 11,
- 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);
+ 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);
- txt_logger = purple_log_logger_new("txt", _("Plain text"), 11,
- 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);
+ 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(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);
+ 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
- purple_marshal_POINTER__POINTER_INT_BOOLEAN,
+ purple_marshal_POINTER__POINTER_INT_BOOLEAN,
#elif SIZEOF_TIME_T == 8
- purple_marshal_POINTER__POINTER_INT64_BOOLEAN,
+ purple_marshal_POINTER__POINTER_INT64_BOOLEAN,
#else
#error Unknown size of time_t
#endif
- purple_value_new(PURPLE_TYPE_STRING), 3,
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_LOG),
+ purple_value_new(PURPLE_TYPE_STRING), 3,
+ purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_LOG),
#if SIZEOF_TIME_T == 4
- purple_value_new(PURPLE_TYPE_INT),
+ purple_value_new(PURPLE_TYPE_INT),
#elif SIZEOF_TIME_T == 8
- purple_value_new(PURPLE_TYPE_INT64),
+ purple_value_new(PURPLE_TYPE_INT64),
#else
# error Unknown size of time_t
#endif
- purple_value_new(PURPLE_TYPE_BOOLEAN));
+ purple_value_new(PURPLE_TYPE_BOOLEAN));
- purple_prefs_connect_callback(NULL, "/purple/logging/format",
- logger_pref_cb, NULL);
+ purple_prefs_connect_callback(NULL, "/purple/logging/format", logger_pref_cb, NULL);
purple_prefs_trigger_callback("/purple/logging/format");
- logsize_users = g_hash_table_new_full((GHashFunc)_purple_logsize_user_hash,
- (GEqualFunc)_purple_logsize_user_equal,
- (GDestroyNotify)_purple_logsize_user_free_key, NULL);
- logsize_users_decayed = g_hash_table_new_full((GHashFunc)_purple_logsize_user_hash,
- (GEqualFunc)_purple_logsize_user_equal,
- (GDestroyNotify)_purple_logsize_user_free_key, NULL);
+ G_LOCK(logsize_users);
+ logsize_users = g_hash_table_new_full((GHashFunc) _purple_logsize_user_hash,
+ (GEqualFunc) _purple_logsize_user_equal,
+ (GDestroyNotify) _purple_logsize_user_free_key, NULL);
+ G_UNLOCK(logsize_users);
+
+ G_LOCK(logsize_users_decayed);
+ logsize_users_decayed = g_hash_table_new_full((GHashFunc) _purple_logsize_user_hash,
+ (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)
+{
+ static gint handle;
+
+ return &handle;
+}
+
void
purple_log_uninit(void)
{
purple_signals_unregister_by_instance(purple_log_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);
+ 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(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);
+ G_UNLOCK(logsize_users);
+
+ G_LOCK(logsize_users_decayed);
g_hash_table_destroy(logsize_users_decayed);
+ G_UNLOCK(logsize_users_decayed);
+
+ G_LOCK(log_ref_table);
+ g_hash_table_destroy(log_ref_table);
+ G_UNLOCK(log_ref_table);
}
/****************************************************************************
* LOGGERS ******************************************************************
****************************************************************************/
-static char *log_get_timestamp(PurpleLog *log, time_t when)
+static gchar *
+log_get_timestamp(PurpleLog *log, time_t when)
{
gboolean show_date;
- char *date;
+ gchar *date;
struct tm tm;
- show_date = (log->type == PURPLE_LOG_SYSTEM) || (time(NULL) > when + 20*60);
+ show_date = log->type == PURPLE_LOG_SYSTEM || time(NULL) > when + 20 * 60;
- date = purple_signal_emit_return_1(purple_log_get_handle(),
- "log-timestamp",
- log, when, show_date);
+ date = purple_signal_emit_return_1(purple_log_get_handle(), "log-timestamp",
+ log, when, show_date);
+
if (date != NULL)
return date;
tm = *(localtime(&when));
+
if (show_date)
return g_strdup(purple_date_format_long(&tm));
else
@@ -772,20 +1837,16 @@ static char *log_get_timestamp(PurpleLog
/* 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 char *
-convert_image_tags(const PurpleLog *log, const char *msg)
+static gchar *
+convert_image_tags(const PurpleLog *log, const gchar *msg)
{
- const char *tmp;
- const char *start;
- const char *end;
+ GString *newmsg = NULL;
GData *attributes;
- GString *newmsg = NULL;
+ const gchar *tmp = msg, *start, *end;
- tmp = msg;
-
while (purple_markup_find_tag("img", tmp, &start, &end, &attributes)) {
- int imgid = 0;
- char *idstr = NULL;
+ gchar *idstr = NULL;
+ gint imgid = 0;
if (newmsg == NULL)
newmsg = g_string_new("");
@@ -797,56 +1858,45 @@ convert_image_tags(const PurpleLog *log,
if ((idstr = g_datalist_get_data(&attributes, "id")) != NULL)
imgid = atoi(idstr);
- if (imgid != 0)
- {
- FILE *image_file;
- char *dir;
- PurpleStoredImage *image;
+ if (imgid != 0) {
+ PurpleStoredImage *image = purple_imgstore_find_by_id(imgid);
+ gchar *new_filename = NULL, *path = NULL, *dir;
gconstpointer image_data;
- char *new_filename = NULL;
- char *path = NULL;
size_t image_byte_count;
- image = purple_imgstore_find_by_id(imgid);
- if (image == NULL)
- {
+ if (image == NULL) {
/* This should never happen. */
/* This *does* happen for failed Direct-IMs -DAA */
g_string_free(newmsg, TRUE);
- g_return_val_if_reached((char *)msg);
+ g_return_val_if_reached((gchar *) msg);
}
- image_data = purple_imgstore_get_data(image);
+ 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);
- new_filename = purple_util_get_image_filename(image_data, image_byte_count);
+ dir = purple_log_get_log_dir(log->type, log->name, log->account);
+ new_filename = purple_util_get_image_filename(image_data, image_byte_count);
path = g_build_filename(dir, new_filename, NULL);
/* Only save unique files. */
- if (!g_file_test(path, G_FILE_TEST_EXISTS))
- {
- if ((image_file = g_fopen(path, "wb")) != NULL)
- {
- if (!fwrite(image_data, image_byte_count, 1, image_file))
- {
+ if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
+ FILE *image_file = g_fopen(path, "wb");
+
+ if (image_file != NULL) {
+ if (!fwrite(image_data, image_byte_count, 1, image_file)) {
purple_debug_error("log", "Error writing %s: %s\n",
- path, g_strerror(errno));
+ path, g_strerror(errno));
fclose(image_file);
/* Attempt to not leave half-written files around. */
unlink(path);
- }
- else
- {
+ } else {
purple_debug_info("log", "Wrote image file: %s\n", path);
fclose(image_file);
}
- }
- else
- {
+ } else {
purple_debug_error("log", "Unable to create file %s: %s\n",
- path, g_strerror(errno));
+ path, g_strerror(errno));
}
}
@@ -860,10 +1910,9 @@ convert_image_tags(const PurpleLog *log,
tmp = end + 1;
}
- if (newmsg == NULL)
- {
+ if (newmsg == NULL) {
/* No images were found to change. */
- return (char *)msg;
+ return (gchar *) msg;
}
/* Append any remaining message data */
@@ -872,21 +1921,20 @@ convert_image_tags(const PurpleLog *log,
return g_string_free(newmsg, FALSE);
}
-void purple_log_common_writer(PurpleLog *log, const char *ext)
+void
+purple_log_common_writer(PurpleLog *log, const gchar *ext)
{
- PurpleLogCommonLoggerData *data = log->logger_data;
+ g_return_if_fail(log != NULL);
- if (data == NULL)
- {
- /* This log is new */
- char *dir;
+ if (log->logger_data == NULL) {
+ PurpleLogCommonLoggerData *data = log->logger_data;
struct tm *tm;
- const char *tz;
- const char *date;
- char *filename;
- char *path;
+ const gchar *tz, *date;
+ gchar *dir, *filename, *path;
+ /* This log is new */
dir = purple_log_get_log_dir(log->type, log->name, log->account);
+
if (dir == NULL)
return;
@@ -896,7 +1944,7 @@ void purple_log_common_writer(PurpleLog
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 ? ext : "");
+ filename = g_strdup_printf("%s%s%s", date, tz, ext != NULL ? ext : "");
path = g_build_filename(dir, filename, NULL);
g_free(dir);
@@ -905,53 +1953,253 @@ void purple_log_common_writer(PurpleLog
log->logger_data = data = g_slice_new0(PurpleLogCommonLoggerData);
data->file = g_fopen(path, "a");
- if (data->file == NULL)
- {
- purple_debug(PURPLE_DEBUG_ERROR, "log",
- "Could not create log file %s\n", path);
+ if (data->file == NULL) {
+ purple_debug_error("log", "Could not create log file %s\n", path);
+
if (log->conv != NULL)
purple_conversation_write(log->conv, NULL, _("Logging of this conversation failed."),
- PURPLE_MESSAGE_ERROR, time(NULL));
+ PURPLE_MESSAGE_ERROR, time(NULL));
g_free(path);
+
return;
}
- g_free(path);
+
+ data->path = path;
}
}
-GList *purple_log_common_lister(PurpleLogType type, const char *name, PurpleAccount *account, const char *ext, PurpleLogLogger *logger)
+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;
+
+ if (data == NULL) {
+ struct tm *tm;
+ const gchar *tz, *date;
+ gchar *dir, *filename, *path, *ext = callback_data->ext;
+
+ /* This log is new */
+ dir = purple_log_get_log_dir(log->type, log->name, log->account);
+
+ if (dir == NULL) {
+ g_simple_async_result_set_error(simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Unable to get log directory"));
+
+ return;
+ }
+
+ if (purple_build_dir(dir, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
+ g_simple_async_result_set_error(simple, G_FILE_ERROR, g_file_error_from_errno(errno),
+ "%s", g_strerror(errno));
+
+ 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);
+
+ filename = g_strdup_printf("%s%s%s", date, tz, ext != NULL ? ext : "");
+ path = g_build_filename(dir, filename, NULL);
+
+ g_free(dir);
+ g_free(filename);
+
+ log->logger_data = data = g_slice_new0(PurpleLogCommonLoggerData);
+ data->file = g_fopen(path, "a");
+
+ if (data->file == NULL) {
+ 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,
+ _("Logging of this conversation failed."),
+ PURPLE_MESSAGE_ERROR, time(NULL));
+
+ g_free(path);
+
+ return;
+ }
+
+ data->path = path;
+ }
+
+ g_simple_async_result_set_op_res_gboolean(simple, TRUE);
+}
+
+void
+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;
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail(log != NULL);
+
+ callback_data = g_new0(_purple_logger_writer_callback_data, 1);
+ callback_data->log = log;
+ callback_data->ext = g_strdup(ext);
+
+ simple = g_simple_async_result_new(NULL, 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_run_in_thread(simple, writer_thread, io_priority, cancellable);
+
+ g_object_unref(simple);
+}
+
+gboolean
+purple_log_common_writer_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ 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_writer_async, FALSE);
+
+ return g_simple_async_result_get_op_res_gboolean(simple);
+}
+
+GList *
+purple_log_common_lister(PurpleLogType type, const gchar *name, PurpleAccount *account,
+ const gchar *ext, PurpleLogLogger *logger)
+{
+ PurpleLog *log;
GDir *dir;
GList *list = NULL;
- const char *filename;
- char *path;
+ const gchar *filename;
+ gchar *path;
+ PurpleLogCommonLoggerData *data;
+ struct tm tm;
- if(!account)
- return NULL;
+ 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);
path = purple_log_get_log_dir(type, name, account);
+
if (path == NULL)
return NULL;
- if (!(dir = g_dir_open(path, 0, NULL)))
- {
+ dir = g_dir_open(path, 0, NULL);
+
+ if (dir == NULL) {
g_free(path);
+
return NULL;
}
- while ((filename = g_dir_read_name(dir)))
- {
+ while ((filename = g_dir_read_name(dir)) != NULL) {
if (purple_str_has_suffix(filename, ext) &&
- strlen(filename) >= (17 + strlen(ext)))
- {
- PurpleLog *log;
- PurpleLogCommonLoggerData *data;
- struct tm tm;
+ strlen(filename) >= (17 + strlen(ext))) {
#if defined (HAVE_TM_GMTOFF) && defined (HAVE_STRUCT_TM_TM_ZONE)
long tz_off;
- const char *rest, *end;
+ const gchar *rest, *end;
+ time_t stamp;
+
+ 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);
+
+ return list;
+}
+
+static void
+common_lister_thread(GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable)
+{
+ _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;
+
+ 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,
+ _("Unable to get log directory"));
+
+ 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
@@ -959,13 +2207,10 @@ GList *purple_log_common_lister(PurpleLo
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)
- {
+ if (stamp == 0 || rest == NULL || (end = strchr(rest, '.')) == NULL || strchr(rest, ' ') != NULL) {
log = purple_log_new(type, name, account, NULL, stamp, NULL);
- }
- else
- {
- char *tmp = g_strndup(rest, end - rest);
+ } 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);
@@ -983,58 +2228,283 @@ GList *purple_log_common_lister(PurpleLo
list = g_list_prepend(list, log);
}
}
+
g_dir_close(dir);
g_free(path);
- return list;
+
+ g_simple_async_result_set_op_res_gpointer(simple, list, NULL);
}
-int purple_log_common_total_sizer(PurpleLogType type, const char *name, PurpleAccount *account, const char *ext)
+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)
{
+ _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(ext != NULL);
+ g_return_if_fail(logger != NULL);
+
+ callback_data = g_new0(_purple_logger_lister_callback_data, 1);
+ callback_data->type = type;
+ callback_data->name = g_strdup(name);
+ callback_data->account = account;
+ callback_data->ext = g_strdup(ext);
+ callback_data->logger = logger;
+
+ simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_common_lister_async);
+
+ g_simple_async_result_set_op_res_gpointer(simple, callback_data,
+ purple_logger_lister_callback_data_free);
+ g_simple_async_result_run_in_thread(simple, common_lister_thread, io_priority,
+ cancellable);
+
+ g_object_unref(simple);
+}
+
+GList *
+purple_log_common_lister_finish(GAsyncResult *res, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ simple = G_SIMPLE_ASYNC_RESULT(res);
+
+ 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);
+}
+
+// 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)
+{
+ _purple_logsize_user *lu;
GDir *dir;
- int size = 0;
- const char *filename;
- char *path;
+ gint size;
+ const gchar *filename;
+ gchar *tmp, *path;
+ struct stat st;
+ gpointer ptrsize;
- if(!account)
- return 0;
+ 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);
+
if (path == NULL)
return 0;
- if (!(dir = g_dir_open(path, 0, NULL)))
- {
+ if (!(dir = g_dir_open(path, 0, NULL))){
g_free(path);
return 0;
}
- while ((filename = g_dir_read_name(dir)))
- {
+ size = 0;
+
+ while ((filename = g_dir_read_name(dir))) {
if (purple_str_has_suffix(filename, ext) &&
- strlen(filename) >= (17 + strlen(ext)))
- {
- char *tmp = g_build_filename(path, filename, NULL);
- struct stat st;
- if (g_stat(tmp, &st))
- {
+ 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;
}
-int purple_log_common_sizer(PurpleLog *log)
+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;
- PurpleLogCommonLoggerData *data = log->logger_data;
+ 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,
+ _("Unable to get log directory"));
+
+ return;
+ }
+
+ dir = g_dir_open(path, 0, &err);
+
+ if (dir == NULL){
+ /* 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?
+
+ if (!g_hash_table_lookup_extended(logsize_users, lu, NULL, NULL))
+ g_hash_table_replace(logsize_users, lu, GINT_TO_POINTER(size));
+
+ 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;
+ }
+
+ 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;
+ }
+
+ g_clear_error(&err);
+
+ 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_LOCK(logsize_users);
+ lu = g_new(_purple_logsize_user, 1);
+ lu->name = g_strdup(purple_normalize(account, name));
+ lu->account = account; //g_object_ref?
+
+ total = size;
+
+ if (g_hash_table_lookup_extended(logsize_users, lu, NULL, &ptrsize))
+ total += GPOINTER_TO_INT(ptrsize);
+
+ g_hash_table_replace(logsize_users, lu, GINT_TO_POINTER(total));
+ G_UNLOCK(logsize_users);
+
+ g_dir_close(dir);
+ g_free(path);
+
+ g_simple_async_result_set_op_res_gssize(simple, size);
+}
+
+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)
+{
+ _purple_logger_total_size_callback_data *callback_data;
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail(name != NULL);
+ g_return_if_fail(account != NULL);
+ g_return_if_fail(ext != NULL);
+
+ callback_data = g_new0(_purple_logger_total_size_callback_data, 1);
+ callback_data->type = type;
+ callback_data->name = g_strdup(name);
+ callback_data->account = account;
+ callback_data->ext = g_strdup(ext);
+
+ simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_common_total_sizer_async);
+
+ 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_object_unref(simple);
+}
+
+gssize
+purple_log_common_total_sizer_finish(GAsyncResult *res, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ 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_total_sizer_async, -1);
+
+ return g_simple_async_result_get_op_res_gssize(simple);
+}
+
+gint
+purple_log_common_sizer(PurpleLog *log)
+{
+ struct stat st;
+ PurpleLogCommonLoggerData *data;
+
+ g_return_val_if_fail(log != NULL, 0);
+
+ data = log->logger_data;
+
g_return_val_if_fail(data != NULL, 0);
if (!data->path || g_stat(data->path, &st))
@@ -1043,28 +2513,100 @@ int purple_log_common_sizer(PurpleLog *l
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;
+
+ 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;
+ }
+
+ 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);
+}
+
+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);
+
+ callback_data = g_new0(_purple_logger_sizer_callback_data, 1);
+ callback_data->log = log;
+
+ simple = g_simple_async_result_new(NULL, cb, userdata, purple_log_common_sizer_async);
+
+ 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);
+}
+
+gssize
+purple_log_common_sizer_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ 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 void log_get_log_sets_common(GHashTable *sets)
+static gboolean
+log_get_log_sets_common(GHashTable *sets, GMutex *mutex, PurpleLogSetCallback set_cb, GError **error)
{
- gchar *log_path = g_build_filename(purple_user_dir(), "logs", NULL);
- GDir *log_dir = g_dir_open(log_path, 0, NULL);
- const gchar *protocol;
+ 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;
+ log_path = g_build_filename(purple_user_dir(), "logs", NULL);
+ log_dir = g_dir_open(log_path, 0, &err);
+
if (log_dir == NULL) {
+ if (error != NULL)
+ *error = err;
+
g_free(log_path);
- return;
+ return FALSE;
}
+ g_clear_error(&err);
+
while ((protocol = g_dir_read_name(log_dir)) != NULL) {
- gchar *protocol_path = g_build_filename(log_path, protocol, NULL);
- GDir *protocol_dir;
- const gchar *username;
- gchar *protocol_unescaped;
- GList *account_iter;
- GList *accounts = NULL;
+ protocol_path = g_build_filename(log_path, protocol, NULL);
+ protocol_dir = g_dir_open(protocol_path, 0, NULL);
- if ((protocol_dir = g_dir_open(protocol_path, 0, NULL)) == NULL) {
+ if (protocol_dir == NULL) {
g_free(protocol_path);
continue;
}
@@ -1072,28 +2614,27 @@ static void log_get_log_sets_common(GHas
/* 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 = account_iter->next) {
- PurplePlugin *prpl;
- PurplePluginProtocolInfo *prpl_info;
+ for (account_iter = purple_accounts_get_all(); account_iter != NULL; account_iter = g_list_next(account_iter)) {
+ prpl = purple_find_prpl(purple_account_get_protocol_id((PurpleAccount *) account_iter->data));
- prpl = purple_find_prpl(purple_account_get_protocol_id((PurpleAccount *)account_iter->data));
if (!prpl)
continue;
+
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+ prpl_protocol = prpl_info->list_icon((PurpleAccount *) account_iter->data, NULL);
- if (purple_strequal(protocol_unescaped, prpl_info->list_icon((PurpleAccount *)account_iter->data, NULL)))
+ if (purple_strequal(protocol_unescaped, prpl_protocol))
accounts = g_list_prepend(accounts, account_iter->data);
}
+
g_free(protocol_unescaped);
while ((username = g_dir_read_name(protocol_dir)) != NULL) {
- gchar *username_path = g_build_filename(protocol_path, username, NULL);
- GDir *username_dir;
- const gchar *username_unescaped;
- PurpleAccount *account = NULL;
- gchar *name;
+ username_path = g_build_filename(protocol_path, username, NULL);
+ account = NULL;
if ((username_dir = g_dir_open(username_path, 0, NULL)) == NULL) {
g_free(username_path);
@@ -1102,20 +2643,18 @@ static void log_get_log_sets_common(GHas
/* Find the account for username in the list of accounts for protocol. */
username_unescaped = purple_unescape_filename(username);
- for (account_iter = g_list_first(accounts) ; account_iter != NULL ; account_iter = account_iter->next) {
- if (purple_strequal(((PurpleAccount *)account_iter->data)->username, username_unescaped)) {
+
+ for (account_iter = g_list_first(accounts); account_iter != NULL; account_iter = g_list_next(account_iter)) {
+ if (purple_strequal(((PurpleAccount *) account_iter->data)->username, username_unescaped)) {
account = account_iter->data;
break;
}
}
/* Don't worry about the cast, name will point to dynamically allocated memory shortly. */
- while ((name = (gchar *)g_dir_read_name(username_dir)) != NULL) {
- size_t len;
- PurpleLogSet *set;
-
+ while ((name = (gchar *) g_dir_read_name(username_dir)) != NULL) {
/* IMPORTANT: Always initialize all members of PurpleLogSet */
- set = g_slice_new(PurpleLogSet);
+ PurpleLogSet *set = g_slice_new(PurpleLogSet);
/* Unescape the filename. */
name = g_strdup(purple_unescape_filename(name));
@@ -1131,14 +2670,17 @@ static void log_get_log_sets_common(GHas
/* Check for .chat or .system at the end of the name to determine the type. */
if (len >= 7) {
- gchar *tmp = &name[len - 7];
+ tmp = &name[len - 7];
+
if (purple_strequal(tmp, ".system")) {
set->type = PURPLE_LOG_SYSTEM;
*tmp = '\0';
}
}
+
if (len > 5) {
- gchar *tmp = &name[len - 5];
+ tmp = &name[len - 5];
+
if (purple_strequal(tmp, ".chat")) {
set->type = PURPLE_LOG_CHAT;
*tmp = '\0';
@@ -1151,86 +2693,194 @@ static void log_get_log_sets_common(GHas
else
set->buddy = FALSE;
- log_add_log_set_to_hash(sets, set);
+ if (mutex != NULL) {
+ g_mutex_lock(mutex);
+ set_cb(sets, set);
+ g_mutex_unlock(mutex);
+ } else
+ set_cb(sets, set);
}
+
g_free(username_path);
g_dir_close(username_dir);
}
+
g_free(protocol_path);
g_dir_close(protocol_dir);
}
+
g_free(log_path);
g_dir_close(log_dir);
+
+ return TRUE;
}
-gboolean purple_log_common_deleter(PurpleLog *log)
+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;
+
+ if (!log_get_log_sets_common(sets, callback_data->mutex,
+ callback_data->set_cb, &err))
+ g_simple_async_result_set_from_error(simple, err);
+ else
+ g_simple_async_result_set_op_res_gpointer(simple, sets,
+ (GDestroyNotify) g_hash_table_unref);
+
+ g_clear_error(&err);
+}
+
+static void
+log_get_log_sets_common_async(GHashTable *sets, GMutex *mutex, PurpleLogSetCallback set_cb,
+ 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_object_unref(simple);
+}
+
+gboolean
+purple_log_common_deleter(PurpleLog *log)
+{
PurpleLogCommonLoggerData *data;
- int ret;
+ GFile *file;
+ GError *err;
+ gboolean result;
g_return_val_if_fail(log != NULL, FALSE);
data = log->logger_data;
- if (data == NULL)
+
+ if (data == NULL || data->path == NULL)
return FALSE;
- if (data->path == NULL)
- return FALSE;
+ err = NULL;
+ file = g_file_new_for_path(data->path);
+ result = g_file_delete(file, NULL, &err);
- ret = g_unlink(data->path);
- if (ret == 0)
- return TRUE;
- else if (ret == -1)
- {
- purple_debug_error("log", "Failed to delete: %s - %s\n", data->path, g_strerror(errno));
+ 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)
+{
+ _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;
+
+ 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);
else
- {
- /* I'm not sure that g_unlink() will ever return
- * something other than 0 or -1. -- rlaager */
- purple_debug_error("log", "Failed to delete: %s\n", data->path);
- }
+ g_simple_async_result_set_op_res_gboolean(simple, TRUE);
- return FALSE;
+ g_object_unref(file);
+ g_clear_error(&err);
}
-gboolean purple_log_common_is_deletable(PurpleLog *log)
+void
+purple_log_common_deleter_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);
+
+ callback_data = g_new0(_purple_logger_deleter_callback_data, 1);
+ callback_data->log = log;
+
+ 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_object_unref(simple);
+}
+
+gboolean
+purple_log_common_deleter_finish(PurpleLog *log, GAsyncResult *res, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ 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)
+{
PurpleLogCommonLoggerData *data;
-#ifndef _WIN32
- gchar *dirname;
-#endif
g_return_val_if_fail(log != NULL, FALSE);
data = log->logger_data;
- if (data == NULL)
- return FALSE;
- if (data->path == NULL)
+ if (data == NULL || data->path == NULL)
return FALSE;
-#ifndef _WIN32
- dirname = g_path_get_dirname(data->path);
- if (g_access(dirname, W_OK) == 0)
+#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 TRUE;
+
+ return FALSE;
}
- purple_debug_info("log", "access(%s) failed: %s\n", dirname, g_strerror(errno));
- g_free(dirname);
#else
/* Unless and until someone writes equivalent win32 code,
* we'll assume the file is deletable. */
return TRUE;
#endif
-
- return FALSE;
}
-static char *process_txt_log(char *txt, char *to_free)
+static gchar *
+process_txt_log(gchar *txt, gchar *to_free)
{
- char *tmp;
+ gchar *tmp;
/* The to_free argument allows us to save a
* g_strdup() in some cases. */
@@ -1259,18 +2909,17 @@ static char *process_txt_log(char *txt,
** XML LOGGER **
****************/
-static const char *str_from_msg_type (PurpleMessageFlags type)
+static const gchar *
+str_from_msg_type (PurpleMessageFlags type)
{
-
- return "";
-
+ return "";
}
-static void xml_logger_write(PurpleLog *log,
- PurpleMessageFlags type,
- const char *from, time_t time, const char *message)
+static void
+xml_logger_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
+ const gchar *message)
{
- char *xhtml = NULL;
+ gchar *xhtml = NULL;
if (!log->logger_data) {
/* This log is new. We could use the loggers 'new' function, but
@@ -1278,11 +2927,11 @@ static void xml_logger_write(PurpleLog *
* that you open a convo with someone, but don't say anything.
*/
struct tm *tm;
- const char *tz;
- const char *date;
- char *dir = purple_log_get_log_dir(log->type, log->name, log->account);
- char *name;
- char *filename;
+ 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;
@@ -1338,7 +2987,8 @@ static void xml_logger_write(PurpleLog *
g_free(xhtml);
}
- static void xml_logger_finalize(PurpleLog *log)
+ static void
+ xml_logger_finalize(PurpleLog *log)
{
if (log->logger_data) {
fprintf(log->logger_data, "</conversation>\n");
@@ -1347,7 +2997,8 @@ static void xml_logger_write(PurpleLog *
}
}
-static GList *xml_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account)
+static GList *
+xml_logger_list(PurpleLogType type, const gchar *sn, PurpleAccount *account)
{
return purple_log_common_lister(type, sn, account, ".xml", &xml_logger);
}
@@ -1368,22 +3019,22 @@ static PurpleLogLogger xml_logger = {
** HTML LOGGER *************
****************************/
-static gsize html_logger_write(PurpleLog *log, PurpleMessageFlags type,
- const char *from, time_t time, const char *message)
+static gsize
+html_logger_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
+ const gchar *message)
{
- char *msg_fixed;
- char *image_corrected_msg;
- char *date;
- char *header;
- char *escaped_from;
- PurplePlugin *plugin = purple_find_prpl(purple_account_get_protocol_id(log->account));
- PurpleLogCommonLoggerData *data = log->logger_data;
- gsize written = 0;
+ 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) {
- const char *prpl =
- PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL);
- const char *date;
+ prpl = PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL);
purple_log_common_writer(log, ".html");
data = log->logger_data;
@@ -1392,17 +3043,18 @@ static gsize html_logger_write(PurpleLog
if(!data->file)
return 0;
- date = purple_date_format_full(localtime(&log->time));
+ 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);
+ 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, purple_account_get_username(log->account), prpl);
+ log->name, date_full, purple_account_get_username(log->account), prpl);
written += fprintf(data->file, "%s", header);
written += fprintf(data->file, "</title></head><body>");
@@ -1437,7 +3089,7 @@ static gsize html_logger_write(PurpleLog
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);
+ 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 <AUTO-REPLY>:</b></font> %s<br/>\n"), date, escaped_from, msg_fixed);
@@ -1446,23 +3098,24 @@ static gsize html_logger_write(PurpleLog
} 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ date, escaped_from, msg_fixed);
}
}
+
g_free(date);
g_free(msg_fixed);
g_free(escaped_from);
@@ -1471,39 +3124,285 @@ static gsize html_logger_write(PurpleLog
return written;
}
-static void html_logger_finalize(PurpleLog *log)
+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;
- if (data) {
- if(data->file) {
+ 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;
+
+ 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;
+
+ /* 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;
+ 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 <AUTO-REPLY>:</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 <AUTO-REPLY>:</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_free(data->path);
g_slice_free(PurpleLogCommonLoggerData, data);
}
}
-static GList *html_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account)
+static GList *
+html_logger_list(PurpleLogType type, const gchar *sn, PurpleAccount *account)
{
- return purple_log_common_lister(type, sn, account, ".html", html_logger);
+ GList *list;
+
+ G_LOCK(html_logger);
+ list = purple_log_common_lister(type, sn, account, ".html", html_logger);
+ G_UNLOCK(html_logger);
+
+ return list;
}
-static GList *html_logger_list_syslog(PurpleAccount *account)
+static void
+html_logger_list_async(PurpleLogType type, const gchar *sn, PurpleAccount *account,
+ gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
{
- return purple_log_common_lister(PURPLE_LOG_SYSTEM, ".system", account, ".html", html_logger);
+ 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 char *html_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
+static GList *
+html_logger_list_syslog(PurpleAccount *account)
{
- char *read;
- PurpleLogCommonLoggerData *data = log->logger_data;
- *flags = PURPLE_LOG_READ_NO_NEWLINE;
+ 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(_("<font color=\"red\"><b>Unable to find log path!</b></font>"));
+ 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)) {
- char *minus_header = strchr(read, '\n');
+ minus_header = strchr(read, '\n');
if (!minus_header)
return read;
@@ -1513,37 +3412,126 @@ static char *html_logger_read(PurpleLog
return minus_header;
}
- return g_strdup_printf(_("<font color=\"red\"><b>Could not read file: %s</b></font>"), data->path);
+
+ return g_strdup_printf("<font color=\"red\"><b>%s: %s</b></font>",
+ _("Could not read file"), data->path);
}
-static int html_logger_total_size(PurpleLogType type, const char *name, PurpleAccount *account)
+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 char *from, time_t time, const char *message)
+static gsize
+txt_logger_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from, time_t time,
+ const gchar *message)
{
- char *date;
- PurplePlugin *plugin = purple_find_prpl(purple_account_get_protocol_id(log->account));
- PurpleLogCommonLoggerData *data = log->logger_data;
- char *stripped = NULL;
+ PurplePlugin *plugin;
+ PurpleLogCommonLoggerData *data;
+ const gchar *prpl;
+ gchar *stripped, *date;
+ gsize written;
- gsize written = 0;
+ 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.
*/
- const char *prpl =
- PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL);
+ prpl = PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL);
purple_log_common_writer(log, ".txt");
data = log->logger_data;
@@ -1576,14 +3564,12 @@ static gsize txt_logger_write(PurpleLog
type & PURPLE_MESSAGE_RECV) {
if (type & PURPLE_MESSAGE_AUTO_RESP) {
written += fprintf(data->file, _("(%s) %s <AUTO-REPLY>: %s\n"), date,
- from, stripped);
+ from, stripped);
} else {
- if(purple_message_meify(stripped, -1))
- written += fprintf(data->file, "(%s) ***%s %s\n", date, from,
- stripped);
+ 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);
+ written += fprintf(data->file, "(%s) %s: %s\n", date, from, stripped);
}
} else if (type & PURPLE_MESSAGE_SYSTEM ||
type & PURPLE_MESSAGE_ERROR ||
@@ -1592,13 +3578,15 @@ static gsize txt_logger_write(PurpleLog
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);
+ from ? ":" : "", stripped);
}
+
g_free(date);
g_free(stripped);
fflush(data->file);
@@ -1606,35 +3594,244 @@ static gsize txt_logger_write(PurpleLog
return written;
}
-static void txt_logger_finalize(PurpleLog *log)
+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;
- if (data) {
- if(data->file)
+ 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 char *sn, PurpleAccount *account)
+static GList *
+txt_logger_list(PurpleLogType type, const gchar *sn, PurpleAccount *account)
{
- return purple_log_common_lister(type, sn, account, ".txt", txt_logger);
+ GList *list;
+
+ G_LOCK(txt_logger);
+ list = purple_log_common_lister(type, sn, account, ".txt", txt_logger);
+ G_UNLOCK(txt_logger);
+
+ return list;
}
-static GList *txt_logger_list_syslog(PurpleAccount *account)
+static void
+txt_logger_list_async(PurpleLogType type, const gchar *sn, PurpleAccount *account,
+ gint io_priority, GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata)
{
- return purple_log_common_lister(PURPLE_LOG_SYSTEM, ".system", account, ".txt", txt_logger);
+ 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 char *txt_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
+static GList *
+txt_logger_list_syslog(PurpleAccount *account)
{
- char *read, *minus_header;
- PurpleLogCommonLoggerData *data = log->logger_data;
- *flags = 0;
+ 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(_("<font color=\"red\"><b>Unable to find log path!</b></font>"));
+ 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');
@@ -1643,15 +3840,99 @@ static char *txt_logger_read(PurpleLog *
else
return process_txt_log(read, NULL);
}
- return g_strdup_printf(_("<font color=\"red\"><b>Could not read file: %s</b></font>"), data->path);
+
+ return g_strdup_printf("<font color=\"red\"><b>%s: %s</b></font>",
+ _("Could not read file"), data->path);
}
-static int txt_logger_total_size(PurpleLogType type, const char *name, PurpleAccount *account)
+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 ***
****************/
@@ -1660,80 +3941,77 @@ static int txt_logger_total_size(PurpleL
* old logs in the log viewer transparently.
*/
-struct old_logger_data {
+typedef struct {
PurpleStringref *pathref;
- int offset;
- int length;
-};
+ gint offset;
+ gint length;
+} old_logger_data;
-static GList *old_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account)
+static GList *
+old_logger_list(PurpleLogType type, const gchar *sn, PurpleAccount *account)
{
- char *logfile = g_strdup_printf("%s.log", purple_normalize(account, sn));
- char *pathstr = g_build_filename(purple_user_dir(), "logs", logfile, NULL);
- PurpleStringref *pathref = purple_stringref_new(pathstr);
+ old_logger_data *data;
+ PurpleStringref *pathref;
+ PurpleLog *log;
+ GList *list;
+ FILE *index, *file;
+ time_t log_last_modified, lasttime;
struct stat st;
- time_t log_last_modified;
- FILE *index;
- FILE *file;
- int index_fd;
- char *index_tmp;
- char buf[BUF_LONG];
struct tm tm;
- char month[4];
- struct old_logger_data *data = NULL;
- int logfound = 0;
- int lastoff = 0;
- int newlen;
- time_t lasttime = 0;
+ gchar *logfile, *pathstr, *index_tmp, buf[BUF_LONG], month[4], convostart[32], *temp;
+ gint index_fd, logfound, lastoff, newlen, length, offset;
+ gulong idx_time;
- PurpleLog *log = NULL;
- GList *list = NULL;
+ 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);
- g_free(logfile);
+ log = NULL;
+ list = NULL;
+ data = NULL;
- if (g_stat(purple_stringref_value(pathref), &st))
- {
+ logfound = 0;
+ lastoff = 0;
+ lasttime = 0;
+
+ if (g_stat(purple_stringref_value(pathref), &st)) {
purple_stringref_unref(pathref);
g_free(pathstr);
+
return NULL;
- }
- else
+ } 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)
- {
+ 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
- {
+ } 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));
+ pathstr, g_strerror(errno));
/* Fall through so that we'll parse the log file. */
- }
- else
- {
+ } else {
purple_debug_info("log", "Using index: %s\n", pathstr);
g_free(pathstr);
- while (fgets(buf, BUF_LONG, index))
- {
- unsigned long idx_time;
- if (sscanf(buf, "%d\t%d\t%lu", &lastoff, &newlen, &idx_time) == 3)
- {
+
+ 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;
- log->time = (time_t)idx_time;
+ G_UNLOCK(old_logger);
- /* IMPORTANT: Always set all members of struct old_logger_data */
- data = g_slice_new(struct old_logger_data);
+ /* 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;
@@ -1743,6 +4021,7 @@ static GList *old_logger_list(PurpleLogT
list = g_list_prepend(list, log);
}
}
+
fclose(index);
purple_stringref_unref(pathref);
@@ -1753,40 +4032,42 @@ static GList *old_logger_list(PurpleLogT
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_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_strerror(errno));
+
g_free(pathstr);
g_free(index_tmp);
index = NULL;
} else {
- if ((index = fdopen(index_fd, "wb")) == NULL)
- {
+ index = fdopen(index_fd, "wb");
+
+ if (index == NULL) {
purple_debug_error("log", "Failed to fdopen() index temp file: %s\n",
- g_strerror(errno));
+ g_strerror(errno));
close(index_fd);
- if (index_tmp != NULL)
- {
+
+ 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) {
- int length;
- int offset;
- char convostart[32];
- char *temp = strchr(buf, '@');
+ temp = strchr(buf, '@');
if (temp == NULL || strlen(temp) < 2)
continue;
@@ -1804,8 +4085,7 @@ static GList *old_logger_list(PurpleLogT
sizeof("<HR><BR><H3 Align=Center> ---- New Conversation @ ") +
sizeof("----</H3><BR>") - 2;
} else {
- newlen -=
- sizeof("---- New Conversation @ ") + sizeof("----") - 2;
+ newlen -= sizeof("---- New Conversation @ ") + sizeof("----") - 2;
}
if(strchr(buf, '\r'))
@@ -1813,11 +4093,14 @@ static GList *old_logger_list(PurpleLogT
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;
- log->time = lasttime;
+ G_UNLOCK(old_logger);
- /* IMPORTANT: Always set all members of struct old_logger_data */
- data = g_slice_new(struct old_logger_data);
+ /* 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;
@@ -1828,7 +4111,8 @@ static GList *old_logger_list(PurpleLogT
/* 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);
+ fprintf(index, "%d\t%d\t%lu\n", data->offset, data->length,
+ (unsigned long)log->time);
}
}
@@ -1837,8 +4121,9 @@ static GList *old_logger_list(PurpleLogT
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);
+ 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;
@@ -1873,11 +4158,14 @@ static GList *old_logger_list(PurpleLogT
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;
- log->time = lasttime;
+ G_UNLOCK(old_logger);
- /* IMPORTANT: Always set all members of struct old_logger_data */
- data = g_slice_new(struct old_logger_data);
+ /* 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;
@@ -1888,44 +4176,45 @@ static GList *old_logger_list(PurpleLogT
/* 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, (int)log->time);
+ fprintf(index, "%d\t%d\t%d\n", data->offset, data->length, (gint) log->time);
}
}
purple_stringref_unref(pathref);
fclose(file);
- if (index != NULL)
- {
+
+ if (index != NULL) {
fclose(index);
- if (index_tmp == NULL)
- {
+ if (index_tmp == NULL) {
g_free(pathstr);
g_return_val_if_reached(list);
}
- if (g_rename(index_tmp, pathstr))
- {
+ 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));
+ index_tmp, pathstr, g_strerror(errno));
g_unlink(index_tmp);
- }
- else
+ } else
purple_debug_info("log", "Built index: %s\n", pathstr);
g_free(index_tmp);
g_free(pathstr);
}
+
return list;
}
-static int old_logger_total_size(PurpleLogType type, const char *name, PurpleAccount *account)
+static gint
+old_logger_total_size(PurpleLogType type, const gchar *name, PurpleAccount *account)
{
- char *logfile = g_strdup_printf("%s.log", purple_normalize(account, name));
- char *pathstr = g_build_filename(purple_user_dir(), "logs", logfile, NULL);
- int size;
+ 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
@@ -1937,52 +4226,73 @@ static int old_logger_total_size(PurpleL
return size;
}
-static char * old_logger_read (PurpleLog *log, PurpleLogReadFlags *flags)
+static gchar *
+old_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
{
size_t result;
- struct old_logger_data *data = log->logger_data;
- const char *path = purple_stringref_value(data->pathref);
- FILE *file = g_fopen(path, "rb");
- char *read = g_malloc(data->length + 1);
+ 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';
- *flags = 0;
- if (strstr(read, "<BR>"))
- {
- *flags |= PURPLE_LOG_READ_NO_NEWLINE;
+
+ 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 int old_logger_size (PurpleLog *log)
+static gint
+old_logger_size(PurpleLog *log)
{
- struct old_logger_data *data = log->logger_data;
+ old_logger_data *data;
+
+ data = log->logger_data;
+
return data ? data->length : 0;
}
-static void old_logger_get_log_sets(PurpleLogSetCallback cb, GHashTable *sets)
+static void
+old_logger_get_log_sets(PurpleLogSetCallback cb, GHashTable *sets)
{
- char *log_path = g_build_filename(purple_user_dir(), "logs", NULL);
- GDir *log_dir = g_dir_open(log_path, 0, NULL);
- gchar *name;
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) {
- size_t len;
- gchar *ext;
- PurpleLogSet *set;
- gboolean found = FALSE;
+ while ((name = (gchar *) g_dir_read_name(log_dir)) != NULL) {
+ found = FALSE;
/* Unescape the filename. */
name = g_strdup(purple_unescape_filename(name));
@@ -2008,8 +4318,9 @@ static void old_logger_get_log_sets(Purp
/* Chat for .chat at the end of the name to determine the type. */
*ext = '\0';
set->type = PURPLE_LOG_IM;
+
if (len > 9) {
- char *tmp = &name[len - 9];
+ tmp = &name[len - 9];
if (purple_strequal(tmp, ".chat")) {
set->type = PURPLE_LOG_CHAT;
*tmp = '\0';
@@ -2021,23 +4332,20 @@ static void old_logger_get_log_sets(Purp
/* 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))
- {
+ 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))
- {
+ 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))
- {
- PurpleBuddy *buddy = (PurpleBuddy *)bnode;
+ 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);
@@ -2048,20 +4356,425 @@ static void old_logger_get_log_sets(Purp
}
}
- if (!found)
- {
+ if (!found) {
set->account = NULL;
set->buddy = FALSE;
}
cb(sets, set);
}
+
g_dir_close(log_dir);
}
-static void old_logger_finalize(PurpleLog *log)
+static void
+old_logger_finalize(PurpleLog *log)
{
- struct old_logger_data *data = log->logger_data;
+ old_logger_data *data;
+
+ data = log->logger_data;
purple_stringref_unref(data->pathref);
- g_slice_free(struct old_logger_data, data);
+ 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) {
+#ifdef HAVE_STRUCT_TM_TM_ZONE
+ /* XXX: This is so wrong... */
+ g_free((gchar *) log->tm->tm_zone);
+#endif
+ g_slice_free(struct tm, log->tm);
+ }
+
+ PURPLE_DBUS_UNREGISTER_POINTER(log);
+ g_slice_free(PurpleLog, log);
+}
+
+static void
+log_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _purple_log_logs_callback_data *callback_data = userdata;
+ GSimpleAsyncResult *simple;
+ GList *list, *n;
+
+ list = purple_log_get_logs_finish(res, NULL);
+
+ callback_data->logs = g_list_concat(callback_data->logs, list);
+ callback_data->counter--;
+
+ if (callback_data->counter < 1) {
+ /* Let the caller know we're done. */
+ if (callback_data->cb) {
+ simple = g_simple_async_result_new(object, callback_data->cb,
+ callback_data->userdata, purple_log_common_lister_async);
+
+ g_simple_async_result_set_op_res_gpointer(simple, callback_data->logs, NULL);
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+ } else {
+ for (n = callback_data->logs; n != NULL; n = g_list_next(n))
+ purple_log_free(n->data);
+
+ g_list_free(callback_data->logs);
+ }
+
+ g_free(callback_data);
+ }
+}
+
+static void
+log_system_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _purple_log_logs_callback_data *callback_data = userdata;
+ GSimpleAsyncResult *simple;
+ GList *list, *n;
+
+ list = purple_log_get_logs_finish(res, NULL);
+
+ callback_data->logs = g_list_concat(callback_data->logs, list);
+ callback_data->counter--;
+
+ if (callback_data->counter < 1) {
+ /* Let the caller know we're done. */
+ if (callback_data->cb) {
+ simple = g_simple_async_result_new(object, callback_data->cb,
+ callback_data->userdata, purple_log_common_lister_async);
+
+ g_simple_async_result_set_op_res_gpointer(simple, callback_data->logs, NULL);
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+ } else {
+ for (n = callback_data->logs; n != NULL; n = g_list_next(n))
+ purple_log_free(n->data);
+
+ g_list_free(callback_data->logs);
+ }
+
+ g_free(callback_data);
+ }
+}
+
+static void
+log_total_size_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _purple_log_total_size_callback_data *callback_data = userdata;
+ GError *err = NULL;
+ gssize size;
+
+ size = purple_log_get_total_size_finish(res, &err);
+
+ /* It's hard to pass all the errors up, so just log them :( */
+ if (size < 0 && err != NULL)
+ purple_debug_error("log", "Error getting total size for logs: %s\n",
+ err->message);
+ else if (size > 0)
+ callback_data->total += size;
+
+ g_clear_error(&err);
+ callback_data->counter--;
+
+ if (callback_data->counter < 1) {
+ if (callback_data->cb) {
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new(NULL, callback_data->cb,
+ callback_data->userdata, purple_log_common_total_sizer_async);
+
+ g_simple_async_result_set_op_res_gssize(simple, callback_data->total);
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+ }
+
+ if (callback_data->cancellable != NULL)
+ g_object_unref(callback_data->cancellable);
+
+ g_free(callback_data);
+ }
+}
+
+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;
+ GError *err = NULL;
+ GList *list, *n;
+ GSimpleAsyncResult *simple;
+
+ list = purple_log_get_logs_finish(res, &err);
+
+ if (list == NULL && err != NULL) {
+ simple = g_simple_async_result_new(NULL, log_total_size_cb,
+ callback_data, purple_log_common_total_sizer_async);
+
+ g_simple_async_result_set_from_error(simple, err);
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+ } else if (g_list_length(list) < 1) {
+ 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, 0);
+ g_simple_async_result_complete_in_idle(simple);
+
+ g_object_unref(simple);
+ } else {
+ callback_data->counter--;
+
+ for (n = list; n != NULL; n = g_list_next(n)) {
+ PurpleLog *log = n->data;
+
+ callback_data->counter++;
+
+ purple_log_get_size_async(log, callback_data->io_priority,
+ callback_data->cancellable, log_total_size_cb, callback_data);
+ }
+ }
+
+ g_clear_error(&err);
+}
+
+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;
+
+ callback_data->counter--;
+
+ if (callback_data->counter < 1) {
+ if (callback_data->cb) {
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new(object, callback_data->cb,
+ callback_data->userdata, log_get_log_sets_common_async);
+
+ 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
+ g_hash_table_unref(callback_data->sets);
+
+ g_mutex_free(callback_data->mutex);
+ g_free(callback_data);
+ }
+}
+
+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);
+ }
+}
+
============================================================
--- pidgin/gtkutils.c 5537edee3cffeb641b749752fefd6c80a4ef183a
+++ pidgin/gtkutils.c 37d6539d01772d33c28214a994dceb7a061cad8a
@@ -1899,6 +1899,8 @@ typedef struct
gpointer filter_func_user_data;
GtkListStore *store;
+
+ GCancellable *cancel;
} PidginCompletionData;
static gboolean buddyname_completion_match_func(GtkEntryCompletion *completion,
@@ -2055,12 +2057,27 @@ static void
}
static void
+add_completion_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ GHashTable *sets;
+ GError *err = NULL;
+
+ sets = purple_log_get_log_sets_finish(res, &err);
+
+ if (sets == NULL && err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("gtkutils", "Unable to get hash table: %s\n", err->message);
+ else
+ g_hash_table_foreach(sets, (GHFunc) get_log_set_name, userdata);
+
+ g_clear_error(&err);
+}
+
+static void
add_completion_list(PidginCompletionData *data)
{
PurpleBlistNode *gnode, *cnode, *bnode;
PidginFilterBuddyCompletionEntryFunc filter_func = data->filter_func;
gpointer user_data = data->filter_func_user_data;
- GHashTable *sets;
gtk_list_store_clear(data->store);
@@ -2092,15 +2109,27 @@ add_completion_list(PidginCompletionData
}
}
- sets = purple_log_get_log_sets();
- g_hash_table_foreach(sets, (GHFunc)get_log_set_name, data);
- g_hash_table_destroy(sets);
+ if (data->cancel != NULL) {
+ g_cancellable_cancel(data->cancel);
+ g_object_unref(data->cancel);
+ }
+ data->cancel = g_cancellable_new();
+
+ purple_log_get_log_sets_async(G_PRIORITY_DEFAULT, data->cancel,
+ add_completion_list_cb, data);
}
static void
-buddyname_autocomplete_destroyed_cb(GtkWidget *widget, gpointer data)
+buddyname_autocomplete_destroyed_cb(GtkWidget *widget, gpointer userdata)
{
+ PidginCompletionData *data = userdata;
+
+ if (data->cancel != NULL) {
+ g_cancellable_cancel(data->cancel);
+ g_object_unref(data->cancel);
+ }
+
g_free(data);
purple_signals_disconnect_by_handle(widget);
}
@@ -2139,6 +2168,7 @@ pidgin_setup_screenname_autocomplete_wit
}
data->store = store;
+ data->cancel = NULL;
add_completion_list(data);
/* Sort the completion list by buddy name */
============================================================
--- libpurple/connection.c e26d259b085aa54418b2ddd5f4e01b1ed087c441
+++ libpurple/connection.c 03b53126a80571d68ad0ae59b93cee09d0617d3e
@@ -46,6 +46,8 @@ static PurpleConnectionUiOps *connection
static GList *connections_connecting = NULL;
static PurpleConnectionUiOps *connection_ui_ops = NULL;
+static GCancellable *write_conn_cancel = NULL;
+
static int connections_handle;
static gboolean
@@ -358,10 +360,11 @@ purple_connection_set_state(PurpleConnec
{
char *msg = g_strdup_printf(_("+++ %s signed on"),
purple_account_get_username(account));
- purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
+ purple_log_write_async(log, PURPLE_MESSAGE_SYSTEM,
purple_account_get_username(account),
purple_presence_get_login_time(presence),
- msg);
+ msg, G_PRIORITY_DEFAULT,
+ write_conn_cancel, NULL, NULL);
g_free(msg);
}
}
@@ -389,9 +392,10 @@ purple_connection_set_state(PurpleConnec
{
char *msg = g_strdup_printf(_("+++ %s signed off"),
purple_account_get_username(account));
- purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
+ purple_log_write_async(log, PURPLE_MESSAGE_SYSTEM,
purple_account_get_username(account), time(NULL),
- msg);
+ msg, G_PRIORITY_DEFAULT,
+ write_conn_cancel, NULL, NULL);
g_free(msg);
}
}
@@ -690,6 +694,8 @@ purple_connections_init(void)
{
void *handle = purple_connections_get_handle();
+ write_conn_cancel = g_cancellable_new();
+
purple_signal_register(handle, "signing-on",
purple_marshal_VOID__POINTER, NULL, 1,
purple_value_new(PURPLE_TYPE_SUBTYPE,
@@ -727,6 +733,12 @@ purple_connections_uninit(void)
void
purple_connections_uninit(void)
{
+ if (write_conn_cancel != NULL) {
+ g_cancellable_cancel(write_conn_cancel);
+ g_object_unref(write_conn_cancel);
+ write_conn_cancel = NULL;
+ }
+
purple_signals_unregister_by_instance(purple_connections_get_handle());
}
============================================================
--- libpurple/log.h fdae3f0adc203739096c27e7d4bb6fc140b28591
+++ libpurple/log.h 3c17a97c416587ac26aa4e54eddf8355a37f5270
@@ -28,82 +28,123 @@
#define _PURPLE_LOG_H_
#include <stdio.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include "account.h"
+#include "conversation.h"
-/********************************************************
- * DATA STRUCTURES **************************************
- ********************************************************/
+/** @copydoc _PurpleLog */
typedef struct _PurpleLog PurpleLog;
+/** @copydoc _PurpleLogLogger */
typedef struct _PurpleLogLogger PurpleLogLogger;
+/** @copydoc _PurpleLogLoggerAsyncFuncs */
+typedef struct _PurpleLogLoggerAsyncFuncs PurpleLogLoggerAsyncFuncs;
+/** @copydoc _PurpleLogCommonLoggerData */
typedef struct _PurpleLogCommonLoggerData PurpleLogCommonLoggerData;
+/** @copydoc _PurpleLogSet */
typedef struct _PurpleLogSet PurpleLogSet;
+/**************************************************************************/
+/* Enumerations */
+/**************************************************************************/
+
+/**
+ * The log type
+ */
typedef enum {
- PURPLE_LOG_IM,
- PURPLE_LOG_CHAT,
- PURPLE_LOG_SYSTEM
+ PURPLE_LOG_IM, /**< Chat log with one person */
+ PURPLE_LOG_CHAT, /**< Group chat log */
+ PURPLE_LOG_SYSTEM /**< System log */
} PurpleLogType;
+/**
+ * Flags for purple_log_read() and purple_log_read_async()
+ */
typedef enum {
- PURPLE_LOG_READ_NO_NEWLINE = 1
+ PURPLE_LOG_READ_NO_NEWLINE = 1 /**< The log contains HTML line break tags, rather than newlines */
} PurpleLogReadFlags;
-#include "account.h"
-#include "conversation.h"
typedef void (*PurpleLogSetCallback) (GHashTable *sets, PurpleLogSet *set);
+
+/********************************************************
+ * DATA STRUCTURES **************************************
+ ********************************************************/
+
/**
+ * 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 */
+
+ // 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
* needed to write and read from logs.
*/
struct _PurpleLogLogger {
- char *name; /**< The logger's name */
- char *id; /**< an identifier to refer to this logger */
+ gchar *name; /**< The logger's name */
+ gchar *id; /**< An identifier to refer to this logger */
- /** This gets called when the log is first created.
- I don't think this is actually needed. */
+ /** This gets called when the log is first created, likely not actually needed */
void (*create)(PurpleLog *log);
/** This is used to write to the log file */
gsize (*write)(PurpleLog *log,
PurpleMessageFlags type,
- const char *from,
+ const gchar *from,
time_t time,
- const char *message);
+ const gchar *message);
/** Called when the log is destroyed */
void (*finalize)(PurpleLog *log);
- /** This function returns a sorted GList of available PurpleLogs */
- GList *(*list)(PurpleLogType type, const char *name, PurpleAccount *account);
+ /** This function returns a sorted #GList of available #PurpleLog<!-- -->s */
+ GList *(*list)(PurpleLogType type, const gchar *name, PurpleAccount *account);
/** Given one of the logs returned by the logger's list function,
* this returns the contents of the log in GtkIMHtml markup */
- char *(*read)(PurpleLog *log, PurpleLogReadFlags *flags);
+ gchar *(*read)(PurpleLog *log, PurpleLogReadFlags *flags);
/** Given one of the logs returned by the logger's list function,
* this returns the size of the log in bytes */
- int (*size)(PurpleLog *log);
+ gint (*size)(PurpleLog *log);
/** Returns the total size of all the logs. If this is undefined a default
* implementation is used */
- int (*total_size)(PurpleLogType type, const char *name, PurpleAccount *account);
+ gint (*total_size)(PurpleLogType type, const gchar *name, PurpleAccount *account);
- /** This function returns a sorted GList of available system PurpleLogs */
+ /** This function returns a sorted #GList of available system #PurpleLog<!-- -->s */
GList *(*list_syslog)(PurpleAccount *account);
- /** Adds PurpleLogSets to a GHashTable. By passing the data in the PurpleLogSets
+ /** 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
* logs in the same directory structure as the stock loggers) do not
* need to implement this function.
*
- * Loggers which implement this function must create a PurpleLogSet,
- * then call @a cb with @a sets and the newly created PurpleLogSet. */
+ * 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);
/* Attempts to delete the specified log, indicating success or failure */
@@ -112,36 +153,70 @@ struct _PurpleLogLogger {
/* Tests whether a log is deletable */
gboolean (*is_deletable)(PurpleLog *log);
- void (*_purple_reserved1)(void);
+ /** 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;
+
void (*_purple_reserved2)(void);
void (*_purple_reserved3)(void);
void (*_purple_reserved4)(void);
};
/**
- * A log. Not the wooden type.
+ * This struct contains the non-blocking versions of the #PurpleLogLogger functions
*/
-struct _PurpleLog {
- PurpleLogType type; /**< The type of log this is */
- char *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 */
+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);
- PurpleLogLogger *logger; /**< The logging mechanism this log
- is to use */
- void *logger_data; /**< Data used by the log logger */
- 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.*/
+ /** 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);
- /* IMPORTANT: Some code in log.c allocates these without zeroing them.
- * IMPORTANT: Update that code if you add members here. */
+ /** Non-blocking version of finalize */
+ void (*finalize_async)(PurpleLog *log, gint io_priority, GCancellable *cancellable,
+ GAsyncReadyCallback cb, gpointer 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);
+
+ /** 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);
+ void (*_purple_reserved4)(void);
};
/**
@@ -149,67 +224,62 @@ struct _PurpleLogCommonLoggerData {
* as a pointer to something else for additional data.
*/
struct _PurpleLogCommonLoggerData {
- char *path;
- FILE *file;
- void *extra_data;
+ gchar *path; /**< Path to log */
+ FILE *file; /**< Log's file handle */
+ gpointer extra_data; /**< Any extra data passed */
};
/**
* Describes available logs.
*
* By passing the elements of this struct to purple_log_get_logs(), the caller
- * can get all available PurpleLogs.
+ * can get all available #PurpleLog<!-- -->s.
*/
struct _PurpleLogSet {
- PurpleLogType type; /**< The type of logs available */
- char *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. (Depending on a
- logger's implementation of
- list, it may not be possible
- to load such logs.) */
- gboolean buddy; /**< Is this (account, name) a buddy
- on the buddy list? */
- char *normalized_name; /**< The normalized version of
- @a name. It must be set, and
- may be set to the same pointer
- value as @a name. */
+ PurpleLogType 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.
+ *
+ * (Depending on a logger's implementation of list, it may
+ * not be possible to load such logs.)
+ */
+ gboolean buddy; /**< Is this (account, name) a buddy on the buddy list? */
+ gchar *normalized_name; /**< The normalized version of @a name. It must be set, and
+ * may be set to the same pointer value as @a name. */
/* IMPORTANT: Some code in log.c allocates these without zeroing them.
* IMPORTANT: Update that code if you add members here. */
};
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
/***************************************/
/** @name Log Functions */
/***************************************/
+
/*@{*/
/**
* Creates a new log
*
- * @param 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 The conversation being logged
- * @param time The time this conversation started
- * @param tm The time this conversation started, with timezone data,
- * if available and if struct tm has the BSD timezone fields.
- * @return The new log
+ * @param 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
+ * @param time The time this conversation started
+ * @param tm (allow-none): The time this conversation started, with timezone data,
+ * if available and if struct tm has the BSD timezone fields
+ *
+ * @return The new log
*/
-PurpleLog *purple_log_new(PurpleLogType type, const char *name, PurpleAccount *account,
- PurpleConversation *conv, time_t time, const struct tm *tm);
+PurpleLog *purple_log_new(PurpleLogType type, const gchar *name, PurpleAccount *account,
+ PurpleConversation *conv, time_t time, const struct tm *tm);
/**
* Frees a log
*
- * @param log The log to destroy
+ * @param log The log to destroy
*/
void purple_log_free(PurpleLog *log);
@@ -223,143 +293,410 @@ void purple_log_free(PurpleLog *log);
* @param time A timestamp in UNIX time
* @param message The message to log
*/
-void purple_log_write(PurpleLog *log,
- PurpleMessageFlags type,
- const char *from,
- time_t time,
- const char *message);
+void purple_log_write(PurpleLog *log, PurpleMessageFlags type, const gchar *from,
+ time_t time, const gchar *message);
/**
+ * Writes to a log file asychronously. Assumes you have checked preferences already.
+ *
+ * @param log The log to write to
+ * @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 io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 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);
+
+/**
+ * Finishing asynchronously writing to a log
+ *
+ * @param log The #PurpleLog that was written to
+ * @param res A #GAsyncResult
+ * @param error (out) (allow-none): a #GError location to store the error
+ *
+ * @return Number of bytes written, -1 on failure
+ *
+ * @since 2.8.0
+ */
+gssize purple_log_write_finish(PurpleLog *log, GAsyncResult *res, GError **error);
+
+/**
* Reads from a log
*
- * @param log The log to read from
- * @param flags The returned logging flags.
+ * @param log The log to read from
+ * @param flags (allow-none): Location to store the returned logging flags
*
- * @return The contents of this log in Purple Markup.
+ * @return The contents of this log in Purple Markup
*/
-char *purple_log_read(PurpleLog *log, PurpleLogReadFlags *flags);
+gchar *purple_log_read(PurpleLog *log, PurpleLogReadFlags *flags);
/**
+ * 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
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 2.8.0
+ */
+void purple_log_read_async(PurpleLog *log, PurpleLogReadFlags *flags, gint io_priority,
+ GCancellable *cancellable, GAsyncReadyCallback cb, gpointer userdata);
+
+/**
+ * Finishes asynchronously reading a log
+ *
+ * Note: If error's message is set, it will already contain the HTML formatting tags
+ *
+ * @param log The #PurpleLog that was read
+ * @param res A #GAsyncResult
+ * @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);
+
+/**
* Returns a list of all available logs
*
- * @param type The type of the log
- * @param name The name of the log
- * @param account The account
- * @return A sorted list of PurpleLogs
+ * @param type The type of the log
+ * @param name The name of the log
+ * @param account The account
+ *
+ * @return A sorted list of #PurpleLog<!-- -->s
*/
-GList *purple_log_get_logs(PurpleLogType type, const char *name, PurpleAccount *account);
+GList *purple_log_get_logs(PurpleLogType type, const gchar *name, PurpleAccount *account);
/**
- * Returns a GHashTable of PurpleLogSets.
+ * Asynchronously gets a list of all available logs
*
+ * @param type The type of the log
+ * @param name The name of the log
+ * @param account The account
+ * @param io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 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);
+
+/**
+ * Finishes asynchronously getting a list of all available logs
+ *
+ * Note: Make sure to examine that error is not NULL as it is completely possible
+ * for the list to be empty and the function to return NULL without an error
+ *
+ * @param res A #GAsyncResult
+ * @param error (out) (allow-none): a #GError location to store the error
+ *
+ * @return pointer to log list or %NULL on error
+ *
+ * @since 2.8.0
+ */
+GList *purple_log_get_logs_finish(GAsyncResult *res, GError **error);
+
+/**
+ * Returns a GHashTable of #PurpleLogSet<!-- -->s.
+ *
* A "log set" here means the information necessary to gather the
- * PurpleLogs for a given buddy/chat. This information would be passed
- * to purple_log_list to get a list of PurpleLogs.
+ * #PurpleLog<!-- -->s for a given buddy/chat. This information would be passed
+ * to purple_log_get_log to get a list of #PurpleLog<!-- -->s.
*
* The primary use of this function is to get a list of everyone the
* user has ever talked to (assuming he or she uses logging).
*
* The GHashTable that's returned will free all log sets in it when
- * destroyed. If a PurpleLogSet is removed from the GHashTable, it
+ * destroyed. If a #PurpleLogSet is removed from the GHashTable, it
* must be freed with purple_log_set_free().
*
- * @return A GHashTable of all available unique PurpleLogSets
+ * @return A GHashTable of all available unique #PurpleLogSet<!-- -->s
*/
GHashTable *purple_log_get_log_sets(void);
/**
+ * Asychronously gets a #GHashTable of #PurpleLogSet<!-- -->s.
+ *
+ * For more details, see purple_log_get_log_sets() which is
+ * the synchronous version of this call.
+ *
+ * @param io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 2.8.0
+ */
+void purple_log_get_log_sets_async(gint io_priority, GCancellable *cancellable,
+ GAsyncReadyCallback cb, gpointer userdata);
+
+/**
+ * Finishes asynchronously getting a hash table of log sets
+ *
+ * @param res A #GAsyncResult
+ * @param error (out) (allow-none): a #GError location to store the error
+ *
+ * @return pointer to hash table of log sets or %NULL on error
+ *
+ * @since 2.8.0
+ */
+GHashTable *purple_log_get_log_sets_finish(GAsyncResult *res, GError **error);
+
+/**
* Returns a list of all available system logs
*
- * @param account The account
- * @return A sorted list of PurpleLogs
+ * @param account The account
+ *
+ * @return A sorted list of #PurpleLog<!-- -->s
*/
GList *purple_log_get_system_logs(PurpleAccount *account);
/**
+ * Asychronously gets a list of all available system logs
+ *
+ * @param account The account
+ * @param io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 2.8.0
+ */
+void purple_log_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
+ *
+ * Note: Make sure to examine that error is not NULL as it is completely possible
+ * for the list to be empty and the function to return NULL without an error
+ *
+ * @param res A #GAsyncResult
+ * @param error (out) (allow-none): a #GError location to store the error
+ *
+ * @return pointer to log list or %NULL on error
+ *
+ * @since 2.8.0
+ */
+GList *purple_log_get_system_logs_finish(GAsyncResult *res, GError **error);
+
+/**
* Returns the size of a log
*
- * @param log The log
- * @return The size of the log, in bytes
+ * @param log The log
+ *
+ * @return The size of the log, in bytes
*/
-int purple_log_get_size(PurpleLog *log);
+gint purple_log_get_size(PurpleLog *log);
/**
+ * Asychronously gets the size of a log
+ *
+ * @param log The log
+ * @param io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 2.8.0
+ */
+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
+ *
+ * @param log The #PurpleLog to size
+ * @param res A #GAsyncResult
+ * @param error (out) (allow-none): a #GError location to store the error
+ *
+ * @return size of file or -1 on error
+ *
+ * @since 2.8.0
+ */
+gssize purple_log_get_size_finish(PurpleLog *log, GAsyncResult *res, GError **error);
+
+/**
* Returns the size, in bytes, of all available logs in this conversation
*
- * @param type The type of the log
- * @param name The name of the log
- * @param account The account
- * @return The size in bytes
+ * @param type The type of the log
+ * @param name The name of the log
+ * @param account The account
+ *
+ * @return The size in bytes
*/
-int purple_log_get_total_size(PurpleLogType type, const char *name, PurpleAccount *account);
+gint purple_log_get_total_size(PurpleLogType type, const gchar *name, PurpleAccount *account);
/**
+ * Asychronously gets the size, in bytes, of all available logs in this conversation
+ *
+ * @param type The type of the log
+ * @param name The name of the log
+ * @param account The account
+ * @param io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 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);
+
+/**
+ * Finishes asynchronously getting the total size of a conversation
+ *
+ * @param res A #GAsyncResult
+ * @param error (out) (allow-none): a #GError location to store the error
+ *
+ * @return size of file or -1 on error
+ *
+ * @since 2.8.0
+ */
+gssize purple_log_get_total_size_finish(GAsyncResult *res, GError **error);
+
+/**
* Returns the activity score of a log, based on total size in bytes,
* which is then decayed based on age
*
- * @param type The type of the log
- * @param name The name of the log
- * @param account The account
- * @return The activity score
+ * @param type The type of the log
+ * @param name The name of the log
+ * @param account The account
*
+ * @return The activity score
+ *
* @since 2.6.0
*/
-int purple_log_get_activity_score(PurpleLogType type, const char *name, PurpleAccount *account);
+gint purple_log_get_activity_score(PurpleLogType type, const gchar *name, PurpleAccount *account);
/**
+ * Asychronously gets the activity score of a log
+ *
+ * @param type The type of the log
+ * @param name The name of the log
+ * @param account The account
+ * @param io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 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);
+
+/**
+ * Finishes asynchronously getting the activity score of a log
+ *
+ * @param res A #GAsyncResult
+ * @param error (out) (allow-none): a #GError location to store the error
+ *
+ * @return The activity score of the log or -1 on failure
+ *
+ * @since 2.8.0
+ */
+gint purple_log_get_activity_score_finish(GAsyncResult *res, GError **error);
+
+/**
* Tests whether a log is deletable
*
* A return value of @c FALSE indicates that purple_log_delete() 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.
*
- * @param log The log
- * @return A boolean indicating if the log is deletable
+ * @param log The log
+ *
+ * @return %TRUE if the log is deletable, %FALSE otherwise
*/
gboolean purple_log_is_deletable(PurpleLog *log);
/**
* Deletes a log
*
- * @param log The log
- * @return A boolean indicating success or failure
+ * @param log The log
+ *
+ * @return %TRUE if the log was deleted, %FALSE otherwise
*/
gboolean purple_log_delete(PurpleLog *log);
/**
+ * Asychronously deletes a log
+ *
+ * @param log The log
+ * @param io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 2.8.0
+ */
+void purple_log_delete_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 res A #GAsyncResult
+ * @param error (out) (allow-none): a #GError location to store the error
+ *
+ * @return %TRUE if the log was deleted, %FALSE otherwise
+ *
+ * @since 2.8.0
+ */
+gboolean purple_log_delete_finish(PurpleLog *log, GAsyncResult *res, GError **error);
+
+/**
* Returns the default logger directory Purple uses for a given account
* and username. This would be where Purple stores logs created by
* the built-in text or HTML loggers.
*
- * @param type The type of the log.
- * @param name The name of the log.
- * @param account The account.
- * @return The default logger directory for Purple.
+ * @param type The type of the log
+ * @param name The name of the log
+ * @param account The account
+ *
+ * @return The default logger directory for Purple
*/
-char *purple_log_get_log_dir(PurpleLogType type, const char *name, PurpleAccount *account);
+gchar *purple_log_get_log_dir(PurpleLogType type, const gchar *name, PurpleAccount *account);
/**
- * Implements GCompareFunc for PurpleLogs
+ * Implements GCompareFunc for #PurpleLog<!-- -->s
*
- * @param y A PurpleLog
- * @param z Another PurpleLog
- * @return A value as specified by GCompareFunc
+ * @param y A #PurpleLog
+ * @param z Another #PurpleLog
+ *
+ * @return A value as specified by GCompareFunc
*/
gint purple_log_compare(gconstpointer y, gconstpointer z);
/**
- * Implements GCompareFunc for PurpleLogSets
+ * Implements GCompareFunc for #PurpleLogSet<!-- -->s
*
- * @param y A PurpleLogSet
- * @param z Another PurpleLogSet
- * @return A value as specified by GCompareFunc
+ * @param y A #PurpleLogSet
+ * @param z Another #PurpleLogSet
+ *
+ * @return A value as specified by GCompareFunc
*/
gint purple_log_set_compare(gconstpointer y, gconstpointer z);
/**
* Frees a log set
*
- * @param set The log set to destroy
+ * @param set The log set to destroy
*/
void purple_log_set_free(PurpleLogSet *set);
@@ -368,10 +705,11 @@ void purple_log_set_free(PurpleLogSet *s
/******************************************/
/** @name Common Logger Functions */
/******************************************/
+
/*@{*/
/**
- * Opens a new log file in the standard Purple log location
+ * Opens a new log file in the standard log location
* with the given file extension, named for the current time,
* for writing. If a log file is already open, the existing
* file handle is retained. The log's logger_data value is
@@ -383,33 +721,100 @@ void purple_log_set_free(PurpleLogSet *s
* It should only be passed to purple_log_logger_new() and never
* called directly.
*
- * @param log The log to write to.
- * @param ext The file extension to give to this log file.
+ * @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 char *ext);
+void purple_log_common_writer(PurpleLog *log, const gchar *ext);
/**
- * Returns a sorted GList of PurpleLogs of the requested type.
+ * Asynchronously opens a new log in the standard log location
*
+ * For more details, see purple_log_common_writer() which is
+ * the synchronous version of this call.
+ *
+ * @param log The log to write to
+ * @param ext (allow-none): The file extension to give to this log file
+ * @param io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ */
+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
+ *
+ * For more details, see purple_log_common_writer() which is
+ * the synchronous version of this call.
+ *
+ * @param log The log to write to
+ * @param res A #GAsyncResult
+ * @param error (out) (allow-none): a #GError location to store the error
+ *
+ * @return %TRUE if the log was created, %FALSE otherwise
+ */
+gboolean purple_log_common_writer_finish(PurpleLog *log, GAsyncResult *res, GError **error);
+
+/**
+ * Returns a sorted #GList of #PurpleLog<!-- -->s of the requested type
+ *
* 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 list function.
* It should only be passed to purple_log_logger_new() and never
* called directly.
*
- * @param type The type of the logs being listed.
- * @param name The name of the log.
- * @param account The account of the log.
- * @param ext The file extension this log format uses.
- * @param logger A reference to the logger struct for this log.
+ * @param type The type of the logs being listed
+ * @param name The name of the log
+ * @param account The account of the log
+ * @param ext The file extension this log format uses
+ * @param logger A reference to the logger struct for this log
*
- * @return A sorted GList of PurpleLogs matching the parameters.
+ * @return A sorted #GList of #PurpleLog<!-- -->s matching the parameters
*/
-GList *purple_log_common_lister(PurpleLogType type, const char *name,
- PurpleAccount *account, const char *ext,
- PurpleLogLogger *logger);
+GList *purple_log_common_lister(PurpleLogType type, const gchar *name, PurpleAccount *account,
+ const gchar *ext, PurpleLogLogger *logger);
/**
+ * Asynchronously gets a sorted #GList of #PurpleLog<!-- -->s of the requested type
+ *
+ * For more details, see purple_log_common_lister() which is
+ * the synchronous version of this call.
+ *
+ * @param type The type of the logs being listed
+ * @param name The name of the log
+ * @param account The account of the log
+ * @param ext The file extension this log format uses
+ * @param logger A reference to the logger struct for this log
+ * @param io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 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);
+
+/**
+ * Finishes asynchronously lister listing the #PurpleLog<!-- -->s of a requested type.
+ * Remember to free the #GList returned if you do not store it.
+ *
+ * Note: Make sure to examine that error is not NULL as it is completely possible
+ * for the list to be empty and the function to return NULL without an error
+ *
+ * @param res A #GAsyncResult
+ * @param error (out) (allow-none): a #GError location to store the error
+ *
+ * @return A sorted #GList of #PurpleLog<!-- -->s or %NULL on error
+ *
+ * @since 2.8.0
+ */
+GList *purple_log_common_lister_finish(GAsyncResult *res, GError **error);
+
+/**
* Returns the total size of all the logs for a given user, with
* a given extension.
*
@@ -419,19 +824,53 @@ GList *purple_log_common_lister(PurpleLo
* It should only be passed to purple_log_logger_new() and never
* called directly.
*
- * @param type The type of the logs being sized.
- * @param name The name of the logs to size
- * (e.g. the username or chat name).
- * @param account The account of the log.
- * @param ext The file extension this log format uses.
+ * @param type The type of the logs being sized
+ * @param name The name of the logs to size
+ * (e.g. the username or chat name)
+ * @param account The account of the log
+ * @param ext The file extension this log format uses
*
- * @return The size of all the logs with the specified extension
- * for the specified user.
+ * @return The size of all the logs with the specified extension
+ * for the specified user
*/
-int purple_log_common_total_sizer(PurpleLogType type, const char *name,
- PurpleAccount *account, const char *ext);
+gint purple_log_common_total_sizer(PurpleLogType type, const gchar *name, PurpleAccount *account,
+ const gchar *ext);
/**
+ * Asychronously gets the total size of all the logs for a given user, with
+ * a given extension.
+ *
+ * For more details, see purple_log_common_total_sizer() which is
+ * the synchronous version of this call.
+ *
+ * @param type The type of the logs being sized
+ * @param name The name of the logs to size (e.g. the username or chat name)
+ * @param account The account of the log
+ * @param ext The file extension this log format uses
+ * @param io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 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);
+
+/**
+ * Finishes asynchronously getting the total size of logs for a given user
+ *
+ * @param res A #GAsyncResult
+ * @param error (out) (allow-none): a #GError location to store the error
+ *
+ * @return size of file or -1 on error
+ *
+ * @since 2.8.0
+ */
+gssize purple_log_common_total_sizer_finish(GAsyncResult *res, GError **error);
+
+/**
* Returns the size of a given PurpleLog.
*
* This function should only be used with logs that are written
@@ -440,13 +879,43 @@ int purple_log_common_total_sizer(Purple
* It should only be passed to purple_log_logger_new() and never
* called directly.
*
- * @param log The PurpleLog to size.
+ * @param log The #PurpleLog to size
*
- * @return An integer indicating the size of the log in bytes.
+ * @return An integer indicating the size of the log in bytes
*/
-int purple_log_common_sizer(PurpleLog *log);
+gint purple_log_common_sizer(PurpleLog *log);
/**
+ * Asychronously gets the size of a given #PurpleLog
+ *
+ * For more details, see purple_log_common_sizer() which is
+ * the synchronous version of this call.
+ *
+ * @param log The #PurpleLog to size
+ * @param io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 2.8.0
+ */
+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
+ *
+ * @param log The #PurpleLog to size
+ * @param res A #GAsyncResult
+ * @param error (out) (allow-none): a #GError location to store the error
+ *
+ * @return size of file or -1 on error
+ *
+ * @since 2.8.0
+ */
+gssize purple_log_common_sizer_finish(PurpleLog *log, GAsyncResult *res, GError **error);
+
+/**
* Deletes a log
*
* This function should only be used with logs that are written
@@ -455,13 +924,46 @@ int purple_log_common_sizer(PurpleLog *l
* 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 delete
*
- * @return A boolean indicating success or failure.
+ * @return %TRUE if the log was deleted, %FALSE otherwise
*/
gboolean purple_log_common_deleter(PurpleLog *log);
/**
+ * Asynchronously deletes 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.
+ * It should only be passed to purple_log_logger_new() and never
+ * called directly.
+ *
+ * @param log The #PurpleLog to delete
+ * @param io_priority The io priority of the request
+ * @param cancellable (allow-none): #GCancellable object
+ * @param cb (allow-none): A #GAsyncReadyCallback to call when the request is satisfied
+ * @param userdata (allow-none): The data to pass to callback function
+ *
+ * @since 2.8.0
+ */
+void purple_log_common_deleter_async(PurpleLog *log, gint io_priority, GCancellable *cancellable,
+ GAsyncReadyCallback cb, gpointer userdata);
+
+/**
+ * Finish asynchronously deleting a log
+ *
+ * @param log The #PurpleLog to delete
+ * @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
+ *
+ * @since 2.8.0
+ */
+gboolean purple_log_common_deleter_finish(PurpleLog *log, GAsyncResult *res, GError **error);
+
+/**
* Checks to see if a log is deletable
*
* This function should only be used with logs that are written
@@ -470,9 +972,9 @@ gboolean purple_log_common_deleter(Purpl
* It should only be passed to purple_log_logger_new() and never
* called directly.
*
- * @param log The PurpleLog to check.
+ * @param log The #PurpleLog to check
*
- * @return A boolean indicating if the log is deletable.
+ * @return %TRUE if the log is deletable, %FALSE otherwise
*/
gboolean purple_log_common_is_deletable(PurpleLog *log);
@@ -481,103 +983,109 @@ gboolean purple_log_common_is_deletable(
/******************************************/
/** @name Logger Functions */
/******************************************/
+
/*@{*/
/**
* 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).
+ * @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
+ * @return The new logger
*/
-PurpleLogLogger *purple_log_logger_new(const char *id, const char *name, int functions, ...);
+PurpleLogLogger *purple_log_logger_new(const gchar *id, const gchar *name, gint functions, ...);
/**
* Frees a logger
*
- * @param logger The logger to free
+ * @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 logger The new logger to add
*/
-void purple_log_logger_add (PurpleLogLogger *logger);
+void purple_log_logger_add(PurpleLogLogger *logger);
/**
- *
* Removes a logger
*
- * @param logger The logger to remove
+ * @param logger The logger to remove
*/
-void purple_log_logger_remove (PurpleLogLogger *logger);
+void purple_log_logger_remove(PurpleLogLogger *logger);
/**
- *
* Sets the current logger
*
- * @param logger The logger to set
+ * @param logger The logger to set
*/
-void purple_log_logger_set (PurpleLogLogger *logger);
+void purple_log_logger_set(PurpleLogLogger *logger);
/**
- *
* Returns the current logger
*
- * @return logger The current logger
+ * @return logger The current logger
*/
-PurpleLogLogger *purple_log_logger_get (void);
+PurpleLogLogger *purple_log_logger_get(void);
/**
- * Returns a GList containing the IDs and names of the registered
- * loggers.
+ * Returns a list of all the available loggers
*
- * @return The list of IDs and names.
+ * @return loggers All available loggers
+ *
+ * @since 2.8.0
*/
+GSList *purple_log_logger_get_all(void);
+
+/**
+ * Gets a #GList containing the IDs and names of the registered loggers.
+ *
+ * @return The list of IDs and names
+ */
GList *purple_log_logger_get_options(void);
+/*@}*/
+
/**************************************************************************/
/** @name Log Subsystem */
/**************************************************************************/
+
/*@{*/
/**
- * Initializes the log subsystem.
+ * Initializes the log subsystem
*/
void purple_log_init(void);
/**
- * Returns the log subsystem handle.
+ * Returns the log subsystem handle
*
- * @return The log subsystem handle.
+ * @return The log subsystem handle
*/
void *purple_log_get_handle(void);
/**
- * Uninitializes the log subsystem.
+ * Uninitializes the log subsystem
*/
void purple_log_uninit(void);
/*@}*/
+G_END_DECLS
-#ifdef __cplusplus
-}
-#endif
-
#endif /* _PURPLE_LOG_H_ */
============================================================
--- libpurple/status.c 1fe615918cf2ee8aebf62008f21b0b5b7aa834ae
+++ libpurple/status.c fd865c7f897eb6d5df5653191a7941b3a20c1d85
@@ -35,6 +35,8 @@
#include "prefs.h"
#include "status.h"
+static GCancellable *write_status_cancel = NULL;
+
/**
* A type of status.
*/
@@ -645,8 +647,9 @@ notify_buddy_status_update(PurpleBuddy *
log = purple_account_get_log(purple_buddy_get_account(buddy), FALSE);
if (log != NULL)
{
- purple_log_write(log, PURPLE_MESSAGE_SYSTEM, buddy_alias,
- current_time, logtmp);
+ purple_log_write_async(log, PURPLE_MESSAGE_SYSTEM, buddy_alias,
+ current_time, logtmp, G_PRIORITY_DEFAULT, write_status_cancel,
+ NULL, NULL);
}
g_free(tmp);
@@ -1267,8 +1270,9 @@ update_buddy_idle(PurpleBuddy *buddy, Pu
tmp2 = g_markup_escape_text(tmp, -1);
g_free(tmp);
- purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
- purple_buddy_get_alias(buddy), current_time, tmp2);
+ purple_log_write_async(log, PURPLE_MESSAGE_SYSTEM,
+ purple_buddy_get_alias(buddy), current_time, tmp2,
+ G_PRIORITY_DEFAULT, write_status_cancel, NULL, NULL);
g_free(tmp2);
}
}
@@ -1287,8 +1291,9 @@ update_buddy_idle(PurpleBuddy *buddy, Pu
tmp2 = g_markup_escape_text(tmp, -1);
g_free(tmp);
- purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
- purple_buddy_get_alias(buddy), current_time, tmp2);
+ purple_log_write_async(log, PURPLE_MESSAGE_SYSTEM,
+ purple_buddy_get_alias(buddy), current_time, tmp2,
+ G_PRIORITY_DEFAULT, write_status_cancel, NULL, NULL);
g_free(tmp2);
}
}
@@ -1354,9 +1359,11 @@ purple_presence_set_idle(PurplePresence
msg = g_markup_escape_text(tmp, -1);
g_free(tmp);
- purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
- purple_account_get_username(account),
- (idle ? idle_time : current_time), msg);
+
+ purple_log_write_async(log, PURPLE_MESSAGE_SYSTEM,
+ purple_account_get_username(account),
+ idle ? idle_time : current_time, msg,
+ G_PRIORITY_DEFAULT, write_status_cancel, NULL, NULL);
g_free(msg);
}
}
@@ -1664,6 +1671,8 @@ purple_status_init(void)
{
void *handle = purple_status_get_handle();
+ write_status_cancel = g_cancellable_new();
+
purple_prefs_add_none("/purple/status");
purple_prefs_add_none("/purple/status/scores");
@@ -1716,5 +1725,11 @@ purple_status_uninit(void)
void
purple_status_uninit(void)
{
+ if (write_status_cancel != NULL) {
+ g_cancellable_cancel(write_status_cancel);
+ g_object_unref(write_status_cancel);
+ write_status_cancel = NULL;
+ }
+
purple_prefs_disconnect_by_handle(purple_prefs_get_handle());
}
============================================================
--- pidgin/gtklog.c f9d82fe3e8fab18648c330dbfffb92f146b5062f
+++ pidgin/gtklog.c 1a7b27749c5c448a40a40c92cbf832f0501b9171
@@ -39,21 +39,200 @@
#include "gtklog.h"
#include "gtkutils.h"
-static GHashTable *log_viewers = NULL;
-static void populate_log_tree(PidginLogViewer *lv);
-static PidginLogViewer *syslog_viewer = NULL;
-struct log_viewer_hash_t {
- PurpleLogType type;
- char *buddyname;
+// PidginLogViewer members are stored here to avoid an ABI break, will be moved back in 3.x
+typedef struct {
+ PidginLogViewer *lv; /**< The rest of the struct */
+ gsize size; /**< The actual value of the total log size */
+ GtkWidget *list_bar; /**< The progress bar */
+ GtkWidget *search_bar; /**< The progess bar for searches */
+ GCancellable *read_cancel; /**< A GCancellable to stop any reads */
+ GCancellable *search_cancel; /**< A GCancellable to stop any searches */
+ GCancellable *list_cancel; /**< A GCancellable to stop any folder listings */
+ gboolean selected; /**< A gboolean to indicate the user has already selected a log */
+ PurpleLogType type; /**< The log type of the window */
+#if GTK_CHECK_VERSION(2, 20, 0)
+ GtkWidget *spinner; /**< A spinner to indicate a read is in progress */
+#endif
+} PidginLogViewerEx;
+
+typedef struct {
+ PidginLogViewerEx *log_viewer;
+ PurpleLog *log;
+
+ gboolean is_window_open;
+ gulong destroy_handler_id;
+
+ guint count;
+ guint total;
+} _pidgin_log_data;
+
+typedef struct {
+ _pidgin_log_data *log_data;
+ PurpleLog *log;
+} _pidgin_search_callback_data;
+
+typedef struct {
+ _pidgin_log_data *log_data;
+ GtkMenuPositionFunc func;
+ GtkTreeIter *iter;
+} log_menu_callback_data;
+
+typedef struct {
+ _pidgin_log_data *log_data;
+ GtkTreeIter *iter;
+ guint destroy_handler_id;
+} log_delete_callback_data;
+
+typedef struct {
PurpleAccount *account;
PurpleContact *contact;
-};
+ PurpleLogType type;
+ gchar *buddyname;
+} log_viewer_hash_t;
-static guint log_viewer_hash(gconstpointer data)
+
+static void pidgin_log_data_free(_pidgin_log_data *);
+static void pidgin_window_destroy_cb(_pidgin_log_data *);
+static guint log_viewer_hash(gconstpointer);
+static gboolean log_viewer_equal(gconstpointer, gconstpointer);
+static void select_first_log(PidginLogViewerEx *);
+static const gchar *log_get_date(PurpleLog *);
+static void pidgin_log_search_done_cb(_pidgin_log_data *);
+static void pidgin_log_search_cb(GObject *, GAsyncResult *, gpointer);
+static void search_cb(GtkWidget *, PidginLogViewerEx *);
+static void destroy_cb(GtkWidget *, gint, log_viewer_hash_t *);
+static void log_row_activated_cb(GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *);
+static void delete_log_cleanup_cb(log_delete_callback_data *);
+static void pidgin_log_delete_log_cb(GObject *, GAsyncResult *, gpointer);
+static void delete_log_cb(log_delete_callback_data *);
+static void remove_delete_log_window(void *);
+static void log_delete_log_cb(GtkWidget *, log_menu_callback_data *);
+static void log_menu_callback_data_free(log_menu_callback_data *);
+static void log_show_popup_menu(GtkWidget *, GdkEventButton *, log_menu_callback_data *);
+static gboolean log_button_press_cb(GtkWidget *, GdkEventButton *, PidginLogViewerEx *);
+static gboolean log_popup_menu_cb(GtkWidget *, PidginLogViewerEx *);
+static gboolean search_find_cb(gpointer);
+static void pidgin_log_read_cb(GObject *, GAsyncResult *, gpointer);
+static void log_select_cb(GtkTreeSelection *, PidginLogViewerEx *);
+static void populate_log_tree(PidginLogViewerEx *);
+static PidginLogViewerEx *display_log_viewer_nonblocking(log_viewer_hash_t *,
+ const gchar *, GtkWidget *, gboolean);
+static void insert_log_viewer_log(PidginLogViewerEx *, PurpleLog *);
+static void append_log_viewer_logs(PidginLogViewerEx *, GList *);
+static void pidgin_log_done_cb(_pidgin_log_data *);
+static void pidgin_log_viewer_update_list_bar(_pidgin_log_data *);
+static void pidgin_log_viewer_update_search_bar(_pidgin_log_data *);
+static void pidgin_log_size_cb(GObject *, GAsyncResult *, gpointer);
+static void pidgin_log_list_cb(GObject *, GAsyncResult *, gpointer);
+static void pidgin_log_system_list_cb(GObject *, GAsyncResult *, gpointer);
+
+
+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)
{
- const struct log_viewer_hash_t *viewer = data;
+ 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)
+{
+ if (data->destroy_handler_id > 0)
+ g_signal_handler_disconnect(data->log_viewer->lv->window,
+ data->destroy_handler_id);
+
+ g_free(data);
+}
+
+static void
+pidgin_window_destroy_cb(_pidgin_log_data *data)
+{
+ data->destroy_handler_id = 0;
+ data->is_window_open = FALSE;
+}
+
+static void
+pidgin_log_viewer_ex_set_read_cancel(PidginLogViewerEx *lv_ex, GCancellable *cancel)
+{
+ if (lv_ex->read_cancel != NULL) {
+ g_cancellable_cancel(lv_ex->read_cancel);
+ g_object_unref(lv_ex->read_cancel);
+ }
+
+ if (cancel != NULL)
+ g_object_ref(cancel);
+
+ lv_ex->read_cancel = cancel;
+}
+
+static void
+pidgin_log_viewer_ex_set_search_cancel(PidginLogViewerEx *lv_ex, GCancellable *cancel)
+{
+ if (lv_ex->search_cancel != NULL) {
+ g_cancellable_cancel(lv_ex->search_cancel);
+ g_object_unref(lv_ex->search_cancel);
+ }
+
+ if (cancel != NULL)
+ g_object_ref(cancel);
+
+ lv_ex->search_cancel = cancel;
+}
+
+static void
+pidgin_log_viewer_ex_set_list_cancel(PidginLogViewerEx *lv_ex, GCancellable *cancel)
+{
+ if (lv_ex->list_cancel != NULL) {
+ g_cancellable_cancel(lv_ex->list_cancel);
+ g_object_unref(lv_ex->list_cancel);
+ }
+
+ if (cancel != NULL)
+ g_object_ref(cancel);
+
+ lv_ex->list_cancel = cancel;
+}
+
+static guint
+log_viewer_hash(gconstpointer data)
+{
+ const log_viewer_hash_t *viewer = data;
+
if (viewer->contact != NULL)
return g_direct_hash(viewer->contact);
@@ -61,24 +240,20 @@ static guint log_viewer_hash(gconstpoint
g_str_hash(purple_account_get_username(viewer->account));
}
-static gboolean log_viewer_equal(gconstpointer y, gconstpointer z)
+static gboolean
+log_viewer_equal(gconstpointer y, gconstpointer z)
{
- const struct log_viewer_hash_t *a, *b;
- int ret;
- char *normal;
+ const log_viewer_hash_t *a = y, *b = z;
+ gint ret;
+ gchar *normal;
- a = y;
- b = z;
-
if (a->contact != NULL) {
if (b->contact != NULL)
return (a->contact == b->contact);
else
return FALSE;
- } else {
- if (b->contact != NULL)
- return FALSE;
- }
+ } else if (b->contact != NULL)
+ return FALSE;
normal = g_strdup(purple_normalize(a->account, a->buddyname));
ret = (a->account == b->account) &&
@@ -88,30 +263,30 @@ static gboolean log_viewer_equal(gconstp
return ret;
}
-static void select_first_log(PidginLogViewer *lv)
+static void
+select_first_log(PidginLogViewerEx *lv_ex)
{
- GtkTreeModel *model;
+ PidginLogViewer *lv = lv_ex->lv;
+ GtkTreeModel *model = GTK_TREE_MODEL(lv->treestore);
GtkTreeIter iter, it;
GtkTreePath *path;
- model = GTK_TREE_MODEL(lv->treestore);
-
if (!gtk_tree_model_get_iter_first(model, &iter))
return;
path = gtk_tree_model_get_path(model, &iter);
- if (gtk_tree_model_iter_children(model, &it, &iter))
- {
+
+ if (gtk_tree_model_iter_children(model, &it, &iter)) {
gtk_tree_view_expand_row(GTK_TREE_VIEW(lv->treeview), path, TRUE);
path = gtk_tree_model_get_path(model, &it);
}
gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(lv->treeview)), path);
-
gtk_tree_path_free(path);
}
-static const char *log_get_date(PurpleLog *log)
+static const gchar *
+log_get_date(PurpleLog *log)
{
if (log->tm)
return purple_date_format_full(log->tm);
@@ -119,81 +294,158 @@ static const char *log_get_date(PurpleLo
return purple_date_format_full(localtime(&log->time));
}
-static void search_cb(GtkWidget *button, PidginLogViewer *lv)
+static void
+pidgin_log_search_done_cb(_pidgin_log_data *pidgin_log_data)
{
- const char *search_term = gtk_entry_get_text(GTK_ENTRY(lv->entry));
+ if (pidgin_log_data->is_window_open) {
+ PidginLogViewerEx *lv_ex = pidgin_log_data->log_viewer;
+
+ if (!lv_ex->selected)
+ select_first_log(lv_ex);
+
+ gtk_widget_hide(lv_ex->search_bar);
+ }
+
+ pidgin_log_data_free(pidgin_log_data);
+}
+
+static void
+pidgin_log_search_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _pidgin_search_callback_data *callback_data = userdata;
+ _pidgin_log_data *pidgin_log_data = callback_data->log_data;
+ PurpleLog *log = callback_data->log;
+ GError *err = NULL;
+ gchar *text;
+
+ text = purple_log_read_finish(log, res, &err);
+ pidgin_log_data->count--;
+
+ if (text == NULL) {
+ if (err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("gtklog", "Error reading file during search: %s\n", err->message);
+ } else if (pidgin_log_data->is_window_open) {
+ PidginLogViewer *lv = pidgin_log_data->log_viewer->lv;
+ GtkTreeIter iter;
+
+ if (*text && purple_strcasestr(text, lv->search)) {
+ gtk_tree_store_append(lv->treestore, &iter, NULL);
+ gtk_tree_store_set(lv->treestore, &iter, 0, log_get_date(log), 1, log, -1);
+ }
+ }
+
+ g_clear_error(&err);
+ pidgin_log_viewer_update_search_bar(pidgin_log_data);
+
+ if (pidgin_log_data->count < 1)
+ pidgin_log_search_done_cb(pidgin_log_data);
+
+ g_free(callback_data);
+}
+
+static void
+search_cb(GtkWidget *button, PidginLogViewerEx *lv_ex)
+{
+ _pidgin_log_data *pidgin_log_data;
+ PidginLogViewer *lv = lv_ex->lv;
+ GtkIMHtml *imhtml;
+ GCancellable *cancel;
GList *logs;
+ const gchar *search_term;
+ guint length;
+ search_term = gtk_entry_get_text(GTK_ENTRY(lv->entry));
+ imhtml = GTK_IMHTML(lv->imhtml);
+
if (!(*search_term)) {
/* reset the tree */
+ pidgin_log_viewer_ex_set_search_cancel(lv_ex, NULL);
+
gtk_tree_store_clear(lv->treestore);
- populate_log_tree(lv);
+ populate_log_tree(lv_ex);
+
g_free(lv->search);
lv->search = NULL;
- gtk_imhtml_search_clear(GTK_IMHTML(lv->imhtml));
- select_first_log(lv);
+
+ gtk_imhtml_search_clear(imhtml);
+ select_first_log(lv_ex);
+
return;
}
- if (lv->search != NULL && !strcmp(lv->search, search_term))
- {
+ if (lv->search != NULL && !strcmp(lv->search, search_term)) {
/* Searching for the same term acts as "Find Next" */
- gtk_imhtml_search_find(GTK_IMHTML(lv->imhtml), lv->search);
+ gtk_imhtml_search_find(imhtml, lv->search);
+
return;
}
- pidgin_set_cursor(lv->window, GDK_WATCH);
+ length = g_list_length(lv->logs);
+ if (length < 1)
+ return;
+
g_free(lv->search);
lv->search = g_strdup(search_term);
gtk_tree_store_clear(lv->treestore);
- gtk_imhtml_clear(GTK_IMHTML(lv->imhtml));
+ gtk_imhtml_clear(imhtml);
+ lv_ex->selected = FALSE;
- for (logs = lv->logs; logs != NULL; logs = logs->next) {
- char *read = purple_log_read((PurpleLog*)logs->data, NULL);
- if (read && *read && purple_strcasestr(read, search_term)) {
- GtkTreeIter iter;
- PurpleLog *log = logs->data;
+ pidgin_log_data = g_new0(_pidgin_log_data, 1);
+ pidgin_log_data->is_window_open = TRUE;
+ pidgin_log_data->log_viewer = lv_ex;
+ pidgin_log_data->count = pidgin_log_data->total = length;
- gtk_tree_store_append (lv->treestore, &iter, NULL);
- gtk_tree_store_set(lv->treestore, &iter,
- 0, log_get_date(log),
- 1, log, -1);
- }
- g_free(read);
+ pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv->window,
+ "destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
+
+ gtk_widget_show(lv_ex->search_bar);
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(lv_ex->search_bar), 0.0);
+
+ cancel = g_cancellable_new();
+ pidgin_log_viewer_ex_set_search_cancel(lv_ex, cancel);
+
+ for (logs = lv->logs; logs != NULL; logs = g_list_next(logs)) {
+ _pidgin_search_callback_data *callback_data;
+ PurpleLog *log = logs->data;
+
+ callback_data = g_new0(_pidgin_search_callback_data, 1);
+ callback_data->log_data = pidgin_log_data;
+ callback_data->log = log;
+
+ purple_log_read_async(log, NULL, G_PRIORITY_DEFAULT_IDLE, cancel,
+ pidgin_log_search_cb, callback_data);
}
- select_first_log(lv);
- pidgin_clear_cursor(lv->window);
+ g_object_unref(cancel);
}
-static void destroy_cb(GtkWidget *w, gint resp, struct log_viewer_hash_t *ht) {
- PidginLogViewer *lv = syslog_viewer;
+static void
+destroy_cb(GtkWidget *w, gint resp, log_viewer_hash_t *ht)
+{
+ PidginLogViewerEx *lv_ex = syslog_viewer;
+ PidginLogViewer *lv;
+ GList *logs;
-#ifdef _WIN32
+#ifdef G_OS_WIN32
if (resp == GTK_RESPONSE_HELP) {
GtkTreeSelection *sel;
GtkTreeIter iter;
GtkTreeModel *model;
PurpleLog *log = NULL;
- char *logdir;
+ gchar *logdir;
if (ht != NULL)
- lv = g_hash_table_lookup(log_viewers, ht);
- model = GTK_TREE_MODEL(lv->treestore);
+ lv_ex = g_hash_table_lookup(log_viewers, ht);
+ lv = lv_ex->lv;
+ model = GTK_TREE_MODEL(lv->treestore);
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(lv->treeview));
- if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
- GValue val;
- val.g_type = 0;
- gtk_tree_model_get_value (model, &iter, 1, &val);
- log = g_value_get_pointer(&val);
- g_value_unset(&val);
- }
+ if (gtk_tree_selection_get_selected(sel, &model, &iter))
+ gtk_tree_model_get(model, &iter, 1, &log, -1);
-
if (log == NULL)
logdir = g_build_filename(purple_user_dir(), "logs", NULL);
else
@@ -201,12 +453,13 @@ static void destroy_cb(GtkWidget *w, gin
winpidgin_shell_execute(logdir, "explore", NULL);
g_free(logdir);
+
return;
}
#endif
if (ht != NULL) {
- lv = g_hash_table_lookup(log_viewers, ht);
+ lv_ex = g_hash_table_lookup(log_viewers, ht);
g_hash_table_remove(log_viewers, ht);
g_free(ht->buddyname);
@@ -214,50 +467,81 @@ static void destroy_cb(GtkWidget *w, gin
} else
syslog_viewer = NULL;
+ lv = lv_ex->lv;
+
purple_request_close_with_handle(lv);
- g_list_foreach(lv->logs, (GFunc)purple_log_free, NULL);
+ for (logs = lv->logs; logs != NULL; logs = g_list_next(logs))
+ purple_log_free(logs->data);
+
g_list_free(lv->logs);
-
g_free(lv->search);
g_free(lv);
+ if (lv_ex->read_cancel != NULL) {
+ g_cancellable_cancel(lv_ex->read_cancel);
+ g_object_unref(lv_ex->read_cancel);
+ }
+
+ if (lv_ex->search_cancel != NULL) {
+ g_cancellable_cancel(lv_ex->search_cancel);
+ g_object_unref(lv_ex->search_cancel);
+ }
+
+ if (lv_ex->list_cancel != NULL) {
+ g_cancellable_cancel(lv_ex->list_cancel);
+ g_object_unref(lv_ex->list_cancel);
+ }
+
+ g_free(lv_ex);
gtk_widget_destroy(w);
}
-static void log_row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, PidginLogViewer *viewer) {
+static void
+log_row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col)
+{
if (gtk_tree_view_row_expanded(tv, path))
gtk_tree_view_collapse_row(tv, path);
else
gtk_tree_view_expand_row(tv, path, FALSE);
}
-static void delete_log_cleanup_cb(gpointer *data)
+static void
+delete_log_cleanup_cb(log_delete_callback_data *data)
{
- g_free(data[1]); /* iter */
+ if (data->destroy_handler_id > 0)
+ g_signal_handler_disconnect(data->log_data->log_viewer->lv->window,
+ data->destroy_handler_id);
+
+ pidgin_log_data_free(data->log_data);
+ g_free(data->iter);
g_free(data);
}
-static void delete_log_cb(gpointer *data)
+static void
+pidgin_log_delete_log_cb(GObject *object, GAsyncResult *res, gpointer userdata)
{
- if (!purple_log_delete((PurpleLog *)data[2]))
- {
- purple_notify_error(NULL, NULL, _("Log Deletion Failed"),
- _("Check permissions and try again."));
- }
- else
- {
- GtkTreeStore *treestore = data[0];
- GtkTreeIter *iter = (GtkTreeIter *)data[1];
+ log_delete_callback_data *temp = userdata;
+ GError *err = NULL;
+ gboolean result;
+
+ /* Technically speaking, purple_log_delete_finish doesn't care about
+ the first argument */
+ result = purple_log_delete_finish(NULL, res, &err);
+
+ if (!result)
+ purple_notify_error(NULL, NULL, "Log Deletion Failed",
+ err->message);
+ else {
+ GtkTreeStore *treestore = temp->log_data->log_viewer->lv->treestore;
+ GtkTreeIter *iter = temp->iter;
GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(treestore), iter);
gboolean first = !gtk_tree_path_prev(path);
- if (!gtk_tree_store_remove(treestore, iter) && first)
- {
+ if (!gtk_tree_store_remove(treestore, iter) && first) {
/* iter was the last child at its level */
- if (gtk_tree_path_up(path))
- {
+ if (gtk_tree_path_up(path)) {
gtk_tree_model_get_iter(GTK_TREE_MODEL(treestore), iter, path);
gtk_tree_store_remove(treestore, iter);
}
@@ -266,46 +550,60 @@ static void delete_log_cb(gpointer *data
gtk_tree_path_free(path);
}
- delete_log_cleanup_cb(data);
+ g_clear_error(&err);
+ delete_log_cleanup_cb(temp);
}
-static void log_delete_log_cb(GtkWidget *menuitem, gpointer *data)
+static void
+delete_log_cb(log_delete_callback_data *data)
{
- PidginLogViewer *lv = data[0];
- PurpleLog *log = data[1];
- const char *time = log_get_date(log);
- const char *name;
- char *tmp;
- gpointer *data2;
+ purple_log_delete_async(data->log_data->log, G_PRIORITY_DEFAULT, NULL,
+ pidgin_log_delete_log_cb, data);
+}
- if (log->type == PURPLE_LOG_IM)
- {
- PurpleBuddy *buddy = purple_find_buddy(log->account, log->name);
+static void
+remove_delete_log_window(void *handle)
+{
+ purple_request_close(PURPLE_REQUEST_ACTION, handle);
+}
+
+static void
+log_delete_log_cb(GtkWidget *menuitem, log_menu_callback_data *data)
+{
+ log_delete_callback_data *data2;
+ PidginLogViewerEx *lv_ex = data->log_data->log_viewer;
+ PurpleBuddy *buddy;
+ PurpleChat *chat;
+ PurpleLog *log = data->log_data->log;
+ const gchar *name, *time = log_get_date(log);
+ gchar *str;
+ void *handle;
+
+ if (log->type == PURPLE_LOG_IM) {
+ buddy = purple_find_buddy(log->account, log->name);
+
if (buddy != NULL)
name = purple_buddy_get_contact_alias(buddy);
else
name = log->name;
- tmp = g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
- "conversation with %s which started at %s?"), name, time);
- }
- else if (log->type == PURPLE_LOG_CHAT)
- {
- PurpleChat *chat = purple_blist_find_chat(log->account, log->name);
+ str = g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
+ "conversation with %s which started at %s?"), name, time);
+ } else if (log->type == PURPLE_LOG_CHAT) {
+ chat = purple_blist_find_chat(log->account, log->name);
+
if (chat != NULL)
name = purple_chat_get_name(chat);
else
name = log->name;
- tmp = g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
- "conversation in %s which started at %s?"), name, time);
- }
- else if (log->type == PURPLE_LOG_SYSTEM)
- {
- tmp = g_strdup_printf(_("Are you sure you want to permanently delete the system log "
- "which started at %s?"), time);
- }
- else
+ str = g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
+ "conversation in %s which started at %s?"), name, time);
+ } else if (log->type == PURPLE_LOG_SYSTEM) {
+ str = g_strdup_printf(_("Are you sure you want to permanently delete the system log "
+ "which started at %s?"), time);
+ } else
+ /* Stuff will get freed elsewhere */
g_return_if_reached();
/* The only way to free data in all cases is to tie it to the menuitem with
@@ -313,174 +611,268 @@ static void log_delete_log_cb(GtkWidget
* delete_log_cb() to delete the log from the log viewer after the file is
* deleted, we have to allocate a new data array and make sure it gets freed
* either way. */
- data2 = g_new(gpointer, 3);
- data2[0] = lv->treestore;
- data2[1] = data[3]; /* iter */
- data2[2] = log;
- purple_request_action(lv, NULL, _("Delete Log?"), tmp, 0,
- NULL, NULL, NULL,
- data2, 2,
- _("Delete"), delete_log_cb,
- _("Cancel"), delete_log_cleanup_cb);
- g_free(tmp);
+ data2 = g_new(log_delete_callback_data, 1);
+ data2->log_data = data->log_data;
+ data2->iter = g_new(GtkTreeIter, 1);
+ *(data2->iter) = *(data->iter);
+ data2->log_data->count++;
+
+ handle = purple_request_action(lv_ex, NULL, _("Delete Log?"), str, 0,
+ NULL, NULL, NULL, data2, 2,
+ _("Delete"), delete_log_cb,
+ _("Cancel"), delete_log_cleanup_cb);
+
+ g_free(str);
+
+ /* Close the dialog window when the log viewer is closed */
+ data2->destroy_handler_id = g_signal_connect_swapped(lv_ex->lv->window,
+ "destroy", G_CALLBACK(remove_delete_log_window), handle);
}
-static void log_show_popup_menu(GtkWidget *treeview, GdkEventButton *event, gpointer *data)
+static void
+log_menu_callback_data_free(log_menu_callback_data *data)
{
- GtkWidget *menu = gtk_menu_new();
- GtkWidget *menuitem = gtk_menu_item_new_with_label(_("Delete Log..."));
+ _pidgin_log_data *pidgin_log_data = data->log_data;
- if (!purple_log_is_deletable((PurpleLog *)data[1]))
+ pidgin_log_data->count--;
+
+ if (pidgin_log_data->count < 1)
+ pidgin_log_data_free(pidgin_log_data);
+
+ g_free(data->iter);
+ g_free(data);
+}
+
+static void
+log_show_popup_menu(GtkWidget *treeview, GdkEventButton *event, log_menu_callback_data *data)
+{
+ _pidgin_log_data *pidgin_log_data = data->log_data;
+ PidginLogViewer *lv = pidgin_log_data->log_viewer->lv;
+ GtkWidget *menu, *menuitem;
+
+ menu = gtk_menu_new();
+ menuitem = gtk_menu_item_new_with_label(_("Delete Log..."));
+
+ if (!purple_log_is_deletable(data->log_data->log))
gtk_widget_set_sensitive(menuitem, FALSE);
g_signal_connect(menuitem, "activate", G_CALLBACK(log_delete_log_cb), data);
- g_object_set_data_full(G_OBJECT(menuitem), "log-viewer-data", data, g_free);
+ g_object_set_data_full(G_OBJECT(menuitem), "log-viewer-data", data,
+ (GDestroyNotify) log_menu_callback_data_free);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
gtk_widget_show_all(menu);
- gtk_menu_popup(GTK_MENU(menu), NULL, NULL, (GtkMenuPositionFunc)data[2], NULL,
- (event != NULL) ? event->button : 0,
- gdk_event_get_time((GdkEvent *)event));
+ pidgin_log_data->count = 1;
+ pidgin_log_data->is_window_open = TRUE;
+ pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv->window,
+ "destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
+
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, data->func, NULL,
+ event != NULL ? event->button : 0,
+ gdk_event_get_time((GdkEvent *) event));
}
-static gboolean log_button_press_cb(GtkWidget *treeview, GdkEventButton *event, PidginLogViewer *lv)
+static gboolean
+log_button_press_cb(GtkWidget *treeview, GdkEventButton *event, PidginLogViewerEx *lv_ex)
{
- if (event->type == GDK_BUTTON_PRESS && event->button == 3)
- {
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ log_menu_callback_data *data;
+ GtkTreeModel *model = GTK_TREE_MODEL(lv_ex->lv->treestore);
+ GtkTreeSelection *selection;
GtkTreePath *path;
GtkTreeIter *iter;
- GValue val;
PurpleLog *log;
- gpointer *data;
- if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), event->x, event->y, &path, NULL, NULL, NULL))
+ if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), event->x, event->y,
+ &path, NULL, NULL, NULL))
return FALSE;
+
iter = g_new(GtkTreeIter, 1);
- gtk_tree_model_get_iter(GTK_TREE_MODEL(lv->treestore), iter, path);
- val.g_type = 0;
- gtk_tree_model_get_value(GTK_TREE_MODEL(lv->treestore), iter, 1, &val);
+ gtk_tree_model_get_iter(model, iter, path);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+ gtk_tree_selection_select_iter(selection, iter);
+ gtk_tree_model_get(model, iter, 1, &log, -1);
gtk_tree_path_free(path);
- log = g_value_get_pointer(&val);
-
- if (log == NULL)
- {
+ if (log == NULL) {
g_free(iter);
return FALSE;
}
- data = g_new(gpointer, 4);
- data[0] = lv;
- data[1] = log;
- data[2] = NULL;
- data[3] = iter;
+ data = g_new(log_menu_callback_data, 1);
+ data->log_data = g_new0(_pidgin_log_data, 1);
+ data->log_data->log_viewer = lv_ex;
+ data->log_data->log = log;
+ data->func = NULL;
+ data->iter = iter;
log_show_popup_menu(treeview, event, data);
+
return TRUE;
}
return FALSE;
}
-static gboolean log_popup_menu_cb(GtkWidget *treeview, PidginLogViewer *lv)
+static gboolean
+log_popup_menu_cb(GtkWidget *treeview, PidginLogViewerEx *lv_ex)
{
+ log_menu_callback_data *data;
+ PidginLogViewer *lv = lv_ex->lv;
GtkTreeSelection *sel;
GtkTreeIter *iter;
- GValue val;
PurpleLog *log;
- gpointer *data;
iter = g_new(GtkTreeIter, 1);
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(lv->treeview));
- if (!gtk_tree_selection_get_selected(sel, NULL, iter))
- {
+
+ if (!gtk_tree_selection_get_selected(sel, NULL, iter)) {
+ g_free(iter);
return FALSE;
}
- val.g_type = 0;
- gtk_tree_model_get_value(GTK_TREE_MODEL(lv->treestore),
- iter, NODE_COLUMN, &val);
+ gtk_tree_model_get(GTK_TREE_MODEL(lv->treestore), iter, NODE_COLUMN, &log, -1);
- log = g_value_get_pointer(&val);
-
- if (log == NULL)
+ if (log == NULL) {
+ g_free(iter);
return FALSE;
+ }
- data = g_new(gpointer, 4);
- data[0] = lv;
- data[1] = log;
- data[2] = pidgin_treeview_popup_menu_position_func;
- data[3] = iter;
+ data = g_new(log_menu_callback_data, 1);
+ data->log_data = g_new0(_pidgin_log_data, 1);
+ data->log_data->log_viewer = lv_ex;
+ data->log_data->log = log;
+ data->func = pidgin_treeview_popup_menu_position_func;
+ data->iter = iter;
log_show_popup_menu(treeview, NULL, data);
+
return TRUE;
}
-static gboolean search_find_cb(gpointer data)
+static gboolean
+search_find_cb(gpointer userdata)
{
- PidginLogViewer *viewer = data;
- gtk_imhtml_search_find(GTK_IMHTML(viewer->imhtml), viewer->search);
- g_object_steal_data(G_OBJECT(viewer->entry), "search-find-cb");
+ _pidgin_log_data *pidgin_log_data = userdata;
+
+ if (pidgin_log_data->is_window_open) {
+ PidginLogViewer *lv = pidgin_log_data->log_viewer->lv;
+
+ gtk_imhtml_search_find(GTK_IMHTML(lv->imhtml), lv->search);
+ }
+
+ pidgin_log_data_free(pidgin_log_data);
+
return FALSE;
}
-static void log_select_cb(GtkTreeSelection *sel, PidginLogViewer *viewer) {
- GtkTreeIter iter;
- GValue val;
- GtkTreeModel *model = GTK_TREE_MODEL(viewer->treestore);
+static void
+pidgin_log_read_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _pidgin_log_data *pidgin_log_data = userdata;
+
+ if (pidgin_log_data->is_window_open) {
+ PurpleLog *log = pidgin_log_data->log;
+ PidginLogViewerEx *lv_ex = pidgin_log_data->log_viewer;
+ PidginLogViewer *lv = lv_ex->lv;
+ GtkIMHtml *imhtml = GTK_IMHTML(lv->imhtml);
+ GError *err = NULL;
+ gchar *text;
+
+ text = purple_log_read_finish(log, res, &err);
+
+ if (text == NULL) {
+ if (err->code == G_IO_ERROR_CANCELLED) {
+ pidgin_log_data_free(pidgin_log_data);
+ g_clear_error(&err);
+
+ return;
+ }
+
+ text = err->message;
+ }
+
+#if GTK_CHECK_VERSION(2, 20, 0)
+ gtk_widget_show(lv->imhtml);
+ gtk_widget_hide(lv_ex->spinner);
+#endif
+
+ gtk_imhtml_clear(imhtml);
+ gtk_imhtml_set_protocol_name(imhtml,
+ purple_account_get_protocol_name(log->account));
+
+ purple_signal_emit(pidgin_log_get_handle(), "log-displaying", lv, log);
+
+ //gtk_imhtml_append_text is a time killer in loading logs, annoyingly
+ gtk_imhtml_append_text(imhtml, text,
+ GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_TITLE | GTK_IMHTML_NO_SCROLL |
+ (lv->flags & PURPLE_LOG_READ_NO_NEWLINE ? GTK_IMHTML_NO_NEWLINE : 0));
+
+ /* If we have something in the search bar, scroll to it in the current log */
+ if (lv->search != NULL) {
+ gtk_imhtml_search_clear(imhtml);
+ g_idle_add(search_find_cb, pidgin_log_data);
+ } else
+ pidgin_log_data_free(pidgin_log_data);
+
+ g_clear_error(&err);
+ } else
+ pidgin_log_data_free(pidgin_log_data);
+}
+
+static void
+log_select_cb(GtkTreeSelection *sel, PidginLogViewerEx *lv_ex)
+{
+ _pidgin_log_data *pidgin_log_data;
+ PidginLogViewer *lv = lv_ex->lv;
PurpleLog *log = NULL;
- PurpleLogReadFlags flags;
- char *read = NULL;
+ GtkTreeModel *model = GTK_TREE_MODEL(lv->treestore);
+ GtkTreeIter iter;
+ GCancellable *cancel;
if (!gtk_tree_selection_get_selected(sel, &model, &iter))
return;
- val.g_type = 0;
- gtk_tree_model_get_value (model, &iter, 1, &val);
- log = g_value_get_pointer(&val);
- g_value_unset(&val);
+ lv_ex->selected = TRUE;
+ gtk_tree_model_get(model, &iter, 1, &log, -1);
if (log == NULL)
return;
- pidgin_set_cursor(viewer->window, GDK_WATCH);
-
if (log->type != PURPLE_LOG_SYSTEM) {
- char *title;
+ gchar *title;
+
if (log->type == PURPLE_LOG_CHAT)
title = g_strdup_printf(_("<span size='larger' weight='bold'>Conversation in %s on %s</span>"),
- log->name, log_get_date(log));
+ log->name, log_get_date(log));
else
title = g_strdup_printf(_("<span size='larger' weight='bold'>Conversation with %s on %s</span>"),
- log->name, log_get_date(log));
+ log->name, log_get_date(log));
- gtk_label_set_markup(GTK_LABEL(viewer->label), title);
+ gtk_label_set_markup(GTK_LABEL(lv->label), title);
g_free(title);
}
- read = purple_log_read(log, &flags);
- viewer->flags = flags;
+ pidgin_log_data = g_new0(_pidgin_log_data, 1);
+ pidgin_log_data->is_window_open = TRUE;
+ pidgin_log_data->count = 1;
+ pidgin_log_data->log_viewer = lv_ex;
+ pidgin_log_data->log = log;
+ pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv->window,
+ "destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
- gtk_imhtml_clear(GTK_IMHTML(viewer->imhtml));
- gtk_imhtml_set_protocol_name(GTK_IMHTML(viewer->imhtml),
- purple_account_get_protocol_name(log->account));
+ cancel = g_cancellable_new();
+ pidgin_log_viewer_ex_set_read_cancel(lv_ex, cancel);
- purple_signal_emit(pidgin_log_get_handle(), "log-displaying", viewer, log);
+#if GTK_CHECK_VERSION(2, 20, 0)
+ gtk_widget_hide(lv->imhtml);
+ gtk_widget_show(lv_ex->spinner);
+#endif
- gtk_imhtml_append_text(GTK_IMHTML(viewer->imhtml), read,
- GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_TITLE | GTK_IMHTML_NO_SCROLL |
- ((flags & PURPLE_LOG_READ_NO_NEWLINE) ? GTK_IMHTML_NO_NEWLINE : 0));
- g_free(read);
+ purple_log_read_async(log, &lv->flags, G_PRIORITY_DEFAULT, cancel,
+ pidgin_log_read_cb, pidgin_log_data);
- if (viewer->search != NULL) {
- guint source;
- gtk_imhtml_search_clear(GTK_IMHTML(viewer->imhtml));
- source = g_idle_add(search_find_cb, viewer);
- g_object_set_data_full(G_OBJECT(viewer->entry), "search-find-cb",
- GINT_TO_POINTER(source), (GDestroyNotify)g_source_remove);
- }
-
- pidgin_clear_cursor(viewer->window);
+ g_object_unref(cancel);
}
/* I want to make this smarter, but haven't come up with a cool algorithm to do so, yet.
@@ -489,26 +881,29 @@ static void log_select_cb(GtkTreeSelecti
*
* For now, I'll just make it a flat list.
*/
-static void populate_log_tree(PidginLogViewer *lv)
+static void
+populate_log_tree(PidginLogViewerEx *lv_ex)
/* Logs are made from trees in real life.
This is a tree made from logs */
{
- const char *month;
- char prev_top_month[30] = "";
+ PurpleLog *log;
+ PidginLogViewer *lv = lv_ex->lv;
GtkTreeIter toplevel, child;
- GList *logs = lv->logs;
+ GList *logs;
+ const gchar *month;
+ gchar prev_top_month[30] = "";
- while (logs != NULL) {
- PurpleLog *log = logs->data;
+ gtk_tree_store_clear(lv->treestore);
- month = purple_utf8_strftime(_("%B %Y"),
- log->tm ? log->tm : localtime(&log->time));
+ for (logs = lv->logs; logs != NULL; logs = g_list_next(logs)) {
+ log = logs->data;
- if (strcmp(month, prev_top_month) != 0)
- {
+ month = purple_utf8_strftime(_("%B %Y"), log->tm ? log->tm : localtime(&log->time));
+
+ if (strcmp(month, prev_top_month) != 0) {
/* top level */
gtk_tree_store_append(lv->treestore, &toplevel, NULL);
- gtk_tree_store_set(lv->treestore, &toplevel, 0, month, 1, NULL, -1);
+ gtk_tree_store_set(lv->treestore, &toplevel, 0, month, 1, NULL, 2, log->tm, -1);
strncpy(prev_top_month, month, sizeof(prev_top_month));
}
@@ -516,87 +911,57 @@ static void populate_log_tree(PidginLogV
/* sub */
gtk_tree_store_append(lv->treestore, &child, &toplevel);
gtk_tree_store_set(lv->treestore, &child,
- 0, log_get_date(log),
- 1, log,
- -1);
-
- logs = logs->next;
+ 0, log_get_date(log), 1, log, -1);
}
}
-static PidginLogViewer *display_log_viewer(struct log_viewer_hash_t *ht, GList *logs,
- const char *title, GtkWidget *icon, int log_size)
+static PidginLogViewerEx *
+display_log_viewer_nonblocking(log_viewer_hash_t *ht,
+ const gchar *title, GtkWidget *icon, gboolean need_log_size)
{
+ PidginLogViewerEx *lv_ex;
PidginLogViewer *lv;
- GtkWidget *title_box;
- char *text;
- GtkWidget *pane;
- GtkWidget *sw;
GtkCellRenderer *rend;
GtkTreeViewColumn *col;
GtkTreeSelection *sel;
- GtkWidget *vbox;
- GtkWidget *frame;
- GtkWidget *hbox;
- GtkWidget *find_button;
- GtkWidget *size_label;
+ GtkWidget *title_box, *pane, *sw, *frame, *find_button, *content_area, *vbox, *hbox;
+ gchar *text;
- if (logs == NULL)
- {
- /* No logs were found. */
- const char *log_preferences = NULL;
-
- if (ht == NULL) {
- if (!purple_prefs_get_bool("/purple/logging/log_system"))
- log_preferences = _("System events will only be logged if the \"Log all status changes to system log\" preference is enabled.");
- } else {
- if (ht->type == PURPLE_LOG_IM) {
- if (!purple_prefs_get_bool("/purple/logging/log_ims"))
- log_preferences = _("Instant messages will only be logged if the \"Log all instant messages\" preference is enabled.");
- } else if (ht->type == PURPLE_LOG_CHAT) {
- if (!purple_prefs_get_bool("/purple/logging/log_chats"))
- log_preferences = _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
- }
- g_free(ht->buddyname);
- g_free(ht);
- }
-
- if(icon != NULL)
- gtk_widget_destroy(icon);
-
- purple_notify_info(NULL, title, _("No logs were found"), log_preferences);
- return NULL;
- }
-
+ lv_ex = g_new0(PidginLogViewerEx, 1);
lv = g_new0(PidginLogViewer, 1);
- lv->logs = logs;
+ lv->logs = NULL;
+ lv_ex->lv = lv;
- if (ht != NULL)
- g_hash_table_insert(log_viewers, ht, lv);
+ if (ht != NULL) {
+ lv_ex->type = ht->type;
+ g_hash_table_insert(log_viewers, ht, lv_ex);
+ } else
+ lv_ex->type = PURPLE_LOG_SYSTEM;
/* Window ***********/
lv->window = gtk_dialog_new_with_buttons(title, NULL, 0,
- GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
-#ifdef _WIN32
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
+ content_area = gtk_dialog_get_content_area(GTK_DIALOG(lv->window));
+
+#ifdef G_OS_WIN32
/* Steal the "HELP" response and use it to trigger browsing to the logs folder */
gtk_dialog_add_button(GTK_DIALOG(lv->window), _("_Browse logs folder"), GTK_RESPONSE_HELP);
#endif
gtk_container_set_border_width (GTK_CONTAINER(lv->window), PIDGIN_HIG_BOX_SPACE);
gtk_dialog_set_has_separator(GTK_DIALOG(lv->window), FALSE);
- gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(lv->window)->vbox), 0);
- g_signal_connect(G_OBJECT(lv->window), "response",
- G_CALLBACK(destroy_cb), ht);
+ gtk_box_set_spacing(GTK_BOX(content_area), 0);
+ g_signal_connect(lv->window, "response", G_CALLBACK(destroy_cb), ht);
gtk_window_set_role(GTK_WINDOW(lv->window), "log_viewer");
/* Icon *************/
if (icon != NULL) {
title_box = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
gtk_container_set_border_width(GTK_CONTAINER(title_box), PIDGIN_HIG_BOX_SPACE);
- gtk_box_pack_start(GTK_BOX(GTK_DIALOG(lv->window)->vbox), title_box, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(content_area), title_box, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(title_box), icon, FALSE, FALSE, 0);
} else
- title_box = GTK_DIALOG(lv->window)->vbox;
+ title_box = content_area;
/* Label ************/
lv->label = gtk_label_new(NULL);
@@ -611,47 +976,43 @@ static PidginLogViewer *display_log_view
/* Pane *************/
pane = gtk_hpaned_new();
gtk_container_set_border_width(GTK_CONTAINER(pane), PIDGIN_HIG_BOX_SPACE);
- gtk_box_pack_start(GTK_BOX(GTK_DIALOG(lv->window)->vbox), pane, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(content_area), pane, TRUE, TRUE, 0);
/* List *************/
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
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 (2, G_TYPE_STRING, G_TYPE_POINTER);
- lv->treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (lv->treestore));
- g_object_unref(G_OBJECT(lv->treestore));
+
+ lv->treestore = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, PURPLE_STRUCT_TM_GET_TYPE);
+ 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);
+ g_object_unref(lv->treestore);
+
rend = gtk_cell_renderer_text_new();
col = gtk_tree_view_column_new_with_attributes ("time", rend, "markup", 0, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(lv->treeview), col);
- gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (lv->treeview), FALSE);
gtk_container_add (GTK_CONTAINER (sw), lv->treeview);
- populate_log_tree(lv);
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(lv->treeview));
- sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (lv->treeview));
- g_signal_connect (G_OBJECT (sel), "changed",
- G_CALLBACK (log_select_cb),
- lv);
- g_signal_connect (G_OBJECT(lv->treeview), "row-activated",
- G_CALLBACK(log_row_activated_cb),
- lv);
- pidgin_set_accessible_label(lv->treeview, lv->label);
+ g_signal_connect(sel, "changed", G_CALLBACK (log_select_cb), lv_ex);
+ g_signal_connect(lv->treeview, "row-activated", G_CALLBACK(log_row_activated_cb), NULL);
+ g_signal_connect(lv->treeview, "button-press-event", G_CALLBACK(log_button_press_cb), lv_ex);
+ g_signal_connect(lv->treeview, "popup-menu", G_CALLBACK(log_popup_menu_cb), lv_ex);
- g_signal_connect(lv->treeview, "button-press-event", G_CALLBACK(log_button_press_cb), lv);
- g_signal_connect(lv->treeview, "popup-menu", G_CALLBACK(log_popup_menu_cb), lv);
-
/* Log size ************/
- if(log_size) {
- char *sz_txt = purple_str_size_to_units(log_size);
- text = g_strdup_printf("<span weight='bold'>%s</span> %s", _("Total log size:"), sz_txt);
- size_label = gtk_label_new(NULL);
- gtk_label_set_markup(GTK_LABEL(size_label), text);
- /* gtk_paned_add1(GTK_PANED(pane), size_label); */
- gtk_misc_set_alignment(GTK_MISC(size_label), 0, 0);
- gtk_box_pack_end(GTK_BOX(GTK_DIALOG(lv->window)->vbox), size_label, FALSE, FALSE, 0);
- g_free(sz_txt);
+ if (need_log_size) {
+ lv->size_label = gtk_label_new(NULL);
+ lv_ex->size = 0;
+
+ text = g_strdup_printf("<span weight='bold'>%s</span> %s", _("Total log size:"), _("calculating..."));
+ gtk_label_set_markup(GTK_LABEL(lv->size_label), text);
g_free(text);
+
+ gtk_misc_set_alignment(GTK_MISC(lv->size_label), 0, 0);
+ gtk_box_pack_end(GTK_BOX(content_area), lv->size_label, FALSE, FALSE, 0);
}
/* A fancy little box ************/
@@ -665,122 +1026,413 @@ static PidginLogViewer *display_log_view
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
gtk_widget_show(frame);
+#if GTK_CHECK_VERSION(2, 20, 0)
+ lv_ex->spinner = gtk_spinner_new();
+ gtk_spinner_start(GTK_SPINNER(lv_ex->spinner));
+ gtk_box_pack_start(GTK_BOX(vbox), lv_ex->spinner, TRUE, FALSE, 0);
+#endif
+
/* Search box **********/
hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
lv->entry = gtk_entry_new();
+ find_button = gtk_button_new_from_stock(GTK_STOCK_FIND);
+
gtk_box_pack_start(GTK_BOX(hbox), lv->entry, TRUE, TRUE, 0);
- find_button = gtk_button_new_from_stock(GTK_STOCK_FIND);
gtk_box_pack_start(GTK_BOX(hbox), find_button, FALSE, FALSE, 0);
- g_signal_connect(GTK_ENTRY(lv->entry), "activate", G_CALLBACK(search_cb), lv);
- g_signal_connect(GTK_BUTTON(find_button), "clicked", G_CALLBACK(search_cb), lv);
- select_first_log(lv);
+ g_signal_connect(lv->entry, "activate", G_CALLBACK(search_cb), lv_ex);
+ g_signal_connect(find_button, "clicked", G_CALLBACK(search_cb), lv_ex);
+ /* Progress bars **********/
+ lv_ex->list_bar = gtk_progress_bar_new();
+ lv_ex->search_bar = gtk_progress_bar_new();
+
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(lv_ex->list_bar), _("Waiting for logs ..."));
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(lv_ex->search_bar), _("Searching logs ..."));
+
+ gtk_box_pack_start(GTK_BOX(vbox), lv_ex->list_bar, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), lv_ex->search_bar, FALSE, FALSE, 0);
+
gtk_widget_show_all(lv->window);
+ gtk_widget_hide(lv_ex->search_bar);
+#if GTK_CHECK_VERSION(2, 20, 0)
+ gtk_widget_hide(lv_ex->spinner);
+#endif
- return lv;
+ lv_ex->read_cancel = NULL;
+ lv_ex->search_cancel = NULL;
+ lv_ex->list_cancel = NULL;
+
+ lv_ex->selected = FALSE;
+
+ return lv_ex;
}
-void pidgin_log_show(PurpleLogType type, const char *buddyname, PurpleAccount *account) {
- struct log_viewer_hash_t *ht;
- PidginLogViewer *lv = NULL;
- const char *name = buddyname;
- char *title;
+static void
+insert_log_viewer_log(PidginLogViewerEx *lv_ex, PurpleLog *log)
+{
+ /* It runs about twice as quick to insert the logs, rather than re-populate the entire tree
+
+ This is also a trade-off though, as it means having to update both this function and
+ populate_log_tree if the tree format is to ever be changed
+ */
+ PurpleLog *child_log;
+ GtkTreeModel *model;
+ GtkTreeStore *store;
+ GtkTreeIter insertion, inserted_month, month_iter, child;
+ const gchar *month;
+ struct tm *tm;
+
+ store = lv_ex->lv->treestore;
+ model = GTK_TREE_MODEL(store);
+ month = purple_utf8_strftime(_("%B %Y"), log->tm ? log->tm : localtime(&log->time));
+
+ if (!gtk_tree_model_get_iter_first(model, &month_iter)) {
+ /* Appending the month */
+ gtk_tree_store_append(store, &inserted_month, NULL);
+ gtk_tree_store_set(store, &inserted_month, 0, month, 1, NULL, 2, log->tm, -1);
+
+ gtk_tree_store_append(store, &insertion, &inserted_month);
+ gtk_tree_store_set(store, &insertion, 0, log_get_date(log), 1, log, -1);
+
+ return;
+ }
+
+ do {
+ gtk_tree_model_get(model, &month_iter, 2, &tm, -1);
+
+ if (tm != NULL && tm->tm_mon == log->tm->tm_mon && tm->tm_year == log->tm->tm_year) {
+ if (!gtk_tree_model_iter_children(model, &child, &month_iter)) {
+ /* Appending the log to the currently selected month */
+ gtk_tree_store_append(store, &insertion, &month_iter);
+ gtk_tree_store_set(store, &insertion, 0, log_get_date(log), 1, log, -1);
+
+ return;
+ }
+
+ do {
+ gtk_tree_model_get(model, &child, 1, &child_log, -1);
+
+ if (log->time > child_log->time) {
+ /* Prepending the log to the currently selected month */
+ gtk_tree_store_insert_before(store, &insertion, &month_iter, &child);
+ gtk_tree_store_set(store, &insertion, 0, log_get_date(log), 1, log, -1);
+
+ return;
+ }
+
+ } while (gtk_tree_model_iter_next(model, &child));
+
+ /* Appending the log to the currently selected month */
+ gtk_tree_store_append(store, &insertion, &month_iter);
+ gtk_tree_store_set(store, &insertion, 0, log_get_date(log), 1, log, -1);
+
+ return;
+ } else if (tm->tm_year < log->tm->tm_year ||
+ (tm->tm_year == log->tm->tm_year && tm->tm_mon < log->tm->tm_mon))
+ {
+ /* Prepending the month */
+ gtk_tree_store_insert_before(store, &inserted_month, NULL, &month_iter);
+ gtk_tree_store_set(store, &inserted_month, 0, month, 1, NULL, 2, log->tm, -1);
+
+ gtk_tree_store_append(store, &insertion, &inserted_month);
+ gtk_tree_store_set(store, &insertion, 0, log_get_date(log), 1, log, -1);
+
+ return;
+ }
+
+ } while (gtk_tree_model_iter_next(model, &month_iter));
+
+ /* Appending the month */
+ gtk_tree_store_append(store, &inserted_month, NULL);
+ gtk_tree_store_set(store, &inserted_month, 0, month, 1, NULL, 2, log->tm, -1);
+
+ gtk_tree_store_append(store, &insertion, &inserted_month);
+ gtk_tree_store_set(store, &insertion, 0, log_get_date(log), 1, log, -1);
+}
+
+static void
+append_log_viewer_logs(PidginLogViewerEx *lv_ex, GList *logs)
+{
+ PidginLogViewer *lv = lv_ex->lv;
+ GList *list;
+
+ logs = g_list_sort(logs, purple_log_compare);
+
+ for (list = logs; list != NULL; list = g_list_next(list))
+ insert_log_viewer_log(lv_ex, list->data);
+
+ lv->logs = g_list_concat(lv->logs, logs);
+ lv->logs = g_list_sort(lv->logs, purple_log_compare);
+}
+
+static void
+pidgin_log_done_cb(_pidgin_log_data *pidgin_log_data)
+{
+ if (pidgin_log_data->is_window_open) {
+ PidginLogViewerEx *lv_ex = pidgin_log_data->log_viewer;
+ PidginLogViewer *lv = lv_ex->lv;
+
+ gtk_widget_hide(lv_ex->list_bar);
+
+ if (lv->logs == NULL) {
+ /* No logs were found. */
+ const gchar *log_preferences = NULL;
+
+ if (lv_ex->type == PURPLE_LOG_SYSTEM) {
+ if (!purple_prefs_get_bool("/purple/logging/log_system"))
+ log_preferences = _("System events will only be logged if "
+ "the \"Log all status changes to system log\" "
+ "preference is enabled.");
+ } else if (lv_ex->type == PURPLE_LOG_IM) {
+ if (!purple_prefs_get_bool("/purple/logging/log_ims"))
+ log_preferences = _("Instant messages will only be logged "
+ "if the \"Log all instant messages\" "
+ "preference is enabled.");
+ } else if (lv_ex->type == PURPLE_LOG_CHAT) {
+ if (!purple_prefs_get_bool("/purple/logging/log_chats"))
+ log_preferences = _("Chats will only be logged if the "
+ "\"Log all chats\" preference is enabled.");
+ }
+
+ purple_notify_info(NULL, gtk_window_get_title(GTK_WINDOW(lv->window)),
+ _("No logs were found"), log_preferences);
+ gtk_dialog_response(GTK_DIALOG(lv->window), GTK_RESPONSE_CLOSE);
+ } else if (!lv_ex->selected)
+ select_first_log(lv_ex);
+ }
+
+ pidgin_log_data_free(pidgin_log_data);
+}
+
+static void
+pidgin_log_viewer_update_list_bar(_pidgin_log_data *pidgin_log_data)
+{
+ if (pidgin_log_data->is_window_open) {
+ PidginLogViewerEx *lv_ex = pidgin_log_data->log_viewer;
+ gdouble fraction;
+
+ fraction = (gdouble) pidgin_log_data->count /
+ (gdouble) pidgin_log_data->total;
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(lv_ex->list_bar), 1.0 - fraction);
+ }
+}
+
+static void
+pidgin_log_viewer_update_search_bar(_pidgin_log_data *pidgin_log_data)
+{
+ if (pidgin_log_data->is_window_open) {
+ PidginLogViewerEx *lv_ex = pidgin_log_data->log_viewer;
+ gdouble fraction;
+
+ fraction = (gdouble) pidgin_log_data->count /
+ (gdouble) pidgin_log_data->total;
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(lv_ex->search_bar), 1.0 - fraction);
+ }
+}
+
+static void
+pidgin_log_size_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _pidgin_log_data *pidgin_log_data = userdata;
+ GError *err = NULL;
+ gssize log_size;
+
+ log_size = purple_log_get_total_size_finish(res, &err);
+ pidgin_log_data->count--;
+
+ if (log_size < 0) {
+ if (err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("gtklog", "Error getting total log size: %s\n",
+ err->message);
+ } else if (pidgin_log_data->is_window_open) {
+ PidginLogViewerEx *lv_ex = pidgin_log_data->log_viewer;
+ gchar *sz_txt, *text;
+ gsize total;
+
+ total = lv_ex->size;
+ total += log_size;
+
+ lv_ex->size = total;
+
+ sz_txt = purple_str_size_to_units(total);
+ text = g_strdup_printf("<span weight='bold'>%s</span> %s",
+ _("Total log size:"), sz_txt);
+
+ gtk_label_set_markup(GTK_LABEL(lv_ex->lv->size_label), text);
+
+ g_free(sz_txt);
+ g_free(text);
+ }
+
+ g_clear_error(&err);
+ pidgin_log_viewer_update_list_bar(pidgin_log_data);
+
+ if (pidgin_log_data->count < 1)
+ pidgin_log_done_cb(pidgin_log_data);
+}
+
+static void
+pidgin_log_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _pidgin_log_data *pidgin_log_data = userdata;
+ GError *err = NULL;
+ GList *list;
+
+ list = purple_log_get_logs_finish(res, &err);
+ pidgin_log_data->count--;
+
+ if (list == NULL) {
+ if (err != NULL && err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("gtklog", "Error getting logs: %s\n", err->message);
+ } else if (pidgin_log_data->is_window_open)
+ append_log_viewer_logs(pidgin_log_data->log_viewer, list);
+
+ g_clear_error(&err);
+ pidgin_log_viewer_update_search_bar(pidgin_log_data);
+
+ if (pidgin_log_data->count < 1)
+ pidgin_log_done_cb(pidgin_log_data);
+}
+
+static void
+pidgin_log_system_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _pidgin_log_data *pidgin_log_data = userdata;
+ GError *err = NULL;
+ GList *list;
+
+ list = purple_log_get_system_logs_finish(res, &err);
+ pidgin_log_data->count--;
+
+ if (list == NULL) {
+ if (err != NULL && err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("gtklog", "Error getting system logs: %s\n", err->message);
+ } else if (pidgin_log_data->is_window_open)
+ append_log_viewer_logs(pidgin_log_data->log_viewer, list);
+
+ g_clear_error(&err);
+ pidgin_log_viewer_update_search_bar(pidgin_log_data);
+
+ if (pidgin_log_data->count < 1)
+ pidgin_log_done_cb(pidgin_log_data);
+}
+
+void
+pidgin_log_show(PurpleLogType type, const gchar *buddyname, PurpleAccount *account)
+{
+ log_viewer_hash_t *ht;
+ _pidgin_log_data *pidgin_log_data;
+ PidginLogViewerEx *lv_ex;
GdkPixbuf *prpl_icon;
+ GtkWidget *image;
+ GCancellable *cancel;
+ const gchar *name;
+ gchar *title;
- g_return_if_fail(account != NULL);
- g_return_if_fail(buddyname != NULL);
+ if (type != PURPLE_LOG_IM) {
+ g_return_if_fail(account != NULL);
+ g_return_if_fail(buddyname != NULL);
+ }
- ht = g_new0(struct log_viewer_hash_t, 1);
+ name = buddyname;
+ ht = g_new0(log_viewer_hash_t, 1);
ht->type = type;
ht->buddyname = g_strdup(buddyname);
ht->account = account;
- if (log_viewers == NULL) {
+ if (log_viewers == NULL)
log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
- } else if ((lv = g_hash_table_lookup(log_viewers, ht))) {
- gtk_window_present(GTK_WINDOW(lv->window));
+ else if ((lv_ex = g_hash_table_lookup(log_viewers, ht)) != NULL) {
+ gtk_window_present(GTK_WINDOW(lv_ex->lv->window));
+
g_free(ht->buddyname);
g_free(ht);
+
return;
}
if (type == PURPLE_LOG_CHAT) {
- PurpleChat *chat;
+ PurpleChat *chat = purple_blist_find_chat(account, buddyname);
- chat = purple_blist_find_chat(account, buddyname);
if (chat != NULL)
name = purple_chat_get_name(chat);
title = g_strdup_printf(_("Conversations in %s"), name);
} else {
- PurpleBuddy *buddy;
+ PurpleBuddy *buddy = purple_find_buddy(account, buddyname);
- buddy = purple_find_buddy(account, buddyname);
if (buddy != NULL)
name = purple_buddy_get_contact_alias(buddy);
title = g_strdup_printf(_("Conversations with %s"), name);
}
+ pidgin_log_data = g_new0(_pidgin_log_data, 1);
+ pidgin_log_data->is_window_open = TRUE;
+ pidgin_log_data->count = 2;
+
prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
+ image = gtk_image_new_from_pixbuf(prpl_icon);
+ lv_ex = pidgin_log_data->log_viewer =
+ display_log_viewer_nonblocking(ht, title, image, TRUE);
- display_log_viewer(ht, purple_log_get_logs(type, buddyname, account),
- title, gtk_image_new_from_pixbuf(prpl_icon),
- purple_log_get_total_size(type, buddyname, account));
+ g_free(title);
- if (prpl_icon)
+ if (prpl_icon != NULL)
g_object_unref(prpl_icon);
- g_free(title);
+
+ pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv_ex->lv->window,
+ "destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
+
+ cancel = g_cancellable_new();
+ pidgin_log_viewer_ex_set_list_cancel(lv_ex, cancel);
+
+ purple_log_get_logs_async(type, buddyname, account, G_PRIORITY_DEFAULT,
+ cancel, pidgin_log_list_cb, pidgin_log_data);
+ purple_log_get_total_size_async(type, buddyname, account, G_PRIORITY_DEFAULT,
+ cancel, pidgin_log_size_cb, pidgin_log_data);
+
+ g_object_unref(cancel);
}
-void pidgin_log_show_contact(PurpleContact *contact) {
- struct log_viewer_hash_t *ht;
+void
+pidgin_log_show_contact(PurpleContact *contact)
+{
+ log_viewer_hash_t *ht;
+ _pidgin_log_data *pidgin_log_data;
PurpleBlistNode *child;
- PidginLogViewer *lv = NULL;
- GList *logs = NULL;
+ PidginLogViewerEx *lv_ex;
+ GCancellable *cancel;
GdkPixbuf *pixbuf;
GtkWidget *image;
- const char *name = NULL;
- char *title;
- int total_log_size = 0;
+ const gchar *name = NULL;
+ gchar *title;
g_return_if_fail(contact != NULL);
- ht = g_new0(struct log_viewer_hash_t, 1);
+ ht = g_new0(log_viewer_hash_t, 1);
ht->type = PURPLE_LOG_IM;
ht->contact = contact;
if (log_viewers == NULL) {
log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
- } else if ((lv = g_hash_table_lookup(log_viewers, ht))) {
- gtk_window_present(GTK_WINDOW(lv->window));
- g_free(ht);
- return;
- }
+ } else if ((lv_ex = g_hash_table_lookup(log_viewers, ht)) != NULL) {
+ gtk_window_present(GTK_WINDOW(lv_ex->lv->window));
- for (child = purple_blist_node_get_first_child((PurpleBlistNode*)contact) ;
- child != NULL ;
- child = purple_blist_node_get_sibling_next(child)) {
- const char *buddy_name;
- PurpleAccount *account;
+ g_free(ht);
- if (!PURPLE_BLIST_NODE_IS_BUDDY(child))
- continue;
-
- buddy_name = purple_buddy_get_name((PurpleBuddy *)child);
- account = purple_buddy_get_account((PurpleBuddy *)child);
- logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, buddy_name, account), logs);
- total_log_size += purple_log_get_total_size(PURPLE_LOG_IM, buddy_name, account);
+ return;
}
- logs = g_list_sort(logs, purple_log_compare);
image = gtk_image_new();
pixbuf = gtk_widget_render_icon(image, PIDGIN_STOCK_STATUS_PERSON,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL), "GtkWindow");
- if (pixbuf) {
+ gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL), "GtkWindow");
+
+ if (pixbuf != NULL) {
gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
g_object_unref(pixbuf);
} else {
@@ -798,37 +1450,81 @@ void pidgin_log_show_contact(PurpleConta
* There is probably a better way to deal with this. */
if (name == NULL) {
if (contact->node.child != NULL && PURPLE_BLIST_NODE_IS_BUDDY(contact->node.child))
- name = purple_buddy_get_contact_alias((PurpleBuddy *) contact->node.child);
+ name = purple_buddy_get_contact_alias(PURPLE_BUDDY(contact->node.child));
if (name == NULL)
name = "";
}
+ pidgin_log_data = g_new0(_pidgin_log_data, 1);
+ pidgin_log_data->is_window_open = TRUE;
+
title = g_strdup_printf(_("Conversations with %s"), name);
- display_log_viewer(ht, logs, title, image, total_log_size);
+ lv_ex = pidgin_log_data->log_viewer = display_log_viewer_nonblocking(ht, title, image, TRUE);
+
g_free(title);
+
+ pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(lv_ex->lv->window,
+ "destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
+
+ pidgin_log_data->count = pidgin_log_data->total = 0;
+ cancel = g_cancellable_new();
+ pidgin_log_viewer_ex_set_list_cancel(lv_ex, cancel);
+
+ for (child = contact->node.child; child != NULL; child = child->next) {
+ if (PURPLE_BLIST_NODE_IS_BUDDY(child)) {
+ PurpleBuddy *buddy = PURPLE_BUDDY(child);
+
+ pidgin_log_data->count = (pidgin_log_data->total += 2);
+
+ purple_log_get_logs_async(PURPLE_LOG_IM, buddy->name, buddy->account,
+ G_PRIORITY_DEFAULT, cancel, pidgin_log_list_cb, pidgin_log_data);
+ purple_log_get_total_size_async(PURPLE_LOG_IM, buddy->name, buddy->account,
+ G_PRIORITY_DEFAULT, cancel, pidgin_log_size_cb, pidgin_log_data);
+ }
+ }
+
+ g_object_unref(cancel);
}
-void pidgin_syslog_show()
+void
+pidgin_syslog_show(void)
{
- GList *accounts = NULL;
- GList *logs = NULL;
+ _pidgin_log_data *pidgin_log_data;
+ PurpleAccount *account;
+ GCancellable *cancel;
+ GList *accounts;
if (syslog_viewer != NULL) {
- gtk_window_present(GTK_WINDOW(syslog_viewer->window));
+ gtk_window_present(GTK_WINDOW(syslog_viewer->lv->window));
return;
}
- for(accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) {
+ pidgin_log_data = g_new0(_pidgin_log_data, 1);
+ pidgin_log_data->is_window_open = TRUE;
+
+ syslog_viewer = pidgin_log_data->log_viewer = display_log_viewer_nonblocking(NULL,
+ _("System Log"), NULL, FALSE);
- PurpleAccount *account = (PurpleAccount *)accounts->data;
- if(purple_find_prpl(purple_account_get_protocol_id(account)) == NULL)
- continue;
+ pidgin_log_data->destroy_handler_id = g_signal_connect_swapped(syslog_viewer->lv->window,
+ "destroy", G_CALLBACK(pidgin_window_destroy_cb), pidgin_log_data);
- logs = g_list_concat(purple_log_get_system_logs(account), logs);
+ cancel = g_cancellable_new();
+ pidgin_log_viewer_ex_set_list_cancel(syslog_viewer, cancel);
+ pidgin_log_data->count = pidgin_log_data->total = 0;
+
+ for(accounts = purple_accounts_get_all(); accounts != NULL; accounts = g_list_next(accounts)) {
+ account = accounts->data;
+
+ if(purple_find_prpl(purple_account_get_protocol_id(account)) != NULL) {
+ pidgin_log_data->count++;
+ pidgin_log_data->total++;
+
+ purple_log_get_system_logs_async(account, G_PRIORITY_DEFAULT, cancel,
+ pidgin_log_system_list_cb, pidgin_log_data);
+ }
}
- logs = g_list_sort(logs, purple_log_compare);
- syslog_viewer = display_log_viewer(NULL, logs, _("System Log"), NULL, 0);
+ g_object_unref(cancel);
}
/****************************************************************************
@@ -843,17 +1539,16 @@ pidgin_log_get_handle(void)
return &handle;
}
-void pidgin_log_init(void)
+void
+pidgin_log_init(void)
{
void *handle = pidgin_log_get_handle();
purple_signal_register(handle, "log-displaying",
- purple_marshal_VOID__POINTER_POINTER,
- NULL, 2,
- purple_value_new(PURPLE_TYPE_BOXED,
- "PidginLogViewer *"),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_LOG));
+ purple_marshal_VOID__POINTER_POINTER,
+ NULL, 2,
+ purple_value_new(PURPLE_TYPE_BOXED, "PidginLogViewer *"),
+ purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_LOG));
}
void
============================================================
--- pidgin/gtklog.h beebb1eb384edcdea9fb0073e342b726f7dd685b
+++ pidgin/gtklog.h d2f68950e4d610a305124ca8efaeccc5f2b27839
@@ -32,54 +32,85 @@
#include "account.h"
+/** @copydoc _PidginLogViewer */
typedef struct _PidginLogViewer PidginLogViewer;
+/********************************************************
+ * DATA STRUCTURES **************************************
+ ********************************************************/
+
/**
* A GTK+ Log Viewer. You can look at logs with it.
*/
struct _PidginLogViewer {
- GList *logs; /**< The list of logs viewed in this viewer */
+ GList *logs; /**< The list of logs viewed in this viewer */
+ GtkWidget *window; /**< The viewer's window */
+ GtkTreeStore *treestore; /**< The treestore containing said logs */
+ GtkWidget *treeview; /**< The treeview representing said treestore */
+ GtkWidget *imhtml; /**< The imhtml to display said logs */
+ GtkWidget *entry; /**< The search entry, in which search terms are entered */
+ PurpleLogReadFlags flags; /**< The most recently used log read flags */
+ gchar *search; /**< The string currently being searched for */
+ GtkWidget *label; /**< The label at the top of the log viewer */
+ GtkWidget *size_label; /**< The label with total log size */
+};
- GtkWidget *window; /**< The viewer's window */
- GtkTreeStore *treestore; /**< The treestore containing said logs */
- GtkWidget *treeview; /**< The treeview representing said treestore */
- GtkWidget *imhtml; /**< The imhtml to display said logs */
- GtkWidget *entry; /**< The search entry, in which search terms
- * are entered */
- PurpleLogReadFlags flags; /**< The most recently used log flags */
- char *search; /**< The string currently being searched for */
- GtkWidget *label; /**< The label at the top of the log viewer */
-};
+G_BEGIN_DECLS
+/**************************************************************************/
+/** @name Log Viewer Creators */
+/**************************************************************************/
+/*@{*/
-void pidgin_log_show(PurpleLogType type, const char *buddyname, PurpleAccount *account);
+/**
+ * Displays the logs of a certain type for a buddy or chat on an account
+ *
+ * @param type: The #PurpleLogType
+ * @param buddyname: The buddy or chat name
+ * @param account: The account
+ */
+void pidgin_log_show(PurpleLogType type, const gchar *buddyname, PurpleAccount *account);
+
+/**
+ * Displays the logs for one contact
+ *
+ * @param contact: The #PurpleContact to display the logs of
+ */
void pidgin_log_show_contact(PurpleContact *contact);
+/**
+ * Displays the system log
+ */
void pidgin_syslog_show(void);
+/*@}*/
+
/**************************************************************************/
/** @name GTK+ Log Subsystem */
/**************************************************************************/
+
/*@{*/
/**
- * Initializes the GTK+ log subsystem.
+ * Initializes the GTK+ log subsystem
*/
void pidgin_log_init(void);
/**
- * Returns the GTK+ log subsystem handle.
+ * Gets the GTK+ log subsystem handle
*
- * @return The GTK+ log subsystem handle.
+ * @return The GTK+ log subsystem handle
*/
void *pidgin_log_get_handle(void);
/**
- * Uninitializes the GTK+ log subsystem.
+ * Uninitializes the GTK+ log subsystem
*/
void pidgin_log_uninit(void);
/*@}*/
+G_END_DECLS
+
#endif
============================================================
--- ChangeLog.API 044e3549da4591f8b8743864fafbcb1880b0ccb6
+++ ChangeLog.API d1d23d41da22fafe590ba716ce132f2e7672aa45
@@ -1,5 +1,25 @@ Pidgin and Finch: The Pimpin' Penguin IM
Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
+version 2.8.0 (??/??/????):
+ libpurple:
+ Added:
+ * Non-blocking Logging API
+ * purple_log_write_async, purple_log_write_finish
+ * purple_log_read_async, purple_log_read_finish
+ * purple_log_get_logs_async, purple_log_get_logs_finish
+ * purple_log_get_log_sets_async, purple_log_get_log_sets_finish
+ * purple_log_get_system_logs_async, purple_log_get_system_logs_finish
+ * purple_log_get_size_async, purple_log_get_size_finish
+ * purple_log_get_total_size_async, purple_log_get_total_size_finish
+ * purple_log_get_activity_score_async, purple_log_get_activity_score_finish
+ * purple_log_delete_async, purple_log_delete_finish
+ * purple_log_common_writer_async, purple_log_common_writer_finish
+ * purple_log_common_lister_async, purple_log_common_lister_finish
+ * purple_log_common_total_sizer_async, purple_log_common_total_sizer_finish
+ * purple_log_common_sizer_async, purple_log_common_sizer_finish
+ * purple_log_common_deleter_async, purple_log_common_deleter_finish
+ * purple_log_logger_get_all
+
version 2.7.9 (??/??/????):
* No changes
============================================================
--- finch/plugins/gnthistory.c 3f06a038dfdd7cb19025a1b4fbd4788cae8f92bc
+++ finch/plugins/gnthistory.c 0ceb2703077587e3417212852dd423e97da2ec0b
@@ -39,168 +39,411 @@
#define HISTORY_SIZE (4 * 1024)
-static void historize(PurpleConversation *c)
+typedef struct {
+ PurpleAccount *account;
+ PurpleConversation *conv;
+ PurpleLog *log;
+ PurpleLogReadFlags flags;
+ GCancellable *cancel;
+ const gchar *name;
+ const gchar *alias;
+ guint count;
+ gulong delete_handle;
+} _historize_callback_data;
+
+static void historize(PurpleConversation *);
+static gboolean _scroll_gnt_text_view_to_end(gpointer);
+static PurpleLog *get_last_log(GList *, PurpleLog *);
+static void historize_log_read_cb(GObject *, GAsyncResult *, gpointer);
+static void historize_log_list_cb(GObject *, GAsyncResult *, gpointer);
+static void historize_log_list_cb_done(_historize_callback_data *);
+static void historize_log_collector_cb(GObject *, GAsyncResult *, gpointer);
+static void historize_log_collector_cb_done(_historize_callback_data *);
+static void historize_stop(PurpleConversation *, gpointer);
+static void historize_log_free(_historize_callback_data *);
+static void history_prefs_check(PurplePlugin *);
+static void history_prefs_cb(const gchar *, PurplePrefType, gconstpointer, gpointer);
+static gboolean plugin_load(PurplePlugin *);
+static void init_plugin(PurplePlugin *);
+
+static void
+historize(PurpleConversation *c)
{
+ _historize_callback_data *callback_data;
PurpleAccount *account = purple_conversation_get_account(c);
- const char *name = purple_conversation_get_name(c);
- PurpleConversationType convtype;
- GList *logs = NULL;
- const char *alias = name;
- PurpleLogReadFlags flags;
- char *history;
- char *header;
- PurpleMessageFlags mflag;
+ PurpleConversationType convtype = purple_conversation_get_type(c);
+ FinchConv *fc = FINCH_CONV(c);
+ const gchar *name = purple_conversation_get_name(c), *alias = name;
- convtype = purple_conversation_get_type(c);
- if (convtype == PURPLE_CONV_TYPE_IM) {
- GSList *buddies;
- GSList *cur;
- FinchConv *fc = FINCH_CONV(c);
- if (fc->list && fc->list->next) /* We were already in the middle of a conversation. */
- return;
+ g_return_if_fail(fc != NULL);
+ g_return_if_fail(fc->list != NULL);
+ callback_data = g_new0(_historize_callback_data, 1);
+ callback_data->conv = c;
+ callback_data->alias = alias;
+ callback_data->name = name;
+ callback_data->account = account;
+ callback_data->cancel = g_cancellable_new();
+
+ if (convtype == PURPLE_CONV_TYPE_IM && fc->list->next == NULL) {
+ GSList *buddies, *cur;
+
/* If we're not logging, don't show anything.
* Otherwise, we might show a very old log. */
- if (!purple_prefs_get_bool("/purple/logging/log_ims"))
+ if (!purple_prefs_get_bool("/purple/logging/log_ims")) {
+ g_object_unref(callback_data->cancel);
+ g_free(callback_data);
+
return;
+ }
/* Find buddies for this conversation. */
buddies = purple_find_buddies(account, name);
/* If we found at least one buddy, save the first buddy's alias. */
if (buddies != NULL)
- alias = purple_buddy_get_contact_alias((PurpleBuddy *)buddies->data);
+ alias = purple_buddy_get_contact_alias(PURPLE_BUDDY(buddies->data));
- for (cur = buddies; cur != NULL; cur = cur->next) {
+ for (cur = buddies; cur != NULL; cur = g_slist_next(cur)) {
PurpleBlistNode *node = cur->data;
- if ((node != NULL) &&
- ((purple_blist_node_get_sibling_prev(node) != NULL) ||
- (purple_blist_node_get_sibling_next(node) != NULL))) {
+
+ if (node != NULL &&
+ (purple_blist_node_get_sibling_prev(node) != NULL ||
+ purple_blist_node_get_sibling_next(node) != NULL))
+ {
PurpleBlistNode *node2;
+ PurpleBlistNode *child = purple_blist_node_get_first_child(parent);
+ PurpleBlistNode *parent = purple_blist_node_get_parent(node);
- alias = purple_buddy_get_contact_alias((PurpleBuddy *)node);
+ alias = purple_buddy_get_contact_alias(PURPLE_BUDDY(node));
/* We've found a buddy that matches this conversation. It's part of a
* PurpleContact with more than one PurpleBuddy. Loop through the PurpleBuddies
* in the contact and get all the logs. */
- for (node2 = purple_blist_node_get_first_child(purple_blist_node_get_parent(node));
- node2 != NULL ; node2 = purple_blist_node_get_sibling_next(node2)) {
- logs = g_list_concat(
- purple_log_get_logs(PURPLE_LOG_IM,
- purple_buddy_get_name((PurpleBuddy *)node2),
- purple_buddy_get_account((PurpleBuddy *)node2)),
- logs);
+ for (node2 = child; node2 != NULL; node2 = purple_blist_node_get_sibling_next(node2))
+ {
+ PurpleBuddy *buddy = PURPLE_BUDDY(node2);
+
+ callback_data->count++;
+
+ purple_log_get_logs_async(PURPLE_LOG_IM, purple_buddy_get_name(buddy),
+ purple_buddy_get_account(buddy), G_PRIORITY_DEFAULT,
+ callback_data->cancel, historize_log_collector_cb, callback_data);
}
+
break;
}
}
- g_slist_free(buddies);
- if (logs == NULL)
- logs = purple_log_get_logs(PURPLE_LOG_IM, name, account);
- else
- logs = g_list_sort(logs, purple_log_compare);
+ g_slist_free(buddies);
} else if (convtype == PURPLE_CONV_TYPE_CHAT) {
/* If we're not logging, don't show anything.
* Otherwise, we might show a very old log. */
- if (!purple_prefs_get_bool("/purple/logging/log_chats"))
+ if (!purple_prefs_get_bool("/purple/logging/log_chats")) {
+ g_object_unref(callback_data->cancel);
+ g_free(callback_data);
+
return;
+ }
- logs = purple_log_get_logs(PURPLE_LOG_CHAT, name, account);
+ callback_data->count = 1;
+
+ purple_log_get_logs_async(PURPLE_LOG_CHAT, name, account, G_PRIORITY_DEFAULT,
+ callback_data->cancel, historize_log_list_cb, callback_data);
}
- if (logs == NULL)
+ callback_data->delete_handle = purple_signal_connect(purple_conversations_get_handle(),
+ "deleting-conversation", c, PURPLE_CALLBACK(historize_stop), callback_data);
+}
+
+static gboolean
+_scroll_gnt_text_view_to_end(gpointer userdata)
+{
+ GntTextView *view = GNT_TEXT_VIEW(userdata);
+
+ gnt_text_view_scroll(view, 0);
+ g_object_unref(view);
+
+ return FALSE;
+}
+
+static PurpleLog *
+get_last_log(GList *list, PurpleLog *last_log)
+{
+ GList *node;
+
+ if (last_log == NULL) {
+ last_log = list->data;
+ node = g_list_next(list);
+ } else
+ node = list;
+
+ for( ; node; node = g_list_next(node))
+ if (purple_log_compare(last_log, node->data) > 0) {
+ purple_log_free(last_log);
+ last_log = node->data;
+ } else
+ purple_log_free(node->data);
+
+ g_list_free(list);
+
+ return last_log;
+}
+
+static void
+historize_log_read_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _historize_callback_data *callback_data = userdata;
+ PurpleConversation *c = callback_data->conv;
+ PurpleLog *log = callback_data->log;
+ PurpleMessageFlags options;
+ FinchConv *fc = FINCH_CONV(callback_data->conv);
+ GntTextView *view = GNT_TEXT_VIEW(fc->tv);
+ GError *err = NULL;
+ GString *buffer = view->string;
+ const gchar *header_date;
+ gchar *text, *escaped_alias, *header, *full_header, *text_backup;
+
+ if (res == NULL) {
+ callback_data->count--;
+
+ if (callback_data->count < 1)
+ historize_log_free(callback_data);
+
return;
+ }
- mflag = PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_DELAYED;
- history = purple_log_read((PurpleLog*)logs->data, &flags);
+ options = PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_DELAYED;
- header = g_strdup_printf(_("<b>Conversation with %s on %s:</b><br>"), alias,
- purple_date_format_full(localtime(&((PurpleLog *)logs->data)->time)));
- purple_conversation_write(c, "", header, mflag, time(NULL));
+ if (buffer != NULL && *buffer->str) {
+ text_backup = strdup(buffer->str);
+ gnt_text_view_clear(view);
+ } else
+ text_backup = NULL;
+
+ if (buffer->len > 0)
+ purple_conversation_write(c, "", "<hr>", options, time(NULL));
+
+ if (log->tm)
+ header_date = purple_date_format_full(log->tm);
+ else
+ header_date = purple_date_format_full(localtime(&log->time));
+
+ escaped_alias = g_markup_escape_text(callback_data->alias, -1);
+ full_header = g_strdup_printf(_("Conversation with %s on %s"), escaped_alias, header_date);
+ header = g_strdup_printf("<b>%s:</b><br>", full_header);
+
+ purple_conversation_write(c, "", header, options, time(NULL));
+
g_free(header);
+ g_free(escaped_alias);
+ g_free(full_header);
- if (flags & PURPLE_LOG_READ_NO_NEWLINE)
- purple_str_strip_char(history, '\n');
- purple_conversation_write(c, "", history, mflag, time(NULL));
- g_free(history);
+ text = purple_log_read_finish(log, res, &err);
- purple_conversation_write(c, "", "<hr>", mflag, time(NULL));
+ if (text == NULL)
+ text = err->message;
+ else
+ g_strchomp(text);
- g_list_foreach(logs, (GFunc)purple_log_free, NULL);
- g_list_free(logs);
+ if (callback_data->flags & PURPLE_LOG_READ_NO_NEWLINE)
+ purple_str_strip_char(text, '\n');
+
+ purple_conversation_write(c, "", text, options, time(NULL));
+ purple_conversation_write(c, "", "<hr>", options, time(NULL));
+
+ if (text_backup != NULL) {
+ purple_conversation_write(c, "", text_backup, PURPLE_MESSAGE_NO_LOG, time(NULL));
+ g_free(text_backup);
+ }
+
+ g_idle_add(_scroll_gnt_text_view_to_end, g_object_ref(view));
+
+ g_clear_error(&err);
+
+ callback_data->count--;
+
+ if (callback_data->count < 1)
+ historize_log_free(callback_data);
}
static void
+historize_log_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _historize_callback_data *callback_data = userdata;
+ GList *list;
+ GError *err = NULL;
+
+ if (res == NULL) {
+ callback_data->count--;
+
+ if (callback_data->count < 1)
+ historize_log_list_cb_done(callback_data);
+
+ return;
+ }
+
+ list = purple_log_get_logs_finish(res, &err);
+
+ if (list != NULL)
+ callback_data->log = get_last_log(list, callback_data->log);
+ else if (err != NULL && err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("gnthistory", "%s\n", err->message);
+
+ g_clear_error(&err);
+
+ callback_data->count--;
+
+ if (callback_data->count < 1)
+ historize_log_list_cb_done(callback_data);
+}
+
+static void
+historize_log_list_cb_done(_historize_callback_data *callback_data)
+{
+ callback_data->count++;
+
+ if (callback_data->log != NULL)
+ purple_log_read_async(callback_data->log, &callback_data->flags,
+ G_PRIORITY_DEFAULT, callback_data->cancel, historize_log_read_cb,
+ callback_data);
+ else
+ historize_log_read_cb(NULL, NULL, callback_data);
+}
+
+static void
+historize_log_collector_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _historize_callback_data *callback_data = userdata;
+ GList *list;
+ GError *err = NULL;
+
+ list = purple_log_get_logs_finish(res, &err);
+
+ if (list != NULL)
+ callback_data->log = get_last_log(list, callback_data->log);
+ else if (err != NULL && err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("gnthistory", "%s\n", err->message);
+
+ g_clear_error(&err);
+
+ callback_data->count--;
+
+ if (callback_data->count < 1)
+ historize_log_collector_cb_done(userdata);
+}
+
+static void
+historize_log_collector_cb_done(_historize_callback_data *callback_data)
+{
+ callback_data->count++;
+
+ if (callback_data->log == NULL)
+ purple_log_get_logs_async(PURPLE_LOG_IM, callback_data->name,
+ callback_data->account, G_PRIORITY_DEFAULT, callback_data->cancel,
+ historize_log_list_cb, callback_data);
+ else
+ historize_log_list_cb(NULL, NULL, callback_data);
+}
+
+static void
+historize_stop(PurpleConversation *c, gpointer userdata)
+{
+ _historize_callback_data *callback_data = userdata;
+
+ if (callback_data->cancel != NULL)
+ g_cancellable_cancel(callback_data->cancel);
+
+ purple_signal_disconnect(purple_conversations_get_handle(), "deleting-conversation",
+ c, PURPLE_CALLBACK(historize_stop));
+}
+
+static void
+historize_log_free(_historize_callback_data *callback_data)
+{
+ if (callback_data->log != NULL)
+ purple_log_free(callback_data->log);
+
+ // Why on earth does pidgin give us gulong to refer to our connected signal if it
+ // offers no API to disconnect it with!?
+ purple_signal_disconnect(purple_conversations_get_handle(), "deleting-conversation",
+ callback_data->conv, PURPLE_CALLBACK(historize_stop));
+
+ g_object_unref(callback_data->cancel);
+
+ g_free(callback_data);
+}
+
+static void
history_prefs_check(PurplePlugin *plugin)
{
if (!purple_prefs_get_bool("/purple/logging/log_ims") &&
- !purple_prefs_get_bool("/purple/logging/log_chats"))
+ !purple_prefs_get_bool("/purple/logging/log_chats"))
{
+ PurpleRequestField *field = purple_request_field_list_new("/purple/logging/format", _("Log format"));
PurpleRequestFields *fields = purple_request_fields_new();
- PurpleRequestFieldGroup *group;
- PurpleRequestField *field;
+ PurpleRequestFieldGroup *group = purple_request_field_group_new(_("Logging"));
+ GList *list = purple_log_logger_get_options();
+ const gchar *system = purple_prefs_get_string("/purple/logging/format");
+ gint iter;
struct {
- const char *pref;
- const char *label;
+ const gchar *pref;
+ const gchar *label;
} prefs[] = {
{"/purple/logging/log_ims", N_("Log IMs")},
{"/purple/logging/log_chats", N_("Log chats")},
{NULL, NULL}
};
- int iter;
- GList *list = purple_log_logger_get_options();
- const char *system = purple_prefs_get_string("/purple/logging/format");
- group = purple_request_field_group_new(_("Logging"));
+ for ( ; list != NULL; list = g_list_delete_link(list, list)) {
+ const gchar *label = _(list->data);
- field = purple_request_field_list_new("/purple/logging/format", _("Log format"));
- while (list) {
- const char *label = _(list->data);
list = g_list_delete_link(list, list);
purple_request_field_list_add_icon(field, label, NULL, list->data);
+
if (system && strcmp(system, list->data) == 0)
purple_request_field_list_add_selected(field, label);
- list = g_list_delete_link(list, list);
}
+
purple_request_field_group_add_field(group, field);
for (iter = 0; prefs[iter].pref; iter++) {
field = purple_request_field_bool_new(prefs[iter].pref, _(prefs[iter].label),
- purple_prefs_get_bool(prefs[iter].pref));
+ purple_prefs_get_bool(prefs[iter].pref));
+
purple_request_field_group_add_field(group, field);
}
purple_request_fields_add_group(fields, group);
purple_request_fields(plugin, NULL, _("History Plugin Requires Logging"),
- _("Logging can be enabled from Tools -> Preferences -> Logging.\n\n"
- "Enabling logs for instant messages and/or chats will activate "
- "history for the same conversation type(s)."),
- fields,
- _("OK"), G_CALLBACK(finch_request_save_in_prefs),
- _("Cancel"), NULL,
- NULL, NULL, NULL, plugin);
+ _("Logging can be enabled from Tools -> Preferences -> Logging.\n\n"
+ "Enabling logs for instant messages and/or chats will activate "
+ "history for the same conversation type(s)."),
+ fields,
+ _("OK"), G_CALLBACK(finch_request_save_in_prefs),
+ _("Cancel"), NULL,
+ NULL, NULL, NULL, plugin);
}
}
-static void history_prefs_cb(const char *name, PurplePrefType type,
- gconstpointer val, gpointer data)
+static void
+history_prefs_cb(const gchar *name, PurplePrefType type, gconstpointer val, gpointer data)
{
- history_prefs_check((PurplePlugin *)data);
+ history_prefs_check((PurplePlugin *) data);
}
static gboolean
plugin_load(PurplePlugin *plugin)
{
purple_signal_connect(purple_conversations_get_handle(),
- "conversation-created",
- plugin, PURPLE_CALLBACK(historize), NULL);
+ "conversation-created",
+ plugin, PURPLE_CALLBACK(historize), NULL);
purple_prefs_connect_callback(plugin, "/purple/logging/log_ims",
- history_prefs_cb, plugin);
+ history_prefs_cb, plugin);
purple_prefs_connect_callback(plugin, "/purple/logging/log_chats",
- history_prefs_cb, plugin);
+ history_prefs_cb, plugin);
history_prefs_check(plugin);
============================================================
--- pidgin/Makefile.mingw 867b6c76a7ab966075f38718bb81a9ce5308a473
+++ pidgin/Makefile.mingw 730c0aa339502a6c46952ca9da4ca355e1bd84d5
@@ -116,6 +116,7 @@ PIDGIN_LIBS = \
-lintl \
-lglib-2.0 \
-lgobject-2.0 \
+ -lgio-2.0 \
-lgthread-2.0 \
-lpurple \
-lz \
============================================================
--- pidgin/plugins/Makefile.mingw eb4f0847480f78f03e28cee4247f552320baa6ec
+++ pidgin/plugins/Makefile.mingw eb914f003734a16f54e0984e598b182ec201bbcd
@@ -44,6 +44,7 @@ LIBS = -lgtk-win32-2.0 \
LIBS = -lgtk-win32-2.0 \
-lglib-2.0 \
-lgdk-win32-2.0 \
+ -lgio-2.0 \
-lgobject-2.0 \
-lgmodule-2.0 \
-lgdk_pixbuf-2.0 \
============================================================
--- libpurple/Makefile.mingw c00779a8a36018e900636774f2d2e7fcfe2890a9
+++ libpurple/Makefile.mingw e15167af874d0b95fb51b36f35d9748cbf8320b2
@@ -96,6 +96,7 @@ LIBS = \
## LIBRARIES
##
LIBS = \
+ -lgio-2.0 \
-lglib-2.0 \
-lgthread-2.0 \
-lgobject-2.0 \
============================================================
--- finch/gntlog.c aaa0c27889f1c7cba351913d53056e2e1b9aa76f
+++ finch/gntlog.c ed7282419ae6ff1bd595c802a14a6f00bd3d55ed
@@ -31,6 +31,7 @@
#include <gntbutton.h>
#include <gntentry.h>
#include <gntlabel.h>
+#include <gntprogressbar.h>
#include <gnttextview.h>
#include <gnttree.h>
#include <gntwindow.h>
@@ -44,41 +45,138 @@
#include "gntlog.h"
-static GHashTable *log_viewers = NULL;
-static void populate_log_tree(FinchLogViewer *lv);
-static FinchLogViewer *syslog_viewer = NULL;
+// FinchLogViewer members are stored here to avoid an ABI break, will be moved back in 3.x
+typedef struct {
+ FinchLogViewer *lv; /**< The rest of the struct */
+ gsize size; /**< The actual value of the total log size */
+ GntWidget *size_label; /**< The label of the size of the log */
+ GntWidget *list_bar; /**< The progress bar */
+ GntWidget *search_bar; /**< The progess bar for searches */
-struct log_viewer_hash_t {
- PurpleLogType type;
- char *username;
+ GCancellable *read_cancel; /**< A GCancellable to stop any reads */
+ GCancellable *search_cancel; /**< A GCancellable to stop any searches */
+ GCancellable *list_cancel; /**< A GCancellable to stop any folder listings */
+
+ PurpleLogType type; /**< The log type of the window */
+} FinchLogViewerEx;
+
+typedef struct {
+ FinchLogViewerEx *log_viewer;
+ PurpleLog *log;
+
+ gboolean is_window_open;
+ gulong destroy_handler_id;
+
+ guint count;
+ guint total;
+} _finch_log_data;
+
+typedef struct {
+ _finch_log_data *log_data;
+ PurpleLog *log;
+} _finch_search_callback_data;
+
+typedef struct {
PurpleAccount *account;
PurpleContact *contact;
-};
+ PurpleLogType type;
+ gchar *username;
+} log_viewer_hash_t;
-static guint log_viewer_hash(gconstpointer data)
+
+static void finch_log_data_free(_finch_log_data *);
+static void finch_window_destroy_cb(_finch_log_data *);
+static void finch_log_viewer_ex_set_read_cancel(FinchLogViewerEx *, GCancellable *);
+static void finch_log_viewer_ex_set_search_cancel(FinchLogViewerEx *, GCancellable *);
+static void finch_log_viewer_ex_set_list_cancel(FinchLogViewerEx *, GCancellable *);
+static guint log_viewer_hash(gconstpointer);
+static gboolean log_viewer_equal(gconstpointer, gconstpointer);
+static const gchar *log_get_date(PurpleLog *);
+static void finch_log_search_done_cb(_finch_log_data *);
+static void finch_log_search_cb(GObject *, GAsyncResult *, gpointer);
+static void search_cb(GntWidget *, FinchLogViewerEx *);
+static void destroy_cb(GntWidget *, log_viewer_hash_t *);
+static void finch_log_read_cb(GObject *, GAsyncResult *, gpointer);
+static void log_select_cb(GntWidget *, gpointer, gpointer, FinchLogViewerEx *);
+static void populate_log_tree(FinchLogViewerEx *);
+static FinchLogViewerEx *display_log_viewer_nonblocking(log_viewer_hash_t *,
+ const gchar *, gboolean);
+static void finch_log_done_cb(_finch_log_data *);
+static void finch_log_viewer_update_list_bar(_finch_log_data *);
+static void finch_log_viewer_update_search_bar(_finch_log_data *);
+static void finch_log_size_cb(GObject *, GAsyncResult *, gpointer);
+static void finch_log_list_cb(GObject *, GAsyncResult *, gpointer);
+static void finch_log_system_list_cb(GObject *, GAsyncResult *, gpointer);
+static void finch_log_sets_cb(GObject *, GAsyncResult *, gpointer);
+
+
+static GHashTable *log_viewers = NULL;
+static FinchLogViewerEx *syslog_viewer = NULL;
+
+
+static void
+finch_log_viewer_ex_set_read_cancel(FinchLogViewerEx *lv_ex, GCancellable *cancel)
{
- const struct log_viewer_hash_t *viewer = data;
+ if (lv_ex->read_cancel != NULL) {
+ g_cancellable_cancel(lv_ex->read_cancel);
+ g_object_unref(lv_ex->read_cancel);
+ }
+ if (cancel != NULL)
+ g_object_ref(cancel);
+
+ lv_ex->read_cancel = cancel;
+}
+
+static void
+finch_log_viewer_ex_set_search_cancel(FinchLogViewerEx *lv_ex, GCancellable *cancel)
+{
+ if (lv_ex->search_cancel != NULL) {
+ g_cancellable_cancel(lv_ex->search_cancel);
+ g_object_unref(lv_ex->search_cancel);
+ }
+
+ if (cancel != NULL)
+ g_object_ref(cancel);
+
+ lv_ex->search_cancel = cancel;
+}
+
+static void
+finch_log_viewer_ex_set_list_cancel(FinchLogViewerEx *lv_ex, GCancellable *cancel)
+{
+ if (lv_ex->list_cancel != NULL) {
+ g_cancellable_cancel(lv_ex->list_cancel);
+ g_object_unref(lv_ex->list_cancel);
+ }
+
+ if (cancel != NULL)
+ g_object_ref(cancel);
+
+ lv_ex->list_cancel = cancel;
+}
+
+static guint
+log_viewer_hash(gconstpointer data)
+{
+ const log_viewer_hash_t *viewer = data;
+
if (viewer->contact != NULL)
return g_direct_hash(viewer->contact);
- if (viewer->account) {
+ if (viewer->account)
return g_str_hash(viewer->username) +
g_str_hash(purple_account_get_username(viewer->account));
- }
return g_direct_hash(viewer);
}
-static gboolean log_viewer_equal(gconstpointer y, gconstpointer z)
+static gboolean
+log_viewer_equal(gconstpointer y, gconstpointer z)
{
- const struct log_viewer_hash_t *a, *b;
- int ret;
- char *normal;
+ const log_viewer_hash_t *a = y, *b = z;
+ gint ret;
- a = y;
- b = z;
-
if (a->contact != NULL) {
if (b->contact != NULL)
return (a->contact == b->contact);
@@ -90,7 +188,7 @@ static gboolean log_viewer_equal(gconstp
}
if (a->username && b->username) {
- normal = g_strdup(purple_normalize(a->account, a->username));
+ gchar *normal = g_strdup(purple_normalize(a->account, a->username));
ret = (a->account == b->account) &&
!strcmp(normal, purple_normalize(b->account, b->username));
g_free(normal);
@@ -101,7 +199,8 @@ static gboolean log_viewer_equal(gconstp
return ret;
}
-static const char *log_get_date(PurpleLog *log)
+static const gchar *
+log_get_date(PurpleLog *log)
{
if (log->tm)
return purple_date_format_full(log->tm);
@@ -109,51 +208,145 @@ static const char *log_get_date(PurpleLo
return purple_date_format_full(localtime(&log->time));
}
-static void search_cb(GntWidget *button, FinchLogViewer *lv)
+static void
+finch_log_data_free(_finch_log_data *data)
{
- const char *search_term = gnt_entry_get_text(GNT_ENTRY(lv->entry));
+ if (data->destroy_handler_id > 0)
+ g_signal_handler_disconnect(data->log_viewer->lv->window,
+ data->destroy_handler_id);
+
+ g_free(data);
+}
+
+static void
+finch_window_destroy_cb(_finch_log_data *data)
+{
+ data->destroy_handler_id = 0;
+ data->is_window_open = FALSE;
+}
+
+static void
+finch_log_search_done_cb(_finch_log_data *finch_log_data)
+{
+// if (finch_log_data->is_window_open) {
+// FinchLogViewerEx *lv_ex = finch_log_data->log_viewer;
+//
+// gnt_widget_hide(lv_ex->search_bar);
+// }
+
+ finch_log_data_free(finch_log_data);
+}
+
+static void
+finch_log_search_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _finch_search_callback_data *callback_data = userdata;
+ _finch_log_data *finch_log_data = callback_data->log_data;
+ PurpleLog *log = callback_data->log;
+ GError *err = NULL;
+ gchar *read;
+
+ read = purple_log_read_finish(log, res, &err);
+ finch_log_data->count--;
+
+ if (read == NULL) {
+ if (err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("gntlog", "%s", err->message);
+ } else if (finch_log_data->is_window_open) {
+ FinchLogViewer *lv = finch_log_data->log_viewer->lv;
+
+ if (*read && purple_strcasestr(read, lv->search))
+ gnt_tree_add_row_last(GNT_TREE(lv->tree), log,
+ gnt_tree_create_row(GNT_TREE(lv->tree),
+ log_get_date(log)),
+ NULL);
+ }
+
+ g_clear_error(&err);
+ finch_log_viewer_update_search_bar(finch_log_data);
+
+ if (finch_log_data->count < 1)
+ finch_log_search_done_cb(finch_log_data);
+
+ g_free(callback_data);
+}
+
+static void
+search_cb(GntWidget *button, FinchLogViewerEx *lv_ex)
+{
+ _finch_search_callback_data *callback_data;
+ _finch_log_data *finch_log_data;
+ FinchLogViewer *lv = lv_ex->lv;
+ GCancellable *cancel;
GList *logs;
+ const gchar *search_term = gnt_entry_get_text(GNT_ENTRY(lv->entry));
+ guint length;
if (!(*search_term)) {
- /* reset the tree */
- gnt_tree_remove_all(GNT_TREE(lv->tree));
+ /* Reset the tree, make sure we clear the search term first so that
+ log_select_cb knows how to tell the months from the individual logs
+ */
g_free(lv->search);
lv->search = NULL;
- populate_log_tree(lv);
+
+ finch_log_viewer_ex_set_search_cancel(lv_ex, NULL);
+ gnt_tree_remove_all(GNT_TREE(lv->tree));
+ populate_log_tree(lv_ex);
+
return;
}
- if (lv->search != NULL && !strcmp(lv->search, search_term)) {
+ if (lv->search != NULL && !strcmp(lv->search, search_term))
return;
- }
g_free(lv->search);
lv->search = g_strdup(search_term);
+ length = g_list_length(lv->logs);
+
+ if (length < 1)
+ return;
+
gnt_tree_remove_all(GNT_TREE(lv->tree));
gnt_text_view_clear(GNT_TEXT_VIEW(lv->text));
- for (logs = lv->logs; logs != NULL; logs = logs->next) {
- char *read = purple_log_read((PurpleLog*)logs->data, NULL);
- if (read && *read && purple_strcasestr(read, search_term)) {
- PurpleLog *log = logs->data;
+ finch_log_data = g_new0(_finch_log_data, 1);
+ finch_log_data->is_window_open = TRUE;
+ finch_log_data->log_viewer = lv_ex;
+ finch_log_data->count = finch_log_data->total = length;
+ finch_log_data->destroy_handler_id = g_signal_connect_swapped(lv->window,
+ "destroy", G_CALLBACK(finch_window_destroy_cb), finch_log_data);
- gnt_tree_add_row_last(GNT_TREE(lv->tree),
- log,
- gnt_tree_create_row(GNT_TREE(lv->tree), log_get_date(log)),
- NULL);
- }
- g_free(read);
+ gnt_progress_bar_set_fraction(GNT_PROGRESS_BAR(lv_ex->search_bar), 0.0);
+
+ cancel = g_cancellable_new();
+ finch_log_viewer_ex_set_search_cancel(lv_ex, cancel);
+
+ purple_debug_info("gntlog", "Searching %u logs\n", length);
+
+ for (logs = lv->logs; logs != NULL; logs = g_list_next(logs)) {
+ PurpleLog *log = logs->data;
+
+ callback_data = g_new0(_finch_search_callback_data, 1);
+ callback_data->log_data = finch_log_data;
+ callback_data->log = log;
+
+ purple_log_read_async(log, NULL, G_PRIORITY_DEFAULT_IDLE, cancel,
+ finch_log_search_cb, callback_data);
}
+ g_object_unref(cancel);
}
-static void destroy_cb(GntWidget *w, struct log_viewer_hash_t *ht)
+static void
+destroy_cb(GntWidget *w, log_viewer_hash_t *ht)
{
- FinchLogViewer *lv = syslog_viewer;
+ FinchLogViewerEx *lv_ex = syslog_viewer;
+ FinchLogViewer *lv;
+ GList *list;
if (ht != NULL) {
- lv = g_hash_table_lookup(log_viewers, ht);
+ lv_ex = g_hash_table_lookup(log_viewers, ht);
g_hash_table_remove(log_viewers, ht);
g_free(ht->username);
@@ -161,61 +354,128 @@ static void destroy_cb(GntWidget *w, str
} else
syslog_viewer = NULL;
+ lv = lv_ex->lv;
purple_request_close_with_handle(lv);
- g_list_foreach(lv->logs, (GFunc)purple_log_free, NULL);
+ for (list = lv->logs; list != NULL; list = g_list_next(list))
+ purple_log_free(list->data);
+
g_list_free(lv->logs);
g_free(lv->search);
g_free(lv);
+ if (lv_ex->read_cancel != NULL) {
+ g_cancellable_cancel(lv_ex->read_cancel);
+ g_object_unref(lv_ex->read_cancel);
+ }
+
+ if (lv_ex->search_cancel != NULL) {
+ g_cancellable_cancel(lv_ex->search_cancel);
+ g_object_unref(lv_ex->search_cancel);
+ }
+
+ if (lv_ex->list_cancel != NULL) {
+ g_cancellable_cancel(lv_ex->list_cancel);
+ g_object_unref(lv_ex->list_cancel);
+ }
+
+ g_free(lv_ex);
gnt_widget_destroy(w);
}
-static void log_select_cb(GntWidget *w, gpointer old, gpointer new, FinchLogViewer *viewer)
+static void
+finch_log_read_cb(GObject *object, GAsyncResult *res, gpointer userdata)
{
+ _finch_log_data *finch_log_data = userdata;
+
+ if (finch_log_data->is_window_open) {
+ PurpleLog *log = finch_log_data->log;
+ FinchLogViewerEx *lv_ex = finch_log_data->log_viewer;
+ FinchLogViewer *lv = lv_ex->lv;
+ GError *err = NULL;
+ gchar *text, *strip;
+
+ text = purple_log_read_finish(log, res, &err);
+
+ if (text == NULL) {
+ if (err->code == G_IO_ERROR_CANCELLED) {
+ finch_log_data_free(finch_log_data);
+ g_clear_error(&err);
+
+ return;
+ }
+
+ text = err->message;
+ }
+
+ if (!(lv->flags & PURPLE_LOG_READ_NO_NEWLINE)) {
+ gchar *newline = purple_strdup_withhtml(text);
+ strip = purple_markup_strip_html(newline);
+ g_free(newline);
+ } else
+ strip = purple_markup_strip_html(text);
+
+ gnt_text_view_clear(GNT_TEXT_VIEW(lv->text));
+
+ purple_signal_emit(finch_log_get_handle(), "log-displaying", lv, log);
+
+ gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(lv->text), strip,
+ GNT_TEXT_FLAG_NORMAL);
+
+ g_free(strip);
+ g_clear_error(&err);
+ }
+
+ finch_log_data_free(finch_log_data);
+}
+
+static void
+log_select_cb(GntWidget *w, gpointer old_row, gpointer new_row, FinchLogViewerEx *lv_ex)
+{
+ _finch_log_data *finch_log_data;
+ FinchLogViewer *lv = lv_ex->lv;
+ PurpleLog *log;
GntTree *tree = GNT_TREE(w);
- PurpleLog *log = NULL;
- PurpleLogReadFlags flags;
- char *read = NULL, *strip, *newline;
+ GCancellable *cancel;
- if (!viewer->search && !gnt_tree_get_parent_key(tree, new))
+ /* Make sure we don't select the month row */
+ if (lv->search == NULL && gnt_tree_get_parent_key(tree, new_row) == NULL)
return;
- log = (PurpleLog *)new;
+ log = new_row;
if (log == NULL)
return;
if (log->type != PURPLE_LOG_SYSTEM) {
- char *title;
+ gchar *title;
+
if (log->type == PURPLE_LOG_CHAT)
title = g_strdup_printf(_("Conversation in %s on %s"),
- log->name, log_get_date(log));
+ log->name, log_get_date(log));
else
title = g_strdup_printf(_("Conversation with %s on %s"),
- log->name, log_get_date(log));
+ log->name, log_get_date(log));
- gnt_label_set_text(GNT_LABEL(viewer->label), title);
+ gnt_label_set_text(GNT_LABEL(lv->label), title);
g_free(title);
}
- read = purple_log_read(log, &flags);
- if (flags != PURPLE_LOG_READ_NO_NEWLINE) {
- newline = purple_strdup_withhtml(read);
- strip = purple_markup_strip_html(newline);
- g_free(newline);
- } else {
- strip = purple_markup_strip_html(read);
- }
- viewer->flags = flags;
+ finch_log_data = g_new0(_finch_log_data, 1);
+ finch_log_data->is_window_open = TRUE;
+ finch_log_data->log_viewer = lv_ex;
+ finch_log_data->log = log;
+ finch_log_data->destroy_handler_id = g_signal_connect_swapped(lv->window,
+ "destroy", G_CALLBACK(finch_window_destroy_cb), finch_log_data);
- purple_signal_emit(finch_log_get_handle(), "log-displaying", viewer, log);
+ cancel = g_cancellable_new();
+ finch_log_viewer_ex_set_read_cancel(lv_ex, cancel);
- gnt_text_view_clear(GNT_TEXT_VIEW(viewer->text));
- gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(viewer->text), strip, GNT_TEXT_FLAG_NORMAL);
- g_free(read);
- g_free(strip);
+ purple_log_read_async(log, &lv->flags, G_PRIORITY_DEFAULT, cancel,
+ finch_log_read_cb, finch_log_data);
+
+ g_object_unref(cancel);
}
/* I want to make this smarter, but haven't come up with a cool algorithm to do so, yet.
@@ -224,106 +484,83 @@ static void log_select_cb(GntWidget *w,
*
* For now, I'll just make it a flat list.
*/
-static void populate_log_tree(FinchLogViewer *lv)
+static void
+populate_log_tree(FinchLogViewerEx *lv_ex)
/* Logs are made from trees in real life.
This is a tree made from logs */
{
- const char *pmonth;
- char *month = NULL;
- char prev_top_month[30] = "";
- GList *logs = lv->logs;
+ FinchLogViewer *lv = lv_ex->lv;
+ GList *logs;
+ const gchar *pmonth;
+ gchar *month = NULL, prev_top_month[30] = "";
- while (logs != NULL) {
+ for (logs = lv->logs; logs != NULL; logs = g_list_next(logs)) {
PurpleLog *log = logs->data;
pmonth = purple_utf8_strftime(_("%B %Y"),
- log->tm ? log->tm : localtime(&log->time));
+ log->tm ? log->tm : localtime(&log->time));
if (strcmp(pmonth, prev_top_month) != 0) {
month = g_strdup(pmonth);
+
/* top level */
- gnt_tree_add_row_last(GNT_TREE(lv->tree),
- month,
- gnt_tree_create_row(GNT_TREE(lv->tree), month),
- NULL);
+ gnt_tree_add_row_last(GNT_TREE(lv->tree), month,
+ gnt_tree_create_row(GNT_TREE(lv->tree), month),
+ NULL);
gnt_tree_set_expanded(GNT_TREE(lv->tree), month, FALSE);
strncpy(prev_top_month, month, sizeof(prev_top_month));
}
/* sub */
- gnt_tree_add_row_last(GNT_TREE(lv->tree),
- log,
- gnt_tree_create_row(GNT_TREE(lv->tree), log_get_date(log)),
- month);
-
- logs = logs->next;
+ gnt_tree_add_row_last(GNT_TREE(lv->tree), log,
+ gnt_tree_create_row(GNT_TREE(lv->tree), log_get_date(log)),
+ month);
}
}
-static FinchLogViewer *display_log_viewer(struct log_viewer_hash_t *ht, GList *logs,
- const char *title, int log_size)
+static FinchLogViewerEx *
+display_log_viewer_nonblocking(log_viewer_hash_t *ht,
+ const gchar *title, gboolean need_log_size)
{
+ FinchLogViewerEx *lv_ex;
FinchLogViewer *lv;
- char *text;
GntWidget *vbox, *hbox;
- GntWidget *size_label;
+ gchar *text;
- if (logs == NULL)
- {
- /* No logs were found. */
- const char *log_preferences = NULL;
-
- if (ht == NULL) {
- if (!purple_prefs_get_bool("/purple/logging/log_system"))
- log_preferences = _("System events will only be logged if the \"Log all status changes to system log\" preference is enabled.");
- } else {
- if (ht->type == PURPLE_LOG_IM) {
- if (!purple_prefs_get_bool("/purple/logging/log_ims"))
- log_preferences = _("Instant messages will only be logged if the \"Log all instant messages\" preference is enabled.");
- } else if (ht->type == PURPLE_LOG_CHAT) {
- if (!purple_prefs_get_bool("/purple/logging/log_chats"))
- log_preferences = _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
- }
- g_free(ht->username);
- g_free(ht);
- }
-
- purple_notify_info(NULL, title, _("No logs were found"), log_preferences);
- return NULL;
- }
-
+ lv_ex = g_new0(FinchLogViewerEx, 1);
lv = g_new0(FinchLogViewer, 1);
- lv->logs = logs;
+ lv->logs = NULL;
+ lv_ex->lv = lv;
- if (ht != NULL)
- g_hash_table_insert(log_viewers, ht, lv);
+ if (ht != NULL) {
+ lv_ex->type = ht->type;
+ g_hash_table_insert(log_viewers, ht, lv_ex);
+ } else
+ lv_ex->type = PURPLE_LOG_SYSTEM;
/* Window ***********/
lv->window = gnt_vwindow_new(FALSE);
gnt_box_set_title(GNT_BOX(lv->window), title);
gnt_box_set_toplevel(GNT_BOX(lv->window), TRUE);
gnt_box_set_pad(GNT_BOX(lv->window), 0);
- g_signal_connect(G_OBJECT(lv->window), "destroy", G_CALLBACK(destroy_cb), ht);
+ g_signal_connect(lv->window, "destroy", G_CALLBACK(destroy_cb), ht);
vbox = gnt_vbox_new(FALSE);
gnt_box_add_widget(GNT_BOX(lv->window), vbox);
/* Label ************/
- text = g_strdup_printf("%s", title);
- lv->label = gnt_label_new_with_format(text, GNT_TEXT_FLAG_BOLD);
- g_free(text);
+ lv->label = gnt_label_new_with_format(title, GNT_TEXT_FLAG_BOLD);
gnt_box_add_widget(GNT_BOX(vbox), lv->label);
hbox = gnt_hbox_new(FALSE);
gnt_box_add_widget(GNT_BOX(vbox), hbox);
+
/* List *************/
lv->tree = gnt_tree_new();
gnt_widget_set_size(lv->tree, 30, 0);
- populate_log_tree(lv);
- g_signal_connect (G_OBJECT(lv->tree), "selection-changed",
- G_CALLBACK (log_select_cb),
- lv);
+ g_signal_connect(lv->tree, "selection-changed",
+ G_CALLBACK(log_select_cb), lv_ex);
gnt_box_add_widget(GNT_BOX(hbox), lv->tree);
/* Viewer ************/
@@ -333,13 +570,15 @@ static FinchLogViewer *display_log_viewe
hbox = gnt_hbox_new(FALSE);
gnt_box_add_widget(GNT_BOX(vbox), hbox);
+
/* Log size ************/
- if (log_size) {
- char *sz_txt = purple_str_size_to_units(log_size);
- text = g_strdup_printf("%s %s", _("Total log size:"), sz_txt);
- size_label = gnt_label_new(text);
- gnt_box_add_widget(GNT_BOX(hbox), size_label);
- g_free(sz_txt);
+ if (need_log_size) {
+ lv_ex->size = 0;
+ text = g_strdup_printf("%s %s", _("Total log size:"), _("calculating..."));
+
+ lv_ex->size_label = gnt_label_new(text);
+ gnt_box_add_widget(GNT_BOX(hbox), lv_ex->size_label);
+
g_free(text);
}
@@ -347,172 +586,443 @@ static FinchLogViewer *display_log_viewe
gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Scroll/Search: ")));
lv->entry = gnt_entry_new("");
gnt_box_add_widget(GNT_BOX(hbox), lv->entry);
- g_signal_connect(GNT_ENTRY(lv->entry), "activate", G_CALLBACK(search_cb), lv);
+ g_signal_connect(lv->entry, "activate", G_CALLBACK(search_cb), lv_ex);
gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(lv->text), lv->entry);
gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(lv->text), lv->entry);
+ /* Progress bars **********/
+ lv_ex->list_bar = gnt_progress_bar_new();
+ lv_ex->search_bar = gnt_progress_bar_new();
+
+ gnt_progress_bar_set_fraction(GNT_PROGRESS_BAR(lv_ex->search_bar), 1.0);
+
+ gnt_box_add_widget(GNT_BOX(vbox), lv_ex->list_bar);
+ gnt_box_add_widget(GNT_BOX(vbox), lv_ex->search_bar);
+
gnt_widget_show(lv->window);
+// gnt_widget_hide(lv_ex->search_bar);
- return lv;
+ lv_ex->read_cancel = NULL;
+ lv_ex->search_cancel = NULL;
+ lv_ex->list_cancel = NULL;
+
+ return lv_ex;
}
static void
-our_logging_blows(PurpleLogSet *set, PurpleLogSet *setagain, GList **list)
+finch_log_done_cb(_finch_log_data *finch_log_data)
{
- /* The iteration happens on the first list. So we use the shorter list in front */
- if (set->type != PURPLE_LOG_IM)
- return;
- *list = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, set->name, set->account), *list);
+ if (finch_log_data->is_window_open) {
+ FinchLogViewerEx *lv_ex = finch_log_data->log_viewer;
+ FinchLogViewer *lv = lv_ex->lv;
+
+// gnt_widget_hide(lv_ex->list_bar);
+
+ if (lv->logs == NULL) {
+ /* No logs were found. */
+ const gchar *log_preferences = NULL;
+
+ if (lv_ex->type == PURPLE_LOG_SYSTEM) {
+ if (!purple_prefs_get_bool("/purple/logging/log_system"))
+ log_preferences = _("System events will only be logged if the \"Log all status changes to system log\""
+ "preference is enabled.");
+ } else if (lv_ex->type == PURPLE_LOG_IM) {
+ if (!purple_prefs_get_bool("/purple/logging/log_ims"))
+ log_preferences = _("Instant messages will only be logged if the \"Log all instant messages\""
+ "preference is enabled.");
+ } else if (lv_ex->type == PURPLE_LOG_CHAT) {
+ if (!purple_prefs_get_bool("/purple/logging/log_chats"))
+ log_preferences = _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
+ }
+
+ purple_notify_info(NULL, GNT_BOX(lv->window)->title,
+ _("No logs were found"), log_preferences);
+ gnt_widget_destroy(lv->window);
+ }
+ }
+
+ finch_log_data_free(finch_log_data);
}
-void finch_log_show(PurpleLogType type, const char *username, PurpleAccount *account)
+static void
+finch_log_viewer_update_list_bar(_finch_log_data *finch_log_data)
{
- struct log_viewer_hash_t *ht;
- FinchLogViewer *lv = NULL;
- const char *name = username;
- char *title;
- GList *logs = NULL;
- int size = 0;
+ if (finch_log_data->is_window_open) {
+ FinchLogViewerEx *lv_ex = finch_log_data->log_viewer;
+ gdouble fraction;
+ fraction = (gdouble) finch_log_data->count /
+ (gdouble) finch_log_data->total;
+ gnt_progress_bar_set_fraction(GNT_PROGRESS_BAR(lv_ex->list_bar),
+ 1.0 - fraction);
+ }
+}
+
+static void
+finch_log_viewer_update_search_bar(_finch_log_data *finch_log_data)
+{
+ if (finch_log_data->is_window_open) {
+ FinchLogViewerEx *lv_ex = finch_log_data->log_viewer;
+ gdouble fraction;
+
+ fraction = (gdouble) finch_log_data->count /
+ (gdouble) finch_log_data->total;
+ gnt_progress_bar_set_fraction(GNT_PROGRESS_BAR(lv_ex->search_bar),
+ 1.0 - fraction);
+ }
+}
+
+static void
+finch_log_size_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _finch_log_data *finch_log_data = userdata;
+ GError *err = NULL;
+ gssize log_size;
+
+ log_size = purple_log_get_total_size_finish(res, &err);
+ finch_log_data->count--;
+
+ if (log_size < 0) {
+ if (err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("gntlog", "Error getting total log size: %s\n", err->message);
+ } else if (finch_log_data->is_window_open) {
+ FinchLogViewerEx *lv_ex = finch_log_data->log_viewer;
+ gchar *sz_txt, *text;
+ gsize total;
+
+ total = lv_ex->size;
+ total += log_size;
+
+ lv_ex->size = total;
+
+ sz_txt = purple_str_size_to_units(total);
+ text = g_strdup_printf("%s: %s", _("Total log size"), sz_txt);
+
+ gnt_label_set_text(GNT_LABEL(lv_ex->size_label), text);
+
+ g_free(sz_txt);
+ g_free(text);
+ }
+
+ g_clear_error(&err);
+ finch_log_viewer_update_list_bar(finch_log_data);
+
+ if (finch_log_data->count < 1)
+ finch_log_done_cb(finch_log_data);
+}
+
+static void
+finch_log_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _finch_log_data *finch_log_data = userdata;
+ GError *err = NULL;
+ GList *list;
+
+ list = purple_log_get_logs_finish(res, &err);
+ finch_log_data->count--;
+
+ if (list == NULL) {
+ if (err != NULL && err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("gntlog", "Error getting logs: %s\n", err->message);
+ } else if (finch_log_data->is_window_open) {
+ FinchLogViewerEx *lv_ex = finch_log_data->log_viewer;
+ FinchLogViewer *lv = lv_ex->lv;
+
+ lv->logs = g_list_concat(lv->logs, list);
+ lv->logs = g_list_sort(lv->logs, purple_log_compare);
+
+ gnt_tree_remove_all(GNT_TREE(lv->tree));
+ populate_log_tree(lv_ex);
+ }
+
+ g_clear_error(&err);
+ finch_log_viewer_update_list_bar(finch_log_data);
+
+ if (finch_log_data->count < 1)
+ finch_log_done_cb(finch_log_data);
+}
+
+static void
+finch_log_system_list_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _finch_log_data *finch_log_data = userdata;
+ GError *err = NULL;
+ GList *list;
+
+ list = purple_log_get_system_logs_finish(res, &err);
+ finch_log_data->count--;
+
+ if (list == NULL) {
+ if (err != NULL && err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("gntlog", "Error getting system logs: %s\n", err->message);
+ } else if (finch_log_data->is_window_open) {
+ FinchLogViewerEx *lv_ex = finch_log_data->log_viewer;
+ FinchLogViewer *lv = lv_ex->lv;
+
+ lv->logs = g_list_concat(lv->logs, list);
+ lv->logs = g_list_sort(lv->logs, purple_log_compare);
+
+ gnt_tree_remove_all(GNT_TREE(lv->tree));
+ populate_log_tree(lv_ex);
+ }
+
+ g_clear_error(&err);
+ finch_log_viewer_update_list_bar(finch_log_data);
+
+ if (finch_log_data->count < 1)
+ finch_log_done_cb(finch_log_data);
+}
+
+static void
+finch_log_sets_cb(GObject *object, GAsyncResult *res, gpointer userdata)
+{
+ _finch_log_data *finch_log_data = userdata;
+ GError *err = NULL;
+ GHashTable *table;
+
+ table = purple_log_get_log_sets_finish(res, &err);
+
+ if (table == NULL) {
+ if (err != NULL && err->code != G_IO_ERROR_CANCELLED)
+ purple_debug_error("gntlog", "Error getting log sets: %s\n", err->message);
+
+ finch_log_data_free(finch_log_data);
+ } else if (finch_log_data->is_window_open) {
+ FinchLogViewerEx *lv_ex = finch_log_data->log_viewer;
+ GList *list, *n;
+ guint length;
+
+ list = g_hash_table_get_keys(table);
+ length = g_list_length(list);
+
+ if (length > 0) {
+ finch_log_data->count = finch_log_data->total = 0;
+
+ for (n = list; n != NULL; n = g_list_next(n)) {
+ PurpleLogSet *set = n->data;
+
+ if (set->type != PURPLE_LOG_IM)
+ continue;
+
+ finch_log_data->count = (finch_log_data->total += 2);
+
+ purple_log_get_logs_async(PURPLE_LOG_IM, set->name, set->account,
+ G_PRIORITY_DEFAULT, lv_ex->list_cancel, finch_log_list_cb, finch_log_data);
+ purple_log_get_total_size_async(PURPLE_LOG_IM, set->name, set->account,
+ G_PRIORITY_DEFAULT, lv_ex->list_cancel, finch_log_size_cb, finch_log_data);
+ }
+ }
+
+ g_list_free(list);
+ g_hash_table_unref(table);
+ } else
+ finch_log_data_free(finch_log_data);
+
+ g_clear_error(&err);
+}
+
+void
+finch_log_show(PurpleLogType type, const gchar *username, PurpleAccount *account)
+{
+ log_viewer_hash_t *ht;
+ _finch_log_data *finch_log_data;
+ FinchLogViewerEx *lv_ex;
+ GCancellable *cancel;
+ const gchar *name = username;
+ gchar *title;
+
if (type != PURPLE_LOG_IM) {
g_return_if_fail(account != NULL);
g_return_if_fail(username != NULL);
}
- ht = g_new0(struct log_viewer_hash_t, 1);
+ ht = g_new0(log_viewer_hash_t, 1);
ht->type = type;
ht->username = g_strdup(username);
ht->account = account;
- if (log_viewers == NULL) {
+ if (log_viewers == NULL)
log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
- } else if ((lv = g_hash_table_lookup(log_viewers, ht))) {
- gnt_window_present(lv->window);
+ else if ((lv_ex = g_hash_table_lookup(log_viewers, ht)) != NULL) {
+ gnt_window_present(lv_ex->lv->window);
+
g_free(ht->username);
g_free(ht);
+
return;
}
if (type == PURPLE_LOG_CHAT) {
- PurpleChat *chat;
+ PurpleChat *chat = purple_blist_find_chat(account, username);
- chat = purple_blist_find_chat(account, username);
if (chat != NULL)
name = purple_chat_get_name(chat);
title = g_strdup_printf(_("Conversations in %s"), name);
} else {
- PurpleBuddy *buddy;
+ if (username != NULL) {
+ PurpleBuddy *buddy = purple_find_buddy(account, username);
- if (username) {
- buddy = purple_find_buddy(account, username);
if (buddy != NULL)
name = purple_buddy_get_contact_alias(buddy);
+
title = g_strdup_printf(_("Conversations with %s"), name);
- } else {
+ } else
title = g_strdup(_("All Conversations"));
- }
}
- if (username) {
- logs = purple_log_get_logs(type, username, account);
- size = purple_log_get_total_size(type, username, account);
+ finch_log_data = g_new0(_finch_log_data, 1);
+ finch_log_data->is_window_open = TRUE;
+
+ lv_ex = finch_log_data->log_viewer = display_log_viewer_nonblocking(ht,
+ title, TRUE);
+
+ finch_log_data->destroy_handler_id = g_signal_connect_swapped(lv_ex->lv->window,
+ "destroy", G_CALLBACK(finch_window_destroy_cb), finch_log_data);
+
+ g_free(title);
+
+ cancel = g_cancellable_new();
+ finch_log_viewer_ex_set_list_cancel(lv_ex, cancel);
+
+ if (username != NULL) {
+ finch_log_data->count = finch_log_data->total = 2;
+
+ purple_log_get_logs_async(type, username, account,
+ G_PRIORITY_DEFAULT, cancel, finch_log_list_cb, finch_log_data);
+ purple_log_get_total_size_async(type, username, account,
+ G_PRIORITY_DEFAULT, cancel, finch_log_size_cb, finch_log_data);
} else {
/* This will happen only for IMs */
- GHashTable *table = purple_log_get_log_sets();
- g_hash_table_foreach(table, (GHFunc)our_logging_blows, &logs);
- g_hash_table_destroy(table);
- logs = g_list_sort(logs, purple_log_compare);
- size = 0;
- }
+ finch_log_data->count = finch_log_data->total = 1;
- display_log_viewer(ht, logs, title, size);
+ purple_log_get_log_sets_async(G_PRIORITY_DEFAULT, cancel,
+ finch_log_sets_cb, finch_log_data);
+ }
- g_free(title);
+ g_object_unref(cancel);
}
-void finch_log_show_contact(PurpleContact *contact)
+void
+finch_log_show_contact(PurpleContact *contact)
{
- struct log_viewer_hash_t *ht;
+ _finch_log_data *finch_log_data;
+ log_viewer_hash_t *ht;
PurpleBlistNode *child;
- FinchLogViewer *lv = NULL;
- GList *logs = NULL;
- const char *name = NULL;
- char *title;
- int total_log_size = 0;
+ FinchLogViewerEx *lv_ex;
+ GCancellable *cancel;
+ const gchar *name = NULL;
+ gchar *title;
g_return_if_fail(contact != NULL);
- ht = g_new0(struct log_viewer_hash_t, 1);
+ ht = g_new0(log_viewer_hash_t, 1);
ht->type = PURPLE_LOG_IM;
ht->contact = contact;
- if (log_viewers == NULL) {
+ if (log_viewers == NULL)
log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
- } else if ((lv = g_hash_table_lookup(log_viewers, ht))) {
- gnt_window_present(lv->window);
+ else if ((lv_ex = g_hash_table_lookup(log_viewers, ht)) != NULL) {
+ gnt_window_present(lv_ex->lv->window);
g_free(ht);
- return;
- }
- for (child = purple_blist_node_get_first_child((PurpleBlistNode*)contact); child;
- child = purple_blist_node_get_sibling_next(child)) {
- const char *name;
- PurpleAccount *account;
- if (!PURPLE_BLIST_NODE_IS_BUDDY(child))
- continue;
-
- name = purple_buddy_get_name((PurpleBuddy *)child);
- account = purple_buddy_get_account((PurpleBuddy *)child);
- logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, name,
- account), logs);
- total_log_size += purple_log_get_total_size(PURPLE_LOG_IM, name, account);
+ return;
}
- logs = g_list_sort(logs, purple_log_compare);
- name = purple_contact_get_alias(contact);
- if (!name)
+ if ((name = purple_contact_get_alias(contact)) == NULL)
name = purple_buddy_get_contact_alias(purple_contact_get_priority_buddy(contact));
/* This will happen if the contact doesn't have an alias,
* and none of the contact's buddies are online.
* There is probably a better way to deal with this. */
if (name == NULL) {
- child = purple_blist_node_get_first_child((PurpleBlistNode*)contact);
+ child = purple_blist_node_get_first_child(PURPLE_BLIST_NODE(contact));
+
if (child != NULL && PURPLE_BLIST_NODE_IS_BUDDY(child))
- name = purple_buddy_get_contact_alias((PurpleBuddy *)child);
+ name = purple_buddy_get_contact_alias(PURPLE_BUDDY(child));
+
if (name == NULL)
name = "";
}
+ finch_log_data = g_new0(_finch_log_data, 1);
+ finch_log_data->is_window_open = TRUE;
+ finch_log_data->count = finch_log_data->total = 0;
+
title = g_strdup_printf(_("Conversations with %s"), name);
- display_log_viewer(ht, logs, title, total_log_size);
+ lv_ex = finch_log_data->log_viewer = display_log_viewer_nonblocking(ht, title, TRUE);
g_free(title);
+
+ finch_log_data->destroy_handler_id = g_signal_connect_swapped(lv_ex->lv->window,
+ "destroy", G_CALLBACK(finch_window_destroy_cb), finch_log_data);
+
+ cancel = g_cancellable_new();
+ finch_log_viewer_ex_set_list_cancel(lv_ex, cancel);
+
+ for (child = purple_blist_node_get_first_child(PURPLE_BLIST_NODE(contact)); child != NULL;
+ child = purple_blist_node_get_sibling_next(child))
+ {
+ const gchar *name;
+ PurpleAccount *account;
+
+ if (!PURPLE_BLIST_NODE_IS_BUDDY(child))
+ continue;
+
+ finch_log_data->count = (finch_log_data->total += 2);
+
+ name = purple_buddy_get_name(PURPLE_BUDDY(child));
+ account = purple_buddy_get_account(PURPLE_BUDDY(child));
+
+ purple_log_get_logs_async(PURPLE_LOG_IM, name, account,
+ G_PRIORITY_DEFAULT, cancel, finch_log_list_cb, finch_log_data);
+ purple_log_get_total_size_async(PURPLE_LOG_IM, name, account,
+ G_PRIORITY_DEFAULT, cancel, finch_log_size_cb, finch_log_data);
+ }
+
+ g_object_unref(cancel);
}
-void finch_syslog_show()
+void
+finch_syslog_show(void)
{
+ _finch_log_data *finch_log_data;
+ GCancellable *cancel;
GList *accounts = NULL;
- GList *logs = NULL;
if (syslog_viewer != NULL) {
- gnt_window_present(syslog_viewer->window);
+ gnt_window_present(syslog_viewer->lv->window);
+
return;
}
- for(accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) {
+ finch_log_data = g_new0(_finch_log_data, 1);
+ finch_log_data->is_window_open = TRUE;
+ finch_log_data->count = finch_log_data->total = 0;
- PurpleAccount *account = (PurpleAccount *)accounts->data;
+ syslog_viewer = finch_log_data->log_viewer = display_log_viewer_nonblocking(NULL,
+ _("System Log"), FALSE);
+
+ finch_log_data->destroy_handler_id = g_signal_connect_swapped(syslog_viewer->lv->window,
+ "destroy", G_CALLBACK(finch_window_destroy_cb), finch_log_data);
+
+ cancel = g_cancellable_new();
+ finch_log_viewer_ex_set_list_cancel(syslog_viewer, cancel);
+
+ for (accounts = purple_accounts_get_all(); accounts != NULL; accounts = g_list_next(accounts)) {
+ PurpleAccount *account = accounts->data;
+
if(purple_find_prpl(purple_account_get_protocol_id(account)) == NULL)
continue;
- logs = g_list_concat(purple_log_get_system_logs(account), logs);
+ finch_log_data->count = finch_log_data->total++;
+
+ purple_log_get_system_logs_async(account,
+ G_PRIORITY_DEFAULT, cancel, finch_log_system_list_cb, finch_log_data);
}
- logs = g_list_sort(logs, purple_log_compare);
- syslog_viewer = display_log_viewer(NULL, logs, _("System Log"), 0);
+ g_object_unref(cancel);
}
/****************************************************************************
@@ -527,17 +1037,16 @@ finch_log_get_handle(void)
return &handle;
}
-void finch_log_init(void)
+void
+finch_log_init(void)
{
void *handle = finch_log_get_handle();
purple_signal_register(handle, "log-displaying",
- purple_marshal_VOID__POINTER_POINTER,
- NULL, 2,
- purple_value_new(PURPLE_TYPE_BOXED,
- "FinchLogViewer *"),
- purple_value_new(PURPLE_TYPE_SUBTYPE,
- PURPLE_SUBTYPE_LOG));
+ purple_marshal_VOID__POINTER_POINTER,
+ NULL, 2,
+ purple_value_new(PURPLE_TYPE_BOXED, "FinchLogViewer *"),
+ purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_LOG));
}
void
============================================================
--- finch/gntlog.h d9f35af7e4f3cfcbebbac9d4f03f705daa2b223e
+++ finch/gntlog.h 02f892c394bbc98990f9c0bbf5f58cd0a9ace230
@@ -30,31 +30,60 @@
#include "account.h"
#include "gntwidget.h"
+/** @copydoc _FinchLogViewer */
typedef struct _FinchLogViewer FinchLogViewer;
+/********************************************************
+ * DATA STRUCTURES **************************************
+ ********************************************************/
+
/**
* A GNT Log Viewer. You can look at logs with it.
*/
struct _FinchLogViewer {
- GList *logs; /**< The list of logs viewed in this viewer */
+ GList *logs; /**< The list of logs viewed in this viewer */
- GntWidget *window; /**< The viewer's window */
- GntWidget *tree; /**< The tree representing said treestore */
- GntWidget *text; /**< The text to display said logs */
- GntWidget *entry; /**< The search entry, in which search terms
- * are entered */
- GntWidget *label;
- PurpleLogReadFlags flags; /**< The most recently used log flags */
- char *search; /**< The string currently being searched for */
+ GntWidget *window; /**< The viewer's window */
+ GntWidget *tree; /**< The tree representing said treestore */
+ GntWidget *text; /**< The text to display said logs */
+ GntWidget *entry; /**< The search entry, in which search terms
+ * are entered */
+ GntWidget *label; /**< The label widget with the title */
+ PurpleLogReadFlags flags; /**< The most recently used log flags */
+ gchar *search; /**< The string currently being searched for */
};
+G_BEGIN_DECLS
+/**************************************************************************/
+/** @name Log Viewer Creators */
+/**************************************************************************/
-void finch_log_show(PurpleLogType type, const char *username, PurpleAccount *account);
+/*@{*/
+
+/**
+ * Displays the logs of a certain type for a buddy or chat on an account
+ *
+ * @param type: The #PurpleLogType
+ * @param username: The buddy or chat name
+ * @param account: The account
+ */
+void finch_log_show(PurpleLogType type, const gchar *username, PurpleAccount *account);
+
+/**
+ * Displays the logs for one contact
+ *
+ * @param contact: The #PurpleContact to display the logs of
+ */
void finch_log_show_contact(PurpleContact *contact);
+/**
+ * Displays the system log
+ */
void finch_syslog_show(void);
+/*@}*/
+
/**************************************************************************/
/** @name GNT Log Subsystem */
/**************************************************************************/
More information about the Commits
mailing list