org.guifications.plugins.autoprofile_merge: 173815f47a6cde55853103f2189cdd0044457d43
rekkanoryo at guifications.org
rekkanoryo at guifications.org
Sun Mar 23 12:55:09 CDT 2008
-----------------------------------------------------------------
Revision: 173815f47a6cde55853103f2189cdd0044457d43
Ancestor: cb62d34f3f8964526dad2f7efef20475f51a45b8
Author: rekkanoryo at guifications.org
Date: 2008-03-23T03:10:31
Branch: org.guifications.plugins.autoprofile_merge
Added files:
autoprofile/Makefile.am autoprofile/autoaway.c
autoprofile/autoprofile.c autoprofile/autoprofile.h
autoprofile/autoreply.c autoprofile/comp_countdownup.c
autoprofile/comp_executable.c autoprofile/comp_http.c
autoprofile/comp_logstats.c autoprofile/comp_logstats.h
autoprofile/comp_logstats_gtk.c autoprofile/comp_quotation.c
autoprofile/comp_rss.c autoprofile/comp_rss.h
autoprofile/comp_rss_parser.c autoprofile/comp_rss_xanga.c
autoprofile/comp_textfile.c autoprofile/comp_timestamp.c
autoprofile/comp_uptime.c autoprofile/component.c
autoprofile/component.h autoprofile/gtk_actions.c
autoprofile/gtk_away_msgs.c autoprofile/gtk_widget.c
autoprofile/preferences.c autoprofile/sizes.h
autoprofile/utility.c autoprofile/utility.h
autoprofile/utility_rfc822.c autoprofile/widget.c
autoprofile/widget.h
Added directories:
autoprofile
Modified files:
configure.ac
ChangeLog:
Adding Autoprofile to the plugin pack. This currently does not build.
-----------------------------------------------------------------
This revision's diffstat output:
autoprofile/Makefile.am | 60 ++
autoprofile/autoaway.c | 145 +++++
autoprofile/autoprofile.c | 861 +++++++++++++++++++++++++++++++++
autoprofile/autoprofile.h | 112 ++++
autoprofile/autoreply.c | 324 ++++++++++++
autoprofile/comp_countdownup.c | 434 ++++++++++++++++
autoprofile/comp_executable.c | 165 ++++++
autoprofile/comp_http.c | 202 +++++++
autoprofile/comp_logstats.c | 1039 ++++++++++++++++++++++++++++++++++++++++
autoprofile/comp_logstats.h | 28 +
autoprofile/comp_logstats_gtk.c | 355 +++++++++++++
autoprofile/comp_quotation.c | 602 +++++++++++++++++++++++
autoprofile/comp_rss.c | 475 ++++++++++++++++++
autoprofile/comp_rss.h | 52 ++
autoprofile/comp_rss_parser.c | 348 +++++++++++++
autoprofile/comp_rss_xanga.c | 117 ++++
autoprofile/comp_textfile.c | 264 ++++++++++
autoprofile/comp_timestamp.c | 140 +++++
autoprofile/comp_uptime.c | 96 +++
autoprofile/component.c | 85 +++
autoprofile/component.h | 74 ++
autoprofile/gtk_actions.c | 341 +++++++++++++
autoprofile/gtk_away_msgs.c | 486 ++++++++++++++++++
autoprofile/gtk_widget.c | 778 +++++++++++++++++++++++++++++
autoprofile/preferences.c | 750 ++++++++++++++++++++++++++++
autoprofile/sizes.h | 49 +
autoprofile/utility.c | 221 ++++++++
autoprofile/utility.h | 41 +
autoprofile/utility_rfc822.c | 187 +++++++
autoprofile/widget.c | 602 +++++++++++++++++++++++
autoprofile/widget.h | 96 +++
configure.ac | 1
32 files changed, 9530 insertions(+)
-------------- next part --------------
============================================================
--- autoprofile/Makefile.am 01355c1045346405e31ef5920ac7e49bf0c36511
+++ autoprofile/Makefile.am 01355c1045346405e31ef5920ac7e49bf0c36511
@@ -0,0 +1,60 @@
+EXTRA_DIST = \
+ .build \
+ .pidgin-plugin
+
+autoprofiledir = $(PURPLE_LIBDIR)
+
+autoprofile_la_LDFLAGS = -module -avoid-version
+
+if HAVE_PIDGIN
+
+autoprofile_LTLIBRARIES = autoprofile.la
+
+autoprofile_la_SOURCES = \
+ autoaway.c \
+ autoprofile.c \
+ autoprofile.h \
+ autoreply.c \
+ comp_countdownup.c \
+ comp_executable.c \
+ comp_http.c \
+ comp_logstats.c \
+ comp_logstats_gtk.c \
+ comp_logstats.h \
+ component.c \
+ component.h \
+ comp_quotation.c \
+ comp_rss.c \
+ comp_rss.h \
+ comp_rss_parser.c \
+ comp_rss_xanga.c \
+ comp_textfile.c \
+ comp_timestamp.c \
+ comp_uptime.c \
+ gtk_actions.c \
+ gtk_away_msgs.c \
+ gtk_widget.c \
+ Makefile.am \
+ preferences.c \
+ sizes.h \
+ utility.c \
+ utility.h \
+ utility_rfc822.c \
+ widget.c \
+ widget.h
+
+listhandler_la_LIBADD = \
+ $(PIDGIN_LIBS) \
+ $(GLIB_LIBS)
+
+endif
+
+AM_CPPFLAGS = \
+ -DLIBDIR=\"$(PIDGIN_LIBDIR)\" \
+ -DDATADIR=\"$(PIDGIN_DATADIR)\" \
+ -DPIXMAPSDIR=\"$(PIDGIN_PIXMAPSDIR)\" \
+ $(DEBUG_CFLAGS) \
+ $(PIDGIN_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(GLIB_CFLAGS)
+
============================================================
--- autoprofile/autoaway.c 8d7270cbd63aca748e86b4ef6fb9fa9a9d16ce0f
+++ autoprofile/autoaway.c 8d7270cbd63aca748e86b4ef6fb9fa9a9d16ce0f
@@ -0,0 +1,145 @@
+/*--------------------------------------------------------------------------*
+ * AUTOPROFILE *
+ * *
+ * A Purple away message and profile manager that supports dynamic text *
+ * *
+ * AutoProfile is the legal property of its developers. Please refer to *
+ * the COPYRIGHT file distributed with this source distribution. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ *--------------------------------------------------------------------------*/
+
+#include "autoprofile.h"
+#include "idle.h"
+#include "conversation.h"
+
+#define AP_IDLE_CHECK_INTERVAL 5
+
+static guint check_timeout = 0;
+static guint pref_cb = 0;
+static time_t last_active_time = 0;
+
+static gboolean is_idle ()
+{
+ PurpleIdleUiOps *ui_ops;
+ time_t time_idle;
+ const gchar *idle_reporting;
+
+ ui_ops = purple_idle_get_ui_ops ();
+
+ idle_reporting = purple_prefs_get_string ("/core/away/idle_reporting");
+ if (!strcmp (idle_reporting, "system") &&
+ (ui_ops != NULL) && (ui_ops->get_time_idle != NULL)) {
+ time_idle = time (NULL) - last_active_time;
+ } else if (!strcmp (idle_reporting, "gaim")) {
+ time_idle = time (NULL) - last_active_time;
+ } else {
+ time_idle = 0;
+ }
+
+ return (time_idle >
+ (60 * purple_prefs_get_int("/core/away/mins_before_away")));
+}
+
+static gboolean ap_check_idleness (gpointer data)
+{
+ gboolean auto_away;
+
+ // ap auto idle
+ // 0 0 0 don't do anything
+ // 0 0 1 ap_use_idleaway ()
+ // 1 0 x don't do anything, we're already away
+ // 1 1 0 ap_dont_use_idleaway ()
+ // 1 1 1 don't do anything
+
+ if (ap_is_currently_away () && !ap_autoaway_in_use ()) return TRUE;
+ auto_away = purple_prefs_get_bool (
+ "/plugins/gtk/autoprofile/away_when_idle");
+
+ if (is_idle ()) {
+ if (auto_away && !ap_is_currently_away () && !ap_autoaway_in_use ()) {
+ ap_autoaway_enable ();
+ }
+ } else {
+ if (ap_is_currently_away () && ap_autoaway_in_use ()) {
+ ap_autoaway_disable ();
+ }
+ }
+
+ return TRUE;
+}
+
+void ap_autoaway_touch ()
+{
+ time (&last_active_time);
+}
+
+
+static gboolean writing_im_msg_cb (PurpleAccount *account, const char *who,
+ char **message, PurpleConversation *conv, PurpleMessageFlags flags)
+{
+ ap_autoaway_touch ();
+ ap_check_idleness (NULL);
+ return FALSE;
+}
+
+static void auto_pref_cb (
+ const char *name, PurplePrefType type, gconstpointer val, gpointer data)
+{
+ if (!purple_prefs_get_bool ("/core/away/away_when_idle")) return;
+
+ purple_notify_error (NULL, NULL,
+ N_("This preference is disabled"),
+ N_("This preference currently has no effect because AutoProfile is in "
+ "use. To modify this behavior, use the AutoProfile configuration "
+ "menu."));
+
+ purple_prefs_set_bool ("/core/away/away_when_idle", FALSE);
+}
+
+/*--------------------------------------------------------------------------*
+ * Global functions to start it all *
+ *--------------------------------------------------------------------------*/
+void ap_autoaway_start ()
+{
+ purple_prefs_set_bool ("/core/away/away_when_idle", FALSE);
+
+ check_timeout = purple_timeout_add (AP_IDLE_CHECK_INTERVAL * 1000,
+ ap_check_idleness, NULL);
+
+ purple_signal_connect (purple_conversations_get_handle (), "writing-im-msg",
+ ap_get_plugin_handle (), PURPLE_CALLBACK(writing_im_msg_cb), NULL);
+
+ pref_cb = purple_prefs_connect_callback (ap_get_plugin_handle (),
+ "/core/away/away_when_idle", auto_pref_cb, NULL);
+
+ ap_autoaway_touch ();
+}
+
+void ap_autoaway_finish ()
+{
+ // Assumes signals are disconnected globally
+
+ purple_prefs_disconnect_callback (pref_cb);
+ pref_cb = 0;
+
+ if (check_timeout > 0) purple_timeout_remove (check_timeout);
+ check_timeout = 0;
+
+ purple_prefs_set_bool ("/core/away/away_when_idle",
+ purple_prefs_get_bool ("/plugins/gtk/autoprofile/away_when_idle"));
+}
+
+
============================================================
--- autoprofile/autoprofile.c 638632fe58fc8095d4b45970eb6ca1d96d527ea5
+++ autoprofile/autoprofile.c 638632fe58fc8095d4b45970eb6ca1d96d527ea5
@@ -0,0 +1,861 @@
+/*--------------------------------------------------------------------------*
+ * AUTOPROFILE *
+ * *
+ * A Purple away message and profile manager that supports dynamic text *
+ * *
+ * AutoProfile is the legal property of its developers. Please refer to *
+ * the COPYRIGHT file distributed with this source distribution. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ *--------------------------------------------------------------------------*/
+
+#include "autoprofile.h"
+
+#include "version.h"
+#include "savedstatuses.h"
+
+/* General functions */
+static void ap_status_changed (
+ const char *, PurplePrefType, gconstpointer, gpointer);
+static void ap_account_connected (PurpleConnection *);
+
+static void ap_delete_legacy_prefs ();
+
+static void ap_update_queue_start ();
+static void ap_update_queue_finish ();
+
+/*--------------------------------------------------------------------------
+ * GENERAL VARIABLES
+ *------------------------------------------------------------------------*/
+
+static PurplePlugin *plugin_handle = NULL;
+static PurpleSavedStatus *current_ap_status = NULL;
+
+static GStaticMutex update_timeout_mutex = G_STATIC_MUTEX_INIT;
+static GHashTable *update_timeouts = NULL;
+
+static gboolean using_idleaway = FALSE;
+
+static GStaticMutex update_queue_mutex = G_STATIC_MUTEX_INIT;
+static GList *queued_profiles = NULL;
+static guint update_queue_timeout = 0;
+
+/* Functions related to general variables */
+PurplePlugin *ap_get_plugin_handle () { return plugin_handle; }
+
+gboolean ap_is_currently_away () {
+ return current_ap_status != NULL &&
+ purple_savedstatus_get_type (current_ap_status) == PURPLE_STATUS_AWAY;
+}
+
+/*--------------------------------------------------------------------------
+ * REQUIRED GAIM FUNCTIONS- INFO, INITIALIZATION, UNLOADING
+ *------------------------------------------------------------------------*/
+/* What to do when plugin is loaded */
+static gboolean plugin_load (PurplePlugin *plugin)
+{
+ GList *accounts_pref;
+
+ ap_debug ("general", "AutoProfile is being loaded");
+
+ plugin_handle = plugin;
+ current_ap_status = purple_savedstatus_new (NULL, PURPLE_STATUS_UNSET);
+ update_timeouts = g_hash_table_new (NULL, NULL);
+
+ ap_delete_legacy_prefs ();
+
+ /* The core autoprofile tracking system */
+ purple_prefs_connect_callback (plugin_handle, "/core/savedstatus/current",
+ ap_status_changed, NULL);
+ purple_signal_connect (purple_connections_get_handle (),
+ "signed-on", plugin_handle,
+ PURPLE_CALLBACK (ap_account_connected), NULL);
+
+ ap_component_start ();
+ ap_gtk_start ();
+
+ accounts_pref = purple_prefs_get_string_list (
+ "/plugins/gtk/autoprofile/profile_accounts");
+ ap_gtk_set_progress_visible (AP_UPDATE_PROFILE, (accounts_pref != NULL));
+ free_string_list (accounts_pref);
+
+ ap_update_after_delay (AP_UPDATE_STATUS);
+ ap_update_after_delay (AP_UPDATE_PROFILE);
+
+ ap_autoaway_start ();
+ ap_autoreply_start ();
+
+ ap_update_queue_start ();
+
+ return TRUE;
+}
+
+/* What to do when plugin is unloaded */
+static gboolean plugin_unload (PurplePlugin *plugin)
+{
+ ap_update_queue_finish ();
+
+ ap_autoreply_finish ();
+ ap_autoaway_finish ();
+
+ /* General vars */
+ using_idleaway = FALSE;
+
+ ap_update_stop (AP_UPDATE_STATUS);
+ ap_update_stop (AP_UPDATE_PROFILE);
+
+ /* Disconnect tracking system */
+ purple_signals_disconnect_by_handle (plugin);
+
+ ap_actions_finish ();
+ ap_gtk_finish ();
+ ap_component_finish ();
+
+ g_hash_table_destroy (update_timeouts);
+ return TRUE;
+}
+
+/* General information */
+static PurplePluginInfo info =
+{
+ PURPLE_PLUGIN_MAGIC,
+ PURPLE_MAJOR_VERSION,
+ PURPLE_MINOR_VERSION,
+ PURPLE_PLUGIN_STANDARD, /* type */
+ PIDGIN_PLUGIN_TYPE, /* ui_requirement */
+ 0, /* flags */
+ NULL, /* dependencies */
+ PURPLE_PRIORITY_DEFAULT, /* priority */
+
+ N_("gtk-kluge-autoprofile"), /* id */
+ N_("AutoProfile"), /* name */
+ AUTOPROFILE_VERSION, /* version */
+ N_("User profile and status message content generator"),/* summary */
+ /* description */
+ N_("Allows user to place dynamic text into profiles\n"
+ "and status messages, with the text automatically\n"
+ "updated whenever content changes"),
+ /* author */
+ N_("Casey Ho <casey at hkn-berkeley-edu>"
+ "\n\t\t\taim:caseyho"),
+ N_("http://autoprofile.sourceforge.net/"), /* homepage */
+ plugin_load, /* load */
+ plugin_unload, /* unload */
+ NULL, /* destroy */
+
+ &ui_info, /* ui_info */
+ NULL, /* extra_info */
+ NULL, /* prefs_info */
+ actions /* actions */
+};
+
+/*--------------------------------------------------------------------------
+ * CORE FUNCTIONS
+ *------------------------------------------------------------------------*/
+
+static gint get_max_size_status (
+ const PurpleAccount *account, const PurpleStatusPrimitive type) {
+ const char *id;
+
+ if (account == NULL) {
+ switch (type) {
+ case PURPLE_STATUS_AVAILABLE: return AP_SIZE_AVAILABLE_MAX;
+ case PURPLE_STATUS_AWAY: return AP_SIZE_AWAY_MAX;
+ default: return AP_SIZE_MAXIMUM;
+ }
+ } else {
+ id = purple_account_get_protocol_id (account);
+
+ switch (type) {
+ case PURPLE_STATUS_AVAILABLE:
+ if (!strcmp (id, "prpl-oscar")) return AP_SIZE_AVAILABLE_AIM;
+ else if (!strcmp (id, "prpl-msn")) return AP_SIZE_AVAILABLE_MSN;
+ else if (!strcmp (id, "prpl-yahoo")) return AP_SIZE_AVAILABLE_YAHOO;
+ else return AP_SIZE_AVAILABLE_MAX;
+ case PURPLE_STATUS_AWAY:
+ if (!strcmp (id, "prpl-oscar")) return AP_SIZE_AWAY_AIM;
+ else return AP_SIZE_AWAY_MAX;
+ default:
+ return AP_SIZE_MAXIMUM;
+ }
+ }
+}
+
+static const char *ap_savedstatus_get_message (
+ const PurpleSavedStatus *status, const PurpleAccount *account)
+{
+ const PurpleSavedStatusSub *substatus;
+
+ substatus = purple_savedstatus_get_substatus(status, account);
+ if (substatus != NULL) {
+ return purple_savedstatus_substatus_get_message (substatus);
+ }
+ return purple_savedstatus_get_message (status);
+}
+
+static PurpleStatusPrimitive ap_savedstatus_get_type (
+ const PurpleSavedStatus *status, const PurpleAccount *account)
+{
+ const PurpleSavedStatusSub *substatus;
+
+ substatus = purple_savedstatus_get_substatus(status, account);
+ if (substatus != NULL) {
+ return purple_status_type_get_primitive (
+ purple_savedstatus_substatus_get_type (substatus));
+ }
+ return purple_savedstatus_get_type (status);
+}
+
+gchar *ap_get_sample_status_message (PurpleAccount *account)
+{
+ const PurpleSavedStatus *s;
+ const gchar *message;
+ PurpleStatusPrimitive type;
+
+ s = (using_idleaway? purple_savedstatus_get_idleaway () :
+ purple_savedstatus_get_current ());
+ message = ap_savedstatus_get_message (s, account);
+ type = ap_savedstatus_get_type (s, account);
+
+ if (!message) return NULL;
+ return ap_generate (message, get_max_size_status (account, type));
+}
+
+/* Generator helper */
+static gchar *ap_process_replacement (const gchar *f) {
+ GString *s;
+ struct widget *w;
+ gchar *result;
+
+ w = ap_widget_find (f);
+ if (w) {
+ result = w->component->generate (w);
+ return result;
+ } else {
+ s = g_string_new ("");
+ g_string_printf (s, "[%s]", f);
+ result = s->str;
+ g_string_free (s, FALSE);
+ return result;
+ }
+}
+
+/* The workhorse generation function! */
+gchar *ap_generate (const gchar *f, gint max_length) {
+ GString *output;
+ gchar *result;
+ gchar *format, *format_start, *percent_start;
+ gchar *replacement;
+ int state;
+
+ output = g_string_new ("");
+ format_start = format = purple_utf8_salvage (f);
+
+ /* When a % has been read (and searching for next %), state is 1
+ * otherwise it is 0
+ */
+ state = 0;
+ percent_start = NULL;
+
+ while (*format) {
+ if (state == 1) {
+ if (*format == '[') {
+ g_string_append_unichar (output, g_utf8_get_char ("["));
+ *format = '\0';
+ g_string_append (output, percent_start);
+ percent_start = format = format+1;
+ } else if (*format == ']') {
+ *format = '\0';
+ format++;
+ state = 0;
+ replacement = ap_process_replacement (percent_start);
+ percent_start = NULL;
+ g_string_append (output, replacement);
+ free (replacement);
+ } else {
+ format = g_utf8_next_char (format);
+ }
+ } else {
+ if (*format == '\n') {
+ g_string_append (output, "<br>");
+ } else if (*format == '[') {
+ state = 1;
+ percent_start = format+1;
+ } else {
+ g_string_append_unichar (output, g_utf8_get_char (format));
+ }
+ format = g_utf8_next_char (format);
+ }
+ }
+
+ /* Deal with case where final ] not found */
+ if (state == 1) {
+ g_string_append_unichar (output, g_utf8_get_char ("["));
+ g_string_append (output, percent_start);
+ }
+
+ /* Set size limit */
+ g_string_truncate (output, max_length);
+
+ /* Finish up */
+ free (format_start);
+ result = purple_utf8_salvage(output->str);
+ g_string_free (output, TRUE);
+
+ return result;
+}
+
+void ap_account_enable_profile (const PurpleAccount *account, gboolean enable) {
+ GList *original, *new;
+ gboolean original_status;
+
+ gchar *username, *protocol_id;
+
+ GList *node, *tmp;
+ GList *ret = NULL;
+
+ original_status = ap_account_has_profile_enabled (account);
+ if (original_status == enable) {
+ ap_debug_warn ("profile", "New status identical to original, skipping");
+ return;
+ }
+
+ original = purple_prefs_get_string_list (
+ "/plugins/gtk/autoprofile/profile_accounts");
+ username = strdup (purple_account_get_username (account));
+ protocol_id = strdup (purple_account_get_protocol_id (account));
+
+ if (!enable) {
+ /* Remove from the list */
+ ap_debug ("profile", "Disabling profile updates for account");
+
+ while (original) {
+ if (!strcmp (original->data, username) &&
+ !strcmp (original->next->data, protocol_id)) {
+ node = original;
+ tmp = node->next;
+ original = original->next->next;
+ free (node->data);
+ free (tmp->data);
+ g_list_free_1 (node);
+ g_list_free_1 (tmp);
+ free (username);
+ free (protocol_id);
+ } else {
+ node = original;
+ original = original->next->next;
+ node->next->next = ret;
+ ret = node;
+ }
+ }
+
+ new = ret;
+ } else {
+ /* Otherwise add on */
+ GList *ret_start, *ret_end;
+
+ ap_debug ("profile", "enabling profile updates for account");
+
+ ret_start = (GList *) malloc (sizeof (GList));
+ ret_end = (GList *) malloc (sizeof (GList));
+ ret_start->data = username;
+ ret_start->next = ret_end;
+ ret_end->data = protocol_id;
+ ret_end->next = original;
+
+ new = ret_start;
+ }
+
+ purple_prefs_set_string_list (
+ "/plugins/gtk/autoprofile/profile_accounts", new);
+ ap_gtk_set_progress_visible (AP_UPDATE_PROFILE, (new != NULL));
+
+ free_string_list (new);
+}
+
+gboolean ap_account_has_profile_enabled (const PurpleAccount *account) {
+ GList *accounts_list, *start_list;
+
+ accounts_list = purple_prefs_get_string_list (
+ "/plugins/gtk/autoprofile/profile_accounts");
+
+ start_list = accounts_list;
+
+ /* Search through list of values */
+ while (accounts_list) {
+ // Make sure these things come in pairs
+ if (accounts_list->next == NULL) {
+ ap_debug_error ("is_account_profile_enabled", "invalid account string");
+ free_string_list (start_list);
+ return FALSE;
+ }
+
+ // Check usernames
+ if (!strcmp ((char *) accounts_list->data, account->username)) {
+ // Check protocol
+ if (!strcmp ((char *) accounts_list->next->data, account->protocol_id))
+ {
+ free_string_list (start_list);
+ return TRUE;
+ }
+ }
+
+ accounts_list = accounts_list->next->next;
+ }
+
+ /* Not found, hence it wasn't enabled */
+ free_string_list (start_list);
+ return FALSE;
+}
+
+/* Profiles: Update every so often */
+static gboolean ap_update_profile () {
+ PurpleAccount *account;
+ const GList *purple_accounts;
+ gboolean account_updated;
+
+ const char *format;
+ const char *old_info;
+ char *generated_profile;
+
+ /* Generate the profile text */
+ format = purple_prefs_get_string ("/plugins/gtk/autoprofile/profile");
+
+ if (format == NULL) {
+ ap_debug_error ("general", "profile is null");
+ return FALSE;
+ }
+
+ generated_profile = ap_generate (format, AP_SIZE_PROFILE_MAX);
+
+ // If string is blank, nothing would happen
+ if (*generated_profile == '\0') {
+ free (generated_profile);
+ ap_debug_misc ("general", "empty profile set");
+ generated_profile = strdup (" ");
+ }
+
+ /* Get all accounts and search through each */
+ account_updated = FALSE;
+ for (purple_accounts = purple_accounts_get_all ();
+ purple_accounts != NULL;
+ purple_accounts = purple_accounts->next) {
+ account = (PurpleAccount *)purple_accounts->data;
+ old_info = purple_account_get_user_info (account);
+
+ /* Check to see if update option set on account */
+ if (ap_account_has_profile_enabled (account) &&
+ (old_info == NULL || strcmp (old_info, generated_profile))) {
+ purple_account_set_user_info (account, generated_profile);
+ account_updated = TRUE;
+
+ if (purple_account_is_connected (account)) {
+ g_static_mutex_lock (&update_queue_mutex);
+ if (g_list_find (queued_profiles, account) == NULL) {
+ queued_profiles = g_list_append (queued_profiles, account);
+ }
+ g_static_mutex_unlock (&update_queue_mutex);
+ } else {
+ ap_debug_misc ("general", "account not online, not setting profile");
+ }
+ }
+ }
+
+ if (account_updated) {
+ ap_gtk_add_message (AP_UPDATE_PROFILE, AP_MESSAGE_TYPE_PROFILE,
+ generated_profile);
+ }
+
+ free (generated_profile);
+ return account_updated;
+}
+
+static gboolean ap_update_status ()
+{
+ const PurpleSavedStatus *template_status;
+ GHashTable *substatus_messages;
+ gchar *new_message, *new_substatus_message;
+ const gchar *sample_message, *old_message;
+ const GList *accounts;
+ gboolean updated;
+ PurpleStatusPrimitive old_type, new_type;
+ const PurpleStatusType *substatus_type;
+ PurpleAccount *account;
+ PurpleSavedStatusSub *substatus;
+
+ template_status = (using_idleaway? purple_savedstatus_get_idleaway () :
+ purple_savedstatus_get_current ());
+ updated = FALSE;
+
+ /* If there are substatuses */
+ if (purple_savedstatus_has_substatuses (template_status)) {
+ substatus_messages = g_hash_table_new (NULL, NULL);
+ for (accounts = purple_accounts_get_all ();
+ accounts != NULL;
+ accounts = accounts->next)
+ {
+ account = (PurpleAccount *) accounts->data;
+
+ substatus = purple_savedstatus_get_substatus (template_status, account);
+ if (substatus) {
+ new_type = purple_status_type_get_primitive (
+ purple_savedstatus_substatus_get_type (substatus));
+ sample_message =
+ purple_savedstatus_substatus_get_message (substatus);
+
+ if (sample_message) {
+ new_substatus_message = ap_generate (sample_message,
+ get_max_size_status (account, new_type));
+ } else {
+ new_substatus_message = NULL;
+ }
+
+ g_hash_table_insert (substatus_messages, account,
+ new_substatus_message);
+
+ if (!updated) {
+ old_type = ap_savedstatus_get_type (current_ap_status, account);
+ old_message =
+ ap_savedstatus_get_message (current_ap_status, account);
+
+ if ((old_type != new_type) ||
+ ((old_message == NULL || new_substatus_message == NULL) &&
+ (old_message != new_substatus_message)) ||
+ (old_message != NULL && new_substatus_message != NULL &&
+ strcmp (old_message, new_substatus_message)))
+ {
+ updated = TRUE;
+ }
+ }
+ }
+ }
+ } else {
+ substatus_messages = NULL;
+ }
+
+ /* And then the generic main message */
+ sample_message = purple_savedstatus_get_message (template_status);
+ if (sample_message) {
+ new_message = ap_generate (sample_message, get_max_size_status (NULL,
+ purple_savedstatus_get_type (template_status)));
+ } else {
+ new_message = NULL;
+ }
+
+ new_type = purple_savedstatus_get_type (template_status);
+
+ if (!updated) {
+ old_type = purple_savedstatus_get_type (current_ap_status);
+ old_message = purple_savedstatus_get_message (current_ap_status);
+
+ if ((old_type != new_type) ||
+ ((old_message == NULL || new_message == NULL) &&
+ (old_message != new_message)) ||
+ (old_message != NULL && new_message != NULL &&
+ strcmp (old_message, new_message)))
+ {
+ updated = TRUE;
+ }
+ }
+
+ if (updated) {
+ APMessageType type;
+ PurpleSavedStatus *new_status;
+
+ new_status = purple_savedstatus_new (NULL, new_type);
+
+ if (new_message) {
+ purple_savedstatus_set_message (new_status, new_message);
+ }
+
+ for (accounts = purple_accounts_get_all ();
+ accounts != NULL;
+ accounts = accounts->next) {
+ account = (PurpleAccount *) accounts->data;
+ substatus = purple_savedstatus_get_substatus (template_status, account);
+
+ if (substatus != NULL) {
+ substatus_type = purple_savedstatus_substatus_get_type (substatus);
+ new_substatus_message = (gchar *)
+ g_hash_table_lookup (substatus_messages, account);
+ purple_savedstatus_set_substatus (
+ new_status, account, substatus_type, new_substatus_message);
+ free (new_substatus_message);
+ }
+
+ purple_savedstatus_activate_for_account (new_status, account);
+ }
+
+ current_ap_status = new_status;
+
+ if (new_type == PURPLE_STATUS_AVAILABLE) type = AP_MESSAGE_TYPE_AVAILABLE;
+ else if (new_type == PURPLE_STATUS_AWAY) type = AP_MESSAGE_TYPE_AWAY;
+ else type = AP_MESSAGE_TYPE_STATUS;
+
+ ap_gtk_add_message (AP_UPDATE_STATUS, type, new_message);
+ }
+
+ if (new_message) free (new_message);
+ if (substatus_messages) {
+ g_hash_table_destroy (substatus_messages);
+ }
+
+ ap_update_queueing ();
+
+ return updated;
+}
+
+static gboolean ap_update_cb (gpointer data) {
+ gboolean result;
+ guint timeout;
+ guint delay;
+
+ g_static_mutex_lock (&update_timeout_mutex);
+
+ /* Start by removing timeout to self no matter what */
+ timeout = GPOINTER_TO_INT (g_hash_table_lookup (update_timeouts, data));
+ if (timeout) purple_timeout_remove (timeout);
+
+ /* In future, check here if widget content has changed? */
+
+ switch (GPOINTER_TO_INT (data)) {
+ case AP_UPDATE_STATUS:
+ result = ap_update_status ();
+ break;
+ case AP_UPDATE_PROFILE:
+ result = ap_update_profile ();
+ break;
+ default:
+ result = TRUE;
+ }
+
+ if (!result) {
+ ap_debug ("general", "Content hasn't changed, updating later");
+ delay = AP_SCHEDULE_UPDATE_DELAY;
+ } else {
+ ap_debug ("general", "Content updated");
+ delay =
+ purple_prefs_get_int ("/plugins/gtk/autoprofile/delay_update") * 1000;
+ }
+ timeout = purple_timeout_add (delay, ap_update_cb, data);
+ g_hash_table_insert (update_timeouts, data, GINT_TO_POINTER (timeout));
+
+ g_static_mutex_unlock (&update_timeout_mutex);
+
+ return FALSE;
+}
+
+void ap_update (APUpdateType type)
+{
+ ap_update_cb (GINT_TO_POINTER (type));
+}
+
+void ap_update_after_delay (APUpdateType type)
+{
+ guint timeout;
+
+ g_static_mutex_lock (&update_timeout_mutex);
+
+ timeout = GPOINTER_TO_INT (g_hash_table_lookup (update_timeouts,
+ GINT_TO_POINTER (type)));
+ if (timeout) purple_timeout_remove (timeout);
+
+ timeout = purple_timeout_add (AP_SCHEDULE_UPDATE_DELAY, ap_update_cb,
+ GINT_TO_POINTER (type));
+ g_hash_table_insert (update_timeouts, GINT_TO_POINTER (type),
+ GINT_TO_POINTER (timeout));
+
+ g_static_mutex_unlock (&update_timeout_mutex);
+}
+
+void ap_update_stop (APUpdateType type)
+{
+ guint timeout;
+
+ g_static_mutex_lock (&update_timeout_mutex);
+
+ timeout = GPOINTER_TO_INT (g_hash_table_lookup (update_timeouts,
+ GINT_TO_POINTER (type)));
+ if (timeout) purple_timeout_remove (timeout);
+
+ g_hash_table_insert (update_timeouts, GINT_TO_POINTER (type), 0);
+
+ g_static_mutex_unlock (&update_timeout_mutex);
+}
+
+static void ap_account_connected (PurpleConnection *gc) {
+ ap_debug ("general", "Account connection detected");
+ ap_update_after_delay (AP_UPDATE_PROFILE);
+ ap_update_after_delay (AP_UPDATE_STATUS);
+}
+
+void ap_update_queueing () {
+ if (ap_is_currently_away ()) {
+ if (purple_prefs_get_bool(
+ "/plugins/gtk/autoprofile/queue_messages_when_away")) {
+ purple_prefs_set_string (PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "away");
+ } else {
+ purple_prefs_set_string (PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "never");
+ }
+ }
+}
+
+/* Called whenever current status is changed by Purple's status menu
+ (in buddy list) */
+static void ap_status_changed (
+ const char *name, PurplePrefType type, gconstpointer val, gpointer data) {
+ ap_debug ("general", "Status change detected");
+
+ using_idleaway = FALSE;
+ ap_autoaway_touch ();
+ ap_update (AP_UPDATE_STATUS);
+}
+
+void ap_autoaway_enable () {
+ ap_debug ("idle", "Using idleaway");
+
+ using_idleaway = TRUE;
+ ap_update (AP_UPDATE_STATUS);
+}
+
+void ap_autoaway_disable () {
+ ap_debug ("idle", "Disabling idleaway");
+
+ using_idleaway = FALSE;
+ ap_update (AP_UPDATE_STATUS);
+}
+
+gboolean ap_autoaway_in_use () {
+ return using_idleaway;
+}
+
+static gboolean ap_update_queue (gpointer data)
+{
+ PurpleAccount *account = NULL;
+ PurpleConnection *gc = NULL;
+
+ g_static_mutex_lock (&update_queue_mutex);
+
+ if (queued_profiles != NULL) {
+ account = (PurpleAccount *) queued_profiles->data;
+ queued_profiles = queued_profiles->next;
+ }
+
+ g_static_mutex_unlock (&update_queue_mutex);
+
+ gc = purple_account_get_connection (account);
+ if (gc != NULL) {
+ serv_set_info (gc, purple_account_get_user_info (account));
+ }
+
+ return TRUE;
+}
+
+static void ap_update_queue_start ()
+{
+ update_queue_timeout = purple_timeout_add (2000, ap_update_queue, NULL);
+}
+
+static void ap_update_queue_finish ()
+{
+ purple_timeout_remove (update_queue_timeout);
+ update_queue_timeout = 0;
+}
+/*--------------------------------------------------------------------------*
+ * Preferences *
+ *--------------------------------------------------------------------------*/
+static void ap_delete_legacy_prefs () {
+ if (purple_prefs_exists ("/plugins/gtk/autoprofile/tab_number")) {
+ ap_debug ("general", "Deleting legacy preferences");
+
+ purple_prefs_remove ("/plugins/gtk/autoprofile/components");
+
+ purple_prefs_remove ("/plugins/gtk/autoprofile/tab_number");
+
+ purple_prefs_remove ("/plugins/gtk/autoprofile/accounts/enable_away");
+ purple_prefs_remove ("/plugins/gtk/autoprofile/accounts/enable_profile");
+ purple_prefs_remove ("/plugins/gtk/autoprofile/accounts");
+
+ purple_prefs_remove ("/plugins/gtk/autoprofile/message_titles");
+ purple_prefs_remove ("/plugins/gtk/autoprofile/message_texts");
+
+ purple_prefs_remove ("/plugins/gtk/autoprofile/default_profile");
+ purple_prefs_remove ("/plugins/gtk/autoprofile/default_away");
+ purple_prefs_remove ("/plugins/gtk/autoprofile/current_away");
+ purple_prefs_remove ("/plugins/gtk/autoprofile/added_text");
+
+ purple_prefs_remove ("/plugins/gtk/autoprofile/delay_profile");
+ purple_prefs_remove ("/plugins/gtk/autoprofile/delay_away");
+
+ purple_prefs_rename ("/plugins/gtk/autoprofile/text_respond",
+ "/plugins/gtk/autoprofile/autorespond/text");
+ purple_prefs_rename ("/plugins/gtk/autoprofile/text_trigger",
+ "/plugins/gtk/autoprofile/autorespond/trigger");
+ purple_prefs_rename ("/plugins/gtk/autoprofile/delay_respond",
+ "/plugins/gtk/autoprofile/autorespond/delay");
+ purple_prefs_rename ("/plugins/gtk/autoprofile/use_trigger",
+ "/plugins/gtk/autoprofile/autorespond/enable");
+ }
+}
+
+static void ap_init_preferences () {
+ ap_debug ("general", "Initializing preference defaults if necessary");
+
+ /* Adding the folders */
+ purple_prefs_add_none ("/plugins/gtk");
+ purple_prefs_add_none ("/plugins/gtk/autoprofile");
+ purple_prefs_add_none ("/plugins/gtk/autoprofile/widgets");
+ purple_prefs_add_none ("/plugins/gtk/autoprofile/autorespond");
+
+ /* Behavior-settings */
+ purple_prefs_add_int ("/plugins/gtk/autoprofile/delay_update", 30);
+ purple_prefs_add_string ("/plugins/gtk/autoprofile/show_summary", "always");
+ purple_prefs_add_bool ("/plugins/gtk/autoprofile/queue_messages_when_away",
+ FALSE);
+ purple_prefs_add_bool ("/plugins/gtk/autoprofile/away_when_idle",
+ purple_prefs_get_bool ("/core/away/away_when_idle"));
+
+ /* Auto-response settings */
+ purple_prefs_add_string ("/plugins/gtk/autoprofile/autorespond/auto_reply",
+ purple_prefs_get_string ("/core/away/auto_reply"));
+ purple_prefs_add_string ("/plugins/gtk/autoprofile/autorespond/text",
+ _("Say the magic word if you want me to talk more!"));
+ purple_prefs_add_string ("/plugins/gtk/autoprofile/autorespond/trigger",
+ _("please"));
+ purple_prefs_add_int ("/plugins/gtk/autoprofile/autorespond/delay", 2);
+ purple_prefs_add_bool ("/plugins/gtk/autoprofile/autorespond/enable", TRUE);
+
+ /* Profile settings */
+ purple_prefs_add_string_list(
+ "/plugins/gtk/autoprofile/profile_accounts", NULL);
+ purple_prefs_add_string ("/plugins/gtk/autoprofile/profile",
+ _("Get AutoProfile for Purple at <a href=\""
+ "http://autoprofile.sourceforge.net/\">"
+ "autoprofile.sourceforge.net</a><br><br>[Timestamp]"));
+}
+
+/*--------------------------------------------------------------------------*
+ * Last Call *
+ *--------------------------------------------------------------------------*/
+static void init_plugin (PurplePlugin *plugin)
+{
+ ap_debug ("general", "Initializing AutoProfile");
+
+ ap_init_preferences ();
+ ap_widget_init ();
+}
+
+PURPLE_INIT_PLUGIN (autoprofile, init_plugin, info)
+
============================================================
--- autoprofile/autoprofile.h 2645e7c203744b3873c426655f56e2d9c3f3479c
+++ autoprofile/autoprofile.h 2645e7c203744b3873c426655f56e2d9c3f3479c
@@ -0,0 +1,112 @@
+/*--------------------------------------------------------------------------*
+ * AUTOPROFILE *
+ * *
+ * A Purple away message and profile manager that supports dynamic text *
+ * *
+ * AutoProfile is the legal property of its developers. Please refer to *
+ * the COPYRIGHT file distributed with this source distribution. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ *--------------------------------------------------------------------------*/
+
+#ifndef AUTOPROFILE_H
+#define AUTOPROFILE_H
+
+#define AUTOPROFILE_VERSION "3.00-beta2"
+
+#include "internal.h"
+
+#include "sizes.h"
+#include "widget.h"
+#include "utility.h"
+
+#include "plugin.h"
+#include "gtkplugin.h"
+
+#include "signals.h"
+#include "prefs.h"
+#include "util.h"
+#include "notify.h"
+
+#include "string.h"
+#include "time.h"
+
+#define AP_SCHEDULE_UPDATE_DELAY 3000
+#define AP_GTK_MAX_MESSAGES 50
+
+/* Data types */
+typedef enum
+{
+ AP_MESSAGE_TYPE_OTHER = -1,
+ AP_MESSAGE_TYPE_PROFILE,
+ AP_MESSAGE_TYPE_AWAY,
+ AP_MESSAGE_TYPE_AVAILABLE,
+ AP_MESSAGE_TYPE_STATUS
+} APMessageType;
+
+typedef enum
+{
+ AP_UPDATE_UNKNOWN = 0,
+ AP_UPDATE_STATUS,
+ AP_UPDATE_PROFILE
+} APUpdateType;
+
+/* Variable access functions */
+PurplePlugin *ap_get_plugin_handle ();
+gboolean ap_is_currently_away ();
+
+void ap_account_enable_profile (const PurpleAccount *, gboolean);
+gboolean ap_account_has_profile_enabled (const PurpleAccount *);
+
+/* Core behavior functions */
+gchar *ap_generate (const char *, gint);
+gchar *ap_get_sample_status_message (PurpleAccount *account);
+void ap_update (APUpdateType);
+void ap_update_after_delay (APUpdateType);
+void ap_update_stop (APUpdateType);
+
+/* Queueing functions */
+void ap_update_queueing ();
+
+/* Auto-away functions */
+void ap_autoaway_start ();
+void ap_autoaway_finish ();
+void ap_autoaway_touch ();
+void ap_autoaway_enable ();
+void ap_autoaway_disable ();
+gboolean ap_autoaway_in_use ();
+
+/* Auto-reply functions */
+void ap_autoreply_start ();
+void ap_autoreply_finish ();
+
+/* Gtk Away Messages */
+void ap_gtk_start ();
+void ap_gtk_finish ();
+void ap_gtk_make_visible ();
+void ap_gtk_add_message (APUpdateType, APMessageType, const gchar *);
+void ap_gtk_set_progress_visible (APUpdateType, gboolean);
+
+/* Gtk Actions */
+GList *actions (PurplePlugin *, gpointer);
+void ap_actions_finish ();
+
+/* Preferences */
+PidginPluginUiInfo ui_info;
+void ap_preferences_display ();
+void ap_gtk_prefs_add_summary_option (GtkWidget *);
+GtkWidget *get_account_page ();
+
+#endif /* #ifndef AUTOPROFILE_H */
============================================================
--- autoprofile/autoreply.c cd281e6a397afe1b0e094497170e18a7abdc0906
+++ autoprofile/autoreply.c cd281e6a397afe1b0e094497170e18a7abdc0906
@@ -0,0 +1,324 @@
+/*--------------------------------------------------------------------------*
+ * AUTOPROFILE *
+ * *
+ * A Purple away message and profile manager that supports dynamic text *
+ * *
+ * AutoProfile is the legal property of its developers. Please refer to *
+ * the COPYRIGHT file distributed with this source distribution. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ *--------------------------------------------------------------------------*/
+
+#include "autoprofile.h"
+#include "conversation.h"
+
+#define SECS_BEFORE_RESENDING_AUTORESPONSE 600
+#define SEX_BEFORE_RESENDING_AUTORESPONSE "Only after you're married"
+#define MILLISECS_BEFORE_PROCESSING_MSG 100
+
+static guint pref_cb;
+
+static GSList *last_auto_responses = NULL;
+struct last_auto_response {
+ PurpleConnection *gc;
+ char name[80];
+ time_t sent;
+};
+
+static time_t response_timeout = 0;
+
+/*--------------------------------------------------------------------------*
+ * Auto-response utility functions *
+ *--------------------------------------------------------------------------*/
+static gboolean
+expire_last_auto_responses(gpointer data)
+{
+ GSList *tmp, *cur;
+ struct last_auto_response *lar;
+
+ tmp = last_auto_responses;
+
+ while (tmp) {
+ cur = tmp;
+ tmp = tmp->next;
+ lar = (struct last_auto_response *)cur->data;
+
+ if ((time(NULL) - lar->sent) > SECS_BEFORE_RESENDING_AUTORESPONSE) {
+ last_auto_responses = g_slist_remove(last_auto_responses, lar);
+ g_free(lar);
+ }
+ }
+
+ return FALSE; /* do not run again */
+}
+
+static struct last_auto_response *
+get_last_auto_response(PurpleConnection *gc, const char *name)
+{
+ GSList *tmp;
+ struct last_auto_response *lar;
+
+ /* because we're modifying or creating a lar, schedule the
+ * function to expire them as the pref dictates */
+ purple_timeout_add((SECS_BEFORE_RESENDING_AUTORESPONSE + 5) * 1000,
+ expire_last_auto_responses, NULL);
+
+ tmp = last_auto_responses;
+
+ while (tmp) {
+ lar = (struct last_auto_response *)tmp->data;
+
+ if (gc == lar->gc && !strncmp(name, lar->name, sizeof(lar->name)))
+ return lar;
+
+ tmp = tmp->next;
+ }
+
+ lar = (struct last_auto_response *)g_new0(struct last_auto_response, 1);
+ g_snprintf(lar->name, sizeof(lar->name), "%s", name);
+ lar->gc = gc;
+ lar->sent = 0;
+ last_auto_responses = g_slist_append(last_auto_responses, lar);
+
+ return lar;
+}
+
+/*--------------------------------------------------------------------------*
+ * Message send/receive general functionality *
+ *--------------------------------------------------------------------------*/
+/* Detecting sent message stuff */
+static void sent_im_msg_cb (PurpleAccount *account, const char *receiver,
+ const char *message)
+{
+ PurpleConnection *gc;
+ PurplePresence *presence;
+ const gchar *auto_reply_pref;
+
+ gc = purple_account_get_connection (account);
+ presence = purple_account_get_presence (account);
+
+ /*
+ * FIXME - If "only auto-reply when away & idle" is set, then shouldn't
+ * this only reset lar->sent if we're away AND idle?
+ */
+ auto_reply_pref =
+ purple_prefs_get_string ("/plugins/gtk/autoprofile/autorespond/auto_reply");
+ if ((gc->flags & PURPLE_CONNECTION_AUTO_RESP) &&
+ !purple_presence_is_available(presence) &&
+ strcmp(auto_reply_pref, "never"))
+ {
+ struct last_auto_response *lar;
+ lar = get_last_auto_response(gc, receiver);
+ lar->sent = time(NULL);
+ }
+}
+
+/* Detecting received message stuff */
+struct received_im_msg {
+ PurpleAccount *account;
+ char *sender;
+ char *message;
+};
+
+static gint process_received_im_msg (gpointer data)
+{
+ struct received_im_msg *received_im;
+ PurpleAccount *account;
+ char *sender;
+ char *message;
+ PurpleConnection *gc;
+ PurpleConversation *conv;
+
+ received_im = (struct received_im_msg *) data;
+ account = received_im->account;
+ sender = received_im->sender;
+ message = received_im->message;
+ free (data);
+
+ gc = purple_account_get_connection (account);
+
+ /* search for conversation again in case it was created by other handlers */
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+ sender, gc->account);
+ if (conv == NULL)
+ conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
+
+ /*
+ * Don't autorespond if:
+ *
+ * - it's not supported on this connection
+ * - we are available
+ * - or it's disabled
+ * - or we're not idle and the 'only auto respond if idle' pref
+ * is set
+ */
+ if (gc->flags & PURPLE_CONNECTION_AUTO_RESP)
+ {
+ PurplePresence *presence;
+ PurpleStatus *status;
+ PurpleStatusType *status_type;
+ PurpleStatusPrimitive primitive;
+ const gchar *auto_reply_pref;
+ char *away_msg = NULL;
+
+ auto_reply_pref = purple_prefs_get_string(
+ "/plugins/gtk/autoprofile/autorespond/auto_reply");
+
+ presence = purple_account_get_presence(account);
+ status = purple_presence_get_active_status(presence);
+ status_type = purple_status_get_type(status);
+ primitive = purple_status_type_get_primitive(status_type);
+ if ((primitive == PURPLE_STATUS_AVAILABLE) ||
+ (primitive == PURPLE_STATUS_INVISIBLE) ||
+ (primitive == PURPLE_STATUS_MOBILE) ||
+ !strcmp(auto_reply_pref, "never") ||
+ (!purple_presence_is_idle(presence) &&
+ !strcmp(auto_reply_pref, "awayidle")))
+ {
+ free (sender);
+ free (message);
+ return FALSE;
+ }
+
+ away_msg = ap_get_sample_status_message (account);
+
+ if ((away_msg != NULL) && (*away_msg != '\0')) {
+ struct last_auto_response *lar;
+ gboolean autorespond_enable;
+ time_t now = time(NULL);
+
+ autorespond_enable = purple_prefs_get_bool (
+ "/plugins/gtk/autoprofile/autorespond/enable");
+ /*
+ * This used to be based on the conversation window. But um, if
+ * you went away, and someone sent you a message and got your
+ * auto-response, and then you closed the window, and then they
+ * sent you another one, they'd get the auto-response back too
+ * soon. Besides that, we need to keep track of this even if we've
+ * got a queue. So the rest of this block is just the auto-response,
+ * if necessary.
+ */
+ lar = get_last_auto_response(gc, sender);
+ if ((now - lar->sent) >= SECS_BEFORE_RESENDING_AUTORESPONSE) {
+ lar->sent = now;
+ // Send basic autoresponse
+ serv_send_im (gc, sender, away_msg, PURPLE_MESSAGE_AUTO_RESP);
+ purple_conv_im_write (PURPLE_CONV_IM(conv), NULL, away_msg,
+ PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP,
+ now);
+
+ // Send additional hint if enabled
+ if (autorespond_enable) {
+ const gchar *query = purple_prefs_get_string (
+ "/plugins/gtk/autoprofile/autorespond/text");
+ serv_send_im (gc, sender, query, PURPLE_MESSAGE_AUTO_RESP);
+ purple_conv_im_write (PURPLE_CONV_IM (conv), NULL, query,
+ PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP,
+ now);
+ }
+
+ } else if (autorespond_enable &&
+ difftime (time(NULL), response_timeout) >
+ purple_prefs_get_int ("/plugins/gtk/autoprofile/autorespond/delay")) {
+ gchar *text = purple_markup_strip_html (message);
+ if (match_start (text, purple_prefs_get_string (
+ "/plugins/gtk/autoprofile/autorespond/trigger")) == 1) {
+ serv_send_im (gc, sender, away_msg, PURPLE_MESSAGE_AUTO_RESP);
+ purple_conv_im_write (PURPLE_CONV_IM (conv), NULL, away_msg,
+ PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP,
+ now);
+
+ response_timeout = time (NULL);
+ ap_debug ("autorespond", "string matched, responding");
+ }
+ free (text);
+ }
+ }
+
+ free (away_msg);
+ }
+
+ free (sender);
+ free (message);
+
+ return FALSE;
+}
+
+static void received_im_msg_cb (PurpleAccount *account, char *sender,
+ char *message, PurpleConversation *conv, PurpleMessageFlags flags)
+{
+ struct received_im_msg *received_im;
+
+ received_im =
+ (struct received_im_msg *) malloc (sizeof (struct received_im_msg));
+ received_im->account = account;
+ received_im->sender = strdup (sender);
+ received_im->message = strdup (message);
+
+ purple_timeout_add (MILLISECS_BEFORE_PROCESSING_MSG, process_received_im_msg,
+ received_im);
+}
+
+static void auto_pref_cb (
+ const char *name, PurplePrefType type, gconstpointer val, gpointer data)
+{
+ if (!strcmp (purple_prefs_get_string ("/core/away/auto_reply"), "never"))
+ return;
+
+ purple_notify_error (NULL, NULL,
+ N_("This preference is disabled"),
+ N_("This preference currently has no effect because AutoProfile is in "
+ "use. To modify this behavior, use the AutoProfile configuration "
+ "menu."));
+
+ purple_prefs_set_string ("/core/away/auto_reply", "never");
+}
+
+/*--------------------------------------------------------------------------*
+ * Global functions *
+ *--------------------------------------------------------------------------*/
+void ap_autoreply_start ()
+{
+ purple_prefs_set_string ("/core/away/auto_reply", "never");
+
+ purple_signal_connect (purple_conversations_get_handle (), "sent-im-msg",
+ ap_get_plugin_handle (), PURPLE_CALLBACK(sent_im_msg_cb), NULL);
+ purple_signal_connect (purple_conversations_get_handle (), "received-im-msg",
+ ap_get_plugin_handle (), PURPLE_CALLBACK(received_im_msg_cb), NULL);
+
+ pref_cb = purple_prefs_connect_callback (ap_get_plugin_handle (),
+ "/core/away/auto_reply", auto_pref_cb, NULL);
+}
+
+void ap_autoreply_finish ()
+{
+ GSList *tmp;
+
+ // Assumes signals are disconnected globally
+
+ purple_prefs_disconnect_callback (pref_cb);
+ pref_cb = 0;
+
+ purple_prefs_set_string ("/core/away/auto_reply", purple_prefs_get_string (
+ "/plugins/gtk/autoprofile/autorespond/auto_reply"));
+
+ while (last_auto_responses) {
+ tmp = last_auto_responses->next;
+ g_free (last_auto_responses->data);
+ g_slist_free_1 (last_auto_responses);
+ last_auto_responses = tmp;
+ }
+}
+
============================================================
--- autoprofile/comp_countdownup.c 46ebaf0f9305871d403a712c26f4ff46005a4ce2
+++ autoprofile/comp_countdownup.c 46ebaf0f9305871d403a712c26f4ff46005a4ce2
@@ -0,0 +1,434 @@
+/*--------------------------------------------------------------------------*
+ * AUTOPROFILE *
+ * *
+ * A Purple away message and profile manager that supports dynamic text *
+ * *
+ * AutoProfile is the legal property of its developers. Please refer to *
+ * the COPYRIGHT file distributed with this source distribution. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ *--------------------------------------------------------------------------*/
+
+#include "component.h"
+#include "gtkprefs.h"
+#include "utility.h"
+
+static GtkWidget *spin_secs;
+static GtkWidget *spin_mins;
+static GtkWidget *spin_hour;
+static GtkWidget *spin_day;
+static GtkWidget *spin_month;
+static GtkWidget *spin_year;
+
+/* Generate the time! */
+char *count_generate (struct widget *w)
+{
+ double d_secs, d_mins, d_hours, d_days;
+ char *s_secs, *s_mins, *s_hours, *s_days;
+ double difference;
+ int l, s;
+
+ struct tm *ref_time;
+ char *result;
+
+ ref_time = (struct tm *) malloc (sizeof (struct tm));
+
+ ref_time->tm_sec = ap_prefs_get_int (w, "secs");
+ ref_time->tm_min = ap_prefs_get_int (w, "mins");
+ ref_time->tm_hour = ap_prefs_get_int (w, "hour");
+ ref_time->tm_mday = ap_prefs_get_int (w, "day");
+ ref_time->tm_mon = ap_prefs_get_int (w, "month") - 1;
+ ref_time->tm_year = ap_prefs_get_int (w, "year") - 1900;
+ ref_time->tm_isdst = -1;
+
+ mktime (ref_time);
+
+ if (ap_prefs_get_int (w, "down") == 1)
+ difference = difftime (mktime (ref_time), time(NULL));
+ else
+ difference = difftime (time(NULL), mktime (ref_time));
+
+ if (difference < 0) {
+ d_secs = 0;
+ d_mins = 0;
+ d_hours = 0;
+ d_days = 0;
+ } else {
+ d_mins = floor (difference / 60);
+ d_secs = difference - (d_mins * 60);
+ d_hours = floor (d_mins / 60);
+ d_mins = d_mins - (d_hours * 60);
+ d_days = floor (d_hours / 24);
+ d_hours = d_hours - (d_days * 24);
+ }
+
+ result = (char *)malloc(sizeof (char) * AP_SIZE_MAXIMUM);
+ l = ap_prefs_get_int (w, "large");
+ s = ap_prefs_get_int (w, "small");
+
+ if (l < s) {
+ g_snprintf(result, AP_SIZE_MAXIMUM,
+ "%.0f days, %.0f hours, %.0f minutes, %.0f seconds",
+ d_days, d_hours, d_mins, d_secs);
+ free (ref_time);
+ return result;
+ }
+
+ if (l < 3)
+ d_hours = d_hours + (d_days * 24);
+ if (l < 2)
+ d_mins = d_mins + (d_hours * 60);
+ if (l < 1)
+ d_secs = d_secs + (d_mins * 60);
+
+ if (d_days == 1.0)
+ s_days = strdup ("day");
+ else
+ s_days = strdup ("days");
+
+ if (d_hours == 1.0)
+ s_hours = strdup ("hour");
+ else
+ s_hours = strdup ("hours");
+
+ if (d_mins == 1.0)
+ s_mins = strdup ("minute");
+ else
+ s_mins = strdup ("minutes");
+
+ if (d_secs == 1.0)
+ s_secs = strdup ("second");
+ else
+ s_secs = strdup ("seconds");
+
+ switch (l) {
+ case 3:
+ switch (s) {
+ case 3:
+ g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s",
+ d_days, s_days);
+ break;
+ case 2:
+ g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s",
+ d_days, s_days, d_hours, s_hours);
+ break;
+ case 1:
+ g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s, %.0f %s",
+ d_days, s_days, d_hours, s_hours, d_mins, s_mins);
+ break;
+ case 0:
+ g_snprintf (result, AP_SIZE_MAXIMUM,
+ "%.0f %s, %.0f %s, %.0f %s, %.0f %s",
+ d_days, s_days, d_hours, s_hours, d_mins, s_mins, d_secs, s_secs);
+ break;
+ default:
+ *result = '\0';
+ }
+ break;
+ case 2:
+ switch (s) {
+ case 2:
+ g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s",
+ d_hours, s_hours);
+ break;
+ case 1:
+ g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s",
+ d_hours, s_hours, d_mins, s_mins);
+ break;
+ case 0:
+ g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s, %.0f %s",
+ d_hours, s_hours, d_mins, s_mins, d_secs, s_secs);
+ break;
+ default:
+ *result = '\0';
+ }
+ break;
+ case 1:
+ switch (s) {
+ case 1:
+ g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s",
+ d_mins, s_mins);
+ break;
+ case 0:
+ g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s, %.0f %s",
+ d_mins, s_mins, d_secs, s_secs);
+ break;
+ default:
+ *result = '\0';
+ }
+ break;
+ case 0:
+ g_snprintf (result, AP_SIZE_MAXIMUM, "%.0f %s",
+ d_secs, s_secs);
+ break;
+ default:
+ *result = '\0';
+ }
+
+ free (s_days);
+ free (s_hours);
+ free (s_mins);
+ free (s_secs);
+ free (ref_time);
+
+ return result;
+}
+
+static void update_year (GtkSpinButton *spinner, struct widget *w)
+{
+ int value = gtk_spin_button_get_value_as_int (spinner);
+ ap_prefs_set_int (w, "year", value);
+}
+
+static void update_month (GtkSpinButton *spinner, struct widget *w)
+{
+ int value = gtk_spin_button_get_value_as_int (spinner);
+ ap_prefs_set_int (w, "month", value);
+}
+
+static void update_day (GtkSpinButton *spinner, struct widget *w)
+{
+ int value = gtk_spin_button_get_value_as_int (spinner);
+ ap_prefs_set_int (w, "day", value);
+}
+
+static void update_hour (GtkSpinButton *spinner, struct widget *w)
+{
+ int value = gtk_spin_button_get_value_as_int (spinner);
+ ap_prefs_set_int (w, "hour", value);
+}
+
+static void update_mins (GtkSpinButton *spinner, struct widget *w)
+{
+ int value = gtk_spin_button_get_value_as_int (spinner);
+ ap_prefs_set_int (w, "mins", value);
+}
+
+static void update_secs (GtkSpinButton *spinner, struct widget *w)
+{
+ int value = gtk_spin_button_get_value_as_int (spinner);
+ ap_prefs_set_int (w, "secs", value);
+}
+
+static void set_to_current_time (GtkButton *button, struct widget *w)
+{
+ time_t the_time;
+ struct tm *ref_time;
+
+ the_time = time(NULL);
+ ref_time = ap_localtime(&the_time);
+ ap_prefs_set_int (w, "year", ref_time->tm_year + 1900);
+ ap_prefs_set_int (w, "month", ref_time->tm_mon + 1);
+ ap_prefs_set_int (w, "day", ref_time->tm_mday);
+ ap_prefs_set_int (w, "hour", ref_time->tm_hour);
+ ap_prefs_set_int (w, "mins", ref_time->tm_min);
+ ap_prefs_set_int (w, "secs", ref_time->tm_sec);
+ free (ref_time);
+
+ if (spin_secs != NULL) {
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_secs),
+ ap_prefs_get_int (w, "secs"));
+ }
+ if (spin_mins != NULL) {
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_mins),
+ ap_prefs_get_int (w, "mins"));
+ }
+ if (spin_hour != NULL) {
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_hour),
+ ap_prefs_get_int (w, "hour"));
+ }
+ if (spin_day != NULL) {
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_day),
+ ap_prefs_get_int (w, "day"));
+ }
+ if (spin_month != NULL) {
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_month),
+ ap_prefs_get_int (w, "month"));
+ }
+ if (spin_year != NULL) {
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_year),
+ ap_prefs_get_int (w, "year"));
+ }
+}
+
+GtkWidget *count_menu (struct widget *w)
+{
+ GtkWidget *vbox, *hbox, *big_hbox, *frame;
+ GtkWidget *label, *spinner, *dropbox, *button;
+ GList *options;
+
+ big_hbox = gtk_hbox_new (FALSE, 6);
+
+ frame = pidgin_make_frame (big_hbox, _("Start/end time"));
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ label = gtk_label_new (_("Year: "));
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
+ spinner = gtk_spin_button_new_with_range (1970, 2035, 1);
+ gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
+ ap_prefs_get_int (w, "year"));
+ g_signal_connect (G_OBJECT (spinner), "value-changed",
+ G_CALLBACK (update_year), w);
+ spin_year = spinner;
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ label = gtk_label_new (_("Month: "));
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
+ spinner = gtk_spin_button_new_with_range (1, 12, 1);
+ gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
+ ap_prefs_get_int (w, "month"));
+ g_signal_connect (G_OBJECT (spinner), "value-changed",
+ G_CALLBACK (update_month), w);
+ spin_month = spinner;
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ label = gtk_label_new (_("Day: "));
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
+ spinner = gtk_spin_button_new_with_range (1, 31, 1);
+ gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
+ ap_prefs_get_int (w, "day"));
+ g_signal_connect (G_OBJECT (spinner), "value-changed",
+ G_CALLBACK (update_day), w);
+ spin_day = spinner;
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ label = gtk_label_new (_("Hour: "));
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
+ spinner = gtk_spin_button_new_with_range (0, 23, 1);
+ gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
+ ap_prefs_get_int (w, "hour"));
+ g_signal_connect (G_OBJECT (spinner), "value-changed",
+ G_CALLBACK (update_hour), w);
+ spin_hour = spinner;
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ label = gtk_label_new (_("Minutes: "));
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
+ spinner = gtk_spin_button_new_with_range (0, 59, 1);
+ gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
+ ap_prefs_get_int (w, "mins"));
+ g_signal_connect (G_OBJECT (spinner), "value-changed",
+ G_CALLBACK (update_mins), w);
+ spin_mins = spinner;
+
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ label = gtk_label_new (_("Seconds: "));
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+ gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
+ spinner = gtk_spin_button_new_with_range (0, 59, 1);
+ gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
+ ap_prefs_get_int (w, "secs"));
+ g_signal_connect (G_OBJECT (spinner), "value-changed",
+ G_CALLBACK (update_secs), w);
+ spin_secs = spinner;
+
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ button = gtk_button_new_with_label ("Set to current time");
+
+ g_signal_connect (G_OBJECT (button), "clicked",
+ G_CALLBACK (set_to_current_time), w);
+ gtk_box_pack_start (GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+ frame = pidgin_make_frame (big_hbox, _("Which way"));
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ options = g_list_append (NULL, (char *) _("Count down to stop date"));
+ options = g_list_append (options, GINT_TO_POINTER(1));
+ options = g_list_append (options, (char *)
+ _("Count time since start date"));
+ options = g_list_append (options, GINT_TO_POINTER(0));
+
+ dropbox = ap_prefs_dropdown_from_list (w, vbox, NULL,
+ PURPLE_PREF_INT, "down", options);
+ g_list_free (options);
+
+ options = g_list_append (NULL, (char *) _("Days"));
+ options = g_list_append (options, GINT_TO_POINTER(3));
+ options = g_list_append (options, (char *) _("Hours"));
+ options = g_list_append (options, GINT_TO_POINTER(2));
+ options = g_list_append (options, (char *) _("Minutes"));
+ options = g_list_append (options, GINT_TO_POINTER(1));
+ options = g_list_append (options, (char *) _("Seconds"));
+ options = g_list_append (options, GINT_TO_POINTER(0));
+
+ dropbox = ap_prefs_dropdown_from_list (w, vbox,
+ _("Largest units displayed"), PURPLE_PREF_INT, "large", options);
+ dropbox = ap_prefs_dropdown_from_list (w, vbox,
+ _("Smallest units displayed"), PURPLE_PREF_INT, "small", options);
+ g_list_free (options);
+
+ return big_hbox;
+}
+
+/* Init prefs */
+void count_init (struct widget *w) {
+ time_t the_time;
+ struct tm *ref_time;
+
+ the_time = time(NULL);
+ ref_time = ap_localtime(&the_time);
+
+ ap_prefs_add_int (w, "down", 1);
+ ap_prefs_add_int (w, "small", 0);
+ ap_prefs_add_int (w, "large", 3);
+ ap_prefs_add_int (w, "year",
+ ref_time->tm_year + 1900);
+ ap_prefs_add_int (w, "month",
+ ref_time->tm_mon + 1);
+ ap_prefs_add_int (w, "day",
+ ref_time->tm_mday);
+ ap_prefs_add_int (w, "hour",
+ ref_time->tm_hour);
+ ap_prefs_add_int (w, "mins",
+ ref_time->tm_min);
+ ap_prefs_add_int (w, "secs",
+ ref_time->tm_sec);
+ free (ref_time);
+}
+
+struct component count =
+{
+ N_("Countdown timer"),
+ N_("Given a date, shows amount of time until it (or since it)"),
+ "Timer",
+ &count_generate,
+ &count_init,
+ NULL,
+ NULL,
+ NULL,
+ &count_menu
+};
+
============================================================
--- autoprofile/comp_executable.c c49398a63c6b16ed80e8ff223698cba94fe0fec3
+++ autoprofile/comp_executable.c c49398a63c6b16ed80e8ff223698cba94fe0fec3
@@ -0,0 +1,165 @@
+/*--------------------------------------------------------------------------*
+ * AUTOPROFILE *
+ * *
+ * A Purple away message and profile manager that supports dynamic text *
+ * *
+ * AutoProfile is the legal property of its developers. Please refer to *
+ * the COPYRIGHT file distributed with this source distribution. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ *--------------------------------------------------------------------------*/
+
+#include "component.h"
+#include "utility.h"
+
+/*---------- EXECUTABLE: STDOUT from a program ----------*/
+static GtkWidget *file_selector;
+static GtkWidget *file_entry;
+
+/* Read file into string and return */
+char *executable_generate (struct widget *w)
+{
+ char *text, *text_start;
+ int max;
+ gboolean exec;
+ GError *return_error;
+
+ max = ap_prefs_get_int (w, "max_size");
+ exec = g_spawn_command_line_sync (ap_prefs_get_string (w, "command"),
+ &text_start, NULL, NULL, &return_error);
+
+ if (!exec) {
+ /* Excution failed */
+ ap_debug ("executable", "command failed to execute");
+ return strdup (_("[ERROR: command failed to execute]"));
+ }
+
+ if (strlen (text_start) < max)
+ text = text_start + strlen(text_start);
+ else
+ text = text_start + max;
+
+ /* Should back off only if the last item is newline */
+ /* Gets rid of the extra <BR> in output */
+ text--;
+ if (*text != '\n')
+ text++;
+
+ *text = '\0';
+ return text_start;
+}
+
+void executable_filename (GtkWidget *widget, gpointer user_data) {
+ const gchar *selected_filename;
+
+ selected_filename = gtk_file_selection_get_filename (
+ GTK_FILE_SELECTION (file_selector));
+
+ ap_prefs_set_string ((struct widget *) user_data, "command",
+ selected_filename);
+ gtk_entry_set_text (GTK_ENTRY (file_entry), selected_filename);
+}
+
+/* Creates and pops up file selection dialog for fortune file */
+void executable_selection (GtkWidget *widget, struct widget *w) {
+ const char *cur_file;
+
+ /* Create the selector */
+ file_selector = gtk_file_selection_new (
+ "Select the location of the program");
+
+ cur_file = ap_prefs_get_string (w, "command");
+ if (strlen (cur_file) > 1) {
+ gtk_file_selection_set_filename (
+ GTK_FILE_SELECTION (file_selector), cur_file);
+ }
+
+ g_signal_connect (GTK_OBJECT(
+ GTK_FILE_SELECTION(file_selector)->ok_button),
+ "clicked", G_CALLBACK (executable_filename), w);
+
+ /* Destroy dialog box when the user clicks button. */
+ g_signal_connect_swapped (GTK_OBJECT(
+ GTK_FILE_SELECTION(file_selector)->ok_button),
+ "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) file_selector);
+
+ g_signal_connect_swapped (GTK_OBJECT (
+ GTK_FILE_SELECTION (file_selector)->cancel_button),
+ "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) file_selector);
+
+ /* Display dialog */
+ gtk_widget_show (file_selector);
+}
+
+static gboolean executable_update (GtkWidget *widget, GdkEventFocus *evt,
+ gpointer data)
+{
+ ap_prefs_set_string ((struct widget *) data, "command",
+ gtk_entry_get_text (GTK_ENTRY (file_entry)));
+ return FALSE;
+}
+
+/* Create the menu */
+GtkWidget *executable_menu (struct widget *w)
+{
+ GtkWidget *ret = gtk_vbox_new (FALSE, 5);
+ GtkWidget *hbox, *label, *button;
+
+ label = gtk_label_new (
+ _("Specify the command line you wish to execute"));
+ gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+
+ hbox = gtk_hbox_new (FALSE, 5);
+ gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0);
+ /* Text entry to type in program name */
+ file_entry = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), file_entry, FALSE, FALSE, 0);
+ gtk_entry_set_text (GTK_ENTRY (file_entry),
+ ap_prefs_get_string (w, "command"));
+ g_signal_connect (G_OBJECT (file_entry), "focus-out-event",
+ G_CALLBACK (executable_update), w);
+ /* Button to bring up file select dialog */
+ button = gtk_button_new_with_label ("Browse for program");
+ g_signal_connect (G_OBJECT (button), "clicked",
+ G_CALLBACK (executable_selection), w);
+
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+ ap_prefs_labeled_spin_button (w, ret,
+ _("Max characters to read from output: "), "max_size",
+ 1, AP_SIZE_MAXIMUM, NULL);
+
+ return ret;
+}
+
+void executable_init (struct widget *w) {
+ ap_prefs_add_string (w, "command", "date");
+ ap_prefs_add_int (w, "max_size", 1000);
+}
+
+struct component executable =
+{
+ N_("Command Line"),
+ N_("Reproduces standard output of running a program on the command line"),
+ "Command",
+ &executable_generate,
+ &executable_init,
+ NULL,
+ NULL,
+ NULL,
+ &executable_menu
+};
+
============================================================
--- autoprofile/comp_http.c 8f9e5686d80c9146a87227399302614c56c0d3c5
+++ autoprofile/comp_http.c 8f9e5686d80c9146a87227399302614c56c0d3c5
@@ -0,0 +1,202 @@
+/*--------------------------------------------------------------------------*
+ * AUTOPROFILE *
+ * *
+ * A Purple away message and profile manager that supports dynamic text *
+ * *
+ * AutoProfile is the legal property of its developers. Please refer to *
+ * the COPYRIGHT file distributed with this source distribution. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ *--------------------------------------------------------------------------*/
+
+#include "component.h"
+
+static GHashTable *refresh_timeouts = NULL;
+
+/*---------- HTTP: HTTP requested Data ----------*/
+static void http_response (PurpleUtilFetchUrlData *reuqest_data, gpointer data, const char *c, gsize len, const gchar *error_message)
+{
+ struct widget *w;
+ w = (struct widget *) data;
+
+ // Invalid URL!
+ if (c == NULL) {
+ ap_prefs_set_string (w, "http_data",
+ _("[AutoProfile error: Invalid URL or no internet connection]"));
+ return;
+ }
+
+ w = (struct widget *) data;
+ ap_prefs_set_string (w, "http_data", c);
+}
+
+static char* http_generate (struct widget *w)
+{
+ const char *result, *url;
+
+ url = ap_prefs_get_string (w, "http_url");
+ if (!url || url[0] == '\0') {
+ return strdup (_("[AutoProfile error: No URL specified]"));
+ }
+
+ result = ap_prefs_get_string (w, "http_data");
+ if (result == NULL) return strdup ("");
+ return strdup (result);
+}
+
+static gboolean http_refresh_update (gpointer user_data)
+{
+ struct widget *w;
+ char *http_url;
+
+ w = (struct widget *) user_data;
+ http_url = strdup (ap_prefs_get_string (w, "http_url"));
+
+ if( http_url && (http_url[0] != '\0') ) {
+ purple_util_fetch_url(http_url, TRUE, NULL, FALSE, http_response, w);
+ } else {
+ ap_prefs_set_string (w, "http_data", "");
+ }
+
+ free (http_url);
+ return TRUE;
+}
+
+static void http_load (struct widget *w)
+{
+ gpointer http_refresh_timeout;
+
+ if (refresh_timeouts == NULL) {
+ refresh_timeouts = g_hash_table_new (NULL, NULL);
+ }
+
+ http_refresh_update (w);
+ http_refresh_timeout = GINT_TO_POINTER (g_timeout_add (
+ ap_prefs_get_int (w, "http_refresh_mins") * 60 * 1000,
+ http_refresh_update, w));
+ g_hash_table_insert (refresh_timeouts, w, http_refresh_timeout);
+}
+
+static void http_unload (struct widget *w)
+{
+ gpointer http_refresh_timeout;
+
+ http_refresh_timeout = g_hash_table_lookup (refresh_timeouts, w);
+ g_source_remove (GPOINTER_TO_INT (http_refresh_timeout));
+ g_hash_table_remove (refresh_timeouts, w);
+}
+
+static void http_init (struct widget *w)
+{
+ ap_prefs_add_string (w, "http_url", "");
+ ap_prefs_add_string (w, "http_data", "");
+ ap_prefs_add_int (w, "http_refresh_mins", 1);
+}
+
+static gboolean http_url_update (GtkWidget *widget, GdkEventFocus *evt,
+ gpointer data)
+{
+ struct widget *w = (struct widget *) data;
+ ap_prefs_set_string (w, "http_url",
+ gtk_entry_get_text (GTK_ENTRY (widget)));
+
+ return FALSE;
+}
+
+static gboolean http_refresh_mins_update (GtkWidget *widget, gpointer data)
+{
+ struct widget *w;
+ gpointer timeout;
+ int minutes;
+
+ minutes = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
+
+ w = (struct widget *) data;
+ ap_prefs_set_int (w, "http_refresh_mins", minutes);
+
+ // Kill the current timer and run a new one
+ timeout = g_hash_table_lookup (refresh_timeouts, w);
+ g_source_remove (GPOINTER_TO_INT(timeout));
+ timeout = GINT_TO_POINTER (g_timeout_add (minutes * 60 * 1000,
+ http_refresh_update, w));
+ g_hash_table_replace (refresh_timeouts, w, timeout);
+
+ return FALSE;
+}
+
+static void http_data_update (GtkWidget *w, gpointer data) {
+ http_refresh_update (data);
+}
+
+static GtkWidget *http_menu (struct widget *w)
+{
+ GtkWidget *ret = gtk_vbox_new (FALSE, 5);
+ GtkWidget *label, *hbox, *button, *spinner;
+ GtkWidget *http_url_entry;
+
+ label = gtk_label_new (_("Select URL with source content"));
+ gtk_box_pack_start (GTK_BOX (ret), label, FALSE, FALSE, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+
+ hbox = gtk_hbox_new (FALSE, 5);
+ gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0);
+
+ // URL Entry
+ http_url_entry = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), http_url_entry, TRUE, TRUE, 0);
+ gtk_entry_set_text (GTK_ENTRY (http_url_entry),
+ ap_prefs_get_string (w, "http_url"));
+ g_signal_connect (G_OBJECT (http_url_entry), "focus-out-event",
+ G_CALLBACK (http_url_update), w);
+
+ // Update Now!
+ button = gtk_button_new_with_label (_("Fetch page now!"));
+ g_signal_connect (G_OBJECT (button), "clicked",
+ G_CALLBACK (http_data_update), w);
+
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 5);
+ gtk_box_pack_start (GTK_BOX (ret), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new (_("Delay"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ spinner = gtk_spin_button_new_with_range (1, 60, 1);
+ gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner),
+ ap_prefs_get_int (w, "http_refresh_mins"));
+ g_signal_connect (G_OBJECT (spinner), "value-changed",
+ G_CALLBACK (http_refresh_mins_update), w);
+
+ label = gtk_label_new (_("minutes between page fetches"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ return ret;
+}
+
+struct component http =
+{
+ N_("Webpage"),
+ N_("Data fetched from an internet URL using HTTP"),
+ "Webpage",
+ &http_generate,
+ &http_init,
+ &http_load,
+ &http_unload,
+ NULL,
+ &http_menu
+};
+
============================================================
--- autoprofile/comp_logstats.c a4ad85fdee55de687154715674ea4aaf9b2a7e3e
+++ autoprofile/comp_logstats.c a4ad85fdee55de687154715674ea4aaf9b2a7e3e
@@ -0,0 +1,1039 @@
+/*--------------------------------------------------------------------------*
+ * AUTOPROFILE *
+ * *
+ * A Purple away message and profile manager that supports dynamic text *
+ * *
+ * AutoProfile is the legal property of its developers. Please refer to *
+ * the COPYRIGHT file distributed with this source distribution. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ *--------------------------------------------------------------------------*/
+
+#include "autoprofile.h"
+#include "log.h"
+#include "account.h"
+#include "conversation.h"
+#include "utility.h"
+#include "util.h"
+
+#include "comp_logstats.h"
+
+struct conversation_time {
+ time_t *start_time;
+ char *name;
+};
+
+/* Represents data about a particular 24 hour period in the logs */
+struct log_date {
+ int year; // The year
+ int month; // The month
+ int day; // The day
+ int received_msgs; // # msgs received
+ int received_words; // # words received
+ int sent_msgs; // # msgs sent
+ int sent_words; // # words sent
+ GSList *conversation_times; // List of conversation_time pointers
+};
+
+/* List of struct log_dates
+ This is SORTED by most recent first */
+static GSList *dates = NULL;
+
+/* Hashtable of log_dates */
+static GHashTable *dates_table = NULL;
+
+/* Is the current line part of a message sent or received? */
+static gboolean receiving = FALSE;
+/* Shortcut vars */
+static char *cur_receiver = NULL;
+static char *cur_sender = NULL;
+
+/* Implements GCompareFunc */
+static gint conversation_time_compare (gconstpointer x, gconstpointer y) {
+ const struct conversation_time *a = x;
+ const struct conversation_time *b = y;
+
+ if (difftime (*(a->start_time), *(b->start_time)) == 0.0) {
+ if (!strcmp (a->name, b->name))
+ return 0;
+ }
+
+ return -1;
+}
+
+/* Implements GCompareFunc */
+static gint log_date_compare (gconstpointer x, gconstpointer y)
+{
+ const struct log_date *a = y;
+ const struct log_date *b = x;
+
+ if (a->year == b->year) {
+ if (a->month == b->month) {
+ if (a->day == b->day)
+ return 0;
+ else
+ return a->day - b->day;
+ } else {
+ return a->month - b->month;
+ }
+ } else {
+ return a->year - b->year;
+ }
+}
+
+/* Implements GHashFunc */
+static guint log_date_hash (gconstpointer key)
+{
+ const struct log_date *d = key;
+ return ((d->year * 365) + (d->month * 12) + (d->day));
+}
+
+/* Implements GEqualFunc */
+static gboolean log_date_equal (gconstpointer x, gconstpointer y)
+{
+ const struct log_date *a = y;
+ const struct log_date *b = x;
+
+ if (a->year == b->year &&
+ a->month == b->month &&
+ a->day == b->day) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Returns the struct log_date associated with a particular date.
+ Will MODIFY list of dates and insert sorted if not yet created */
+static struct log_date *get_date (int year, int month, int day)
+{
+ struct log_date *cur_date;
+ gpointer *node;
+
+ cur_date = (struct log_date *)malloc(sizeof(struct log_date));
+ cur_date->year = year;
+ cur_date->month = month;
+ cur_date->day = day;
+
+ if ((node = g_hash_table_lookup (dates_table, cur_date))) {
+ free (cur_date);
+ return (struct log_date *)node;
+ } else {
+ g_hash_table_insert (dates_table, cur_date, cur_date);
+ cur_date->received_msgs = 0;
+ cur_date->received_words = 0;
+ cur_date->sent_msgs = 0;
+ cur_date->sent_words = 0;
+ cur_date->conversation_times = NULL;
+ return cur_date;
+ }
+}
+
+/* Like get_date, except specific to the current date */
+static struct log_date *get_today ()
+{
+ time_t the_time;
+ struct tm *cur_time;
+
+ time (&the_time);
+ cur_time = localtime (&the_time);
+
+ return get_date (cur_time->tm_year, cur_time->tm_mon, cur_time->tm_mday);
+}
+
+static int string_word_count (const char *line)
+{
+ int count, state;
+
+ count = 0;
+ state = 0;
+
+ /* If state is 1, currently processing a word */
+ while (*line) {
+ if (state == 0) {
+ if (!isspace (*line))
+ state = 1;
+ } else {
+ if (isspace (*line)) {
+ state = 0;
+ count++;
+ }
+ }
+ line++;
+ }
+
+ if (state == 1)
+ count++;
+
+ return count;
+}
+
+/* Figure out if a person is yourself or someone else */
+static gboolean is_self (PurpleAccount *a, const char *name) {
+ GList *accounts, *aliases, *aliases_start;
+ PurpleAccount *account;
+
+ char *normalized;
+ const char *normalized_alias;
+
+ if (cur_sender && !strcmp (cur_sender, name)) {
+ return TRUE;
+ }
+
+ if (cur_receiver && !strcmp (cur_receiver, name)) {
+ return FALSE;
+ }
+
+ normalized = strdup (purple_normalize (a, name));
+ accounts = purple_accounts_get_all ();
+
+ aliases_start = aliases = purple_prefs_get_string_list (
+ "/plugins/gtk/autoprofile/components/logstat/aliases");
+
+ while (aliases) {
+ normalized_alias = purple_normalize (a, (char *)aliases->data);
+
+ if (!strcmp (normalized, normalized_alias)) {
+ free_string_list (aliases_start);
+ free (normalized);
+
+ if (cur_sender)
+ free (cur_sender);
+ cur_sender = strdup (name);
+
+ return TRUE;
+ }
+
+ aliases = aliases->next;
+ }
+
+ free_string_list (aliases_start);
+
+ while (accounts) {
+ account = (PurpleAccount *)accounts->data;
+ if (!strcmp (normalized, purple_account_get_username (account))) {
+ free (normalized);
+ if (cur_sender)
+ free (cur_sender);
+ cur_sender = strdup (name);
+ return TRUE;
+ }
+ accounts = accounts->next;
+ }
+
+ free (normalized);
+
+ if (cur_receiver)
+ free (cur_receiver);
+ cur_receiver = strdup (name);
+ return FALSE;
+}
+
+/* Parses a line of a conversation */
+static void parse_line (PurpleLog *cur_log, char *l, struct log_date *d)
+{
+ char *cur_line, *cur_line_start;
+ char *name;
+ char *message;
+
+ char *line = l;
+
+ if (strlen (line) > 14 && *line == ' ')
+ line++;
+
+ if (strlen (line) > 13 &&
+ *line == '(' &&
+ isdigit (*(line + 1)) &&
+ isdigit (*(line + 2)) &&
+ *(line + 3) == ':' &&
+ isdigit (*(line + 4)) &&
+ isdigit (*(line + 5)) &&
+ *(line + 6) == ':' &&
+ isdigit (*(line + 7)) &&
+ isdigit (*(line + 8)) &&
+ *(line + 9) == ')' &&
+ isspace (*(line + 10))) {
+ cur_line_start = cur_line = line + 11;
+ while (*cur_line) {
+ if (*cur_line == ':') {
+ *cur_line = '\0';
+ name = cur_line_start;
+ message = ++cur_line;
+
+ receiving = !is_self (cur_log->account, name);
+
+ if (receiving) {
+ d->received_msgs++;
+ d->received_words += string_word_count (message);
+ } else {
+ d->sent_msgs++;
+ d->sent_words += string_word_count (message);
+ }
+
+ return;
+ }
+ cur_line++;
+ }
+
+ }
+
+ if (receiving) {
+ d->received_words += string_word_count (line);
+ } else {
+ d->sent_words += string_word_count (line);
+ }
+}
+
+/* Parses a conversation if hasn't been handled yet */
+static void parse_log (PurpleLog *cur_log)
+{
+ struct log_date *the_date;
+ struct tm *the_time;
+ struct conversation_time *conv_time;
+
+ PurpleLogReadFlags flags;
+ char *content, *cur_content, *cur_content_start, *temp;
+
+ the_time = localtime (&(cur_log->time));
+ the_date = get_date (the_time->tm_year, the_time->tm_mon, the_time->tm_mday);
+
+ /* Check for old log and if no conflicts, add to list */
+ conv_time = (struct conversation_time *)malloc (
+ sizeof (struct conversation_time));
+ conv_time->start_time = (time_t *)malloc (sizeof(time_t));
+ *(conv_time->start_time) = cur_log->time;
+ conv_time->name = strdup (cur_log->name);
+
+ if (g_slist_find_custom (the_date->conversation_times, conv_time,
+ conversation_time_compare)) {
+ /* We already processed this! Halt! */
+ free (conv_time->start_time);
+ free (conv_time->name);
+ free (conv_time);
+ return;
+ }
+
+ the_date->conversation_times = g_slist_prepend (the_date->conversation_times,
+ conv_time);
+
+ /* Start rolling the counters! */
+ temp = purple_log_read (cur_log, &flags);
+ if (!strcmp ("html", cur_log->logger->id)) {
+ content = purple_markup_strip_html (temp);
+ free (temp);
+ } else {
+ content = temp;
+ }
+
+ cur_content_start = cur_content = content;
+
+ /* Splits the conversation into lines (each line may not necessarily
+ be a seperate message */
+ while (*cur_content) {
+ if (*cur_content == '\n') {
+ *cur_content = '\0';
+ parse_line (cur_log, cur_content_start, the_date);
+ cur_content_start = cur_content + 1;
+ }
+ cur_content++;
+ }
+
+ parse_line (cur_log, cur_content_start, the_date);
+
+ free (content);
+}
+
+/* Get names of users in logs */
+static GList *logstats_get_names (PurpleLogType type, PurpleAccount *account)
+{
+ GDir *dir;
+ const char *prpl;
+ GList *ret;
+ const char *filename;
+ char *path, *me, *tmp;
+
+ ret = NULL;
+
+ if (type == PURPLE_LOG_CHAT)
+ me = g_strdup_printf ("%s.chat", purple_normalize(account,
+ purple_account_get_username(account)));
+ else
+ me = g_strdup (purple_normalize(account,
+ purple_account_get_username(account)));
+
+ /* Get the old logger names */
+ path = g_build_filename(purple_user_dir(), "logs", NULL);
+ if (!(dir = g_dir_open(path, 0, NULL))) {
+ g_free(path);
+ return ret;
+ }
+
+ while ((filename = g_dir_read_name (dir))) {
+ if (purple_str_has_suffix (filename, ".log")) {
+ tmp = strdup (filename);
+ *(tmp + strlen (filename) - 4) = '\0';
+ if (!string_list_find (ret, tmp))
+ ret = g_list_prepend (ret, strdup (tmp));
+ free (tmp);
+ }
+ }
+
+ g_dir_close (dir);
+ g_free (path);
+
+ /* Get the account-specific names */
+ prpl = PURPLE_PLUGIN_PROTOCOL_INFO
+ (purple_find_prpl (purple_account_get_protocol_id(account)))->list_icon(
+ account, NULL);
+
+ path = g_build_filename(purple_user_dir(), "logs", prpl, me, NULL);
+ g_free (me);
+
+ if (!(dir = g_dir_open(path, 0, NULL))) {
+ g_free(path);
+ return ret;
+ }
+
+ while ((filename = g_dir_read_name (dir))) {
+ if (!string_list_find (ret, filename))
+ ret = g_list_prepend (ret, strdup (filename));
+ }
+
+ g_dir_close (dir);
+ g_free (path);
+
+ return ret;
+}
+
+/* On load, reads in all logs and initializes stats database */
+static void logstats_read_logs ()
+{
+ GList *accounts, *logs, *logs_start, *names, *names_start;
+ PurpleLog *cur_log;
+
+ accounts = purple_accounts_get_all();
+
+ ap_debug ("logstats", "parsing log files");
+
+ while (accounts) {
+ names_start = names = logstats_get_names (PURPLE_LOG_IM,
+ (PurpleAccount *)accounts->data);
+
+ while (names) {
+ logs_start = purple_log_get_logs (PURPLE_LOG_IM, (char *)names->data,
+ (PurpleAccount *)accounts->data);
+ logs = logs_start;
+
+ while (logs) {
+ cur_log = (PurpleLog *)logs->data;
+ parse_log (cur_log);
+ purple_log_free (cur_log);
+ logs = logs->next;
+ }
+
+ g_list_free (logs_start);
+ names = names->next;
+ }
+
+ free_string_list (names_start);
+ accounts = accounts->next;
+ }
+
+ /* Cleanup */
+
+ ap_debug ("logstats", "finished parsing log files");
+}
+
+/* Implements GHFunc */
+static void add_element (gpointer key, gpointer value, gpointer data)
+{
+ dates = g_slist_insert_sorted (dates, value, log_date_compare);
+}
+
+/* Updates GList against hashtable */
+static void logstats_update_dates ()
+{
+ g_slist_free (dates);
+ dates = NULL;
+ g_hash_table_foreach (dates_table, add_element, NULL);
+}
+
+/*--------------------- Total calculations -------------------*/
+static int get_total (const char *field)
+{
+ GSList *cur_day;
+ int count;
+ struct log_date *d;
+
+ cur_day = dates;
+ count = 0;
+ while (cur_day) {
+ d = (struct log_date *)cur_day->data;
+ if (!strcmp (field, "received_msgs")) {
+ count += d->received_msgs;
+ } else if (!strcmp (field, "received_words")) {
+ count += d->received_words;
+ } else if (!strcmp (field, "sent_msgs")) {
+ count += d->sent_msgs;
+ } else if (!strcmp (field, "sent_words")) {
+ count += d->sent_words;
+ } else if (!strcmp (field, "num_convos")) {
+ count += g_slist_length (d->conversation_times);
+ }
+
+ cur_day = cur_day->next;
+ }
+
+ return count;
+}
+
+static int get_recent_total (const char *field, int hours)
+{
+ GSList *cur_day;
+ int count;
+ struct log_date *d;
+ time_t cur_day_time;
+
+ cur_day = dates;
+ count = 0;
+
+ while (cur_day) {
+ d = (struct log_date *)cur_day->data;
+ cur_day_time = purple_time_build (d->year + 1900, d->month + 1, d->day,
+ 0, 0, 0);
+ if (difftime (time (NULL), cur_day_time) > (double) hours * 60.0 * 60.0)
+ break;
+
+ if (!strcmp (field, "received_msgs")) {
+ count += d->received_msgs;
+ } else if (!strcmp (field, "sent_msgs")) {
+ count += d->sent_msgs;
+ } else if (!strcmp (field, "num_convos")) {
+ count += g_slist_length (d->conversation_times);
+ }
+
+ cur_day = cur_day->next;
+ }
+
+ return count;
+}
+
+static int num_days_since_start ()
+{
+ GSList *first_day;
+ double difference;
+ struct log_date *d;
+
+ first_day = g_slist_last (dates);
+
+ if (!first_day)
+ return 0;
+
+ d = (struct log_date *)first_day->data;
+
+ difference = difftime (
+ time (NULL), purple_time_build (d->year + 1900, d->month + 1, d->day,
+ 0, 0, 0));
+
+ return (int) difference / (60.0 * 60.0 * 24.0);
+}
+
+static struct log_date *get_max_date (const char *field)
+{
+ struct log_date *max_date, *cur_date;
+ int max_so_far, cur_max;
+ GSList *cur_day;
+
+ max_so_far = 0;
+ max_date = NULL;
+ cur_day = dates;
+
+ while (cur_day) {
+ cur_date = (struct log_date *)cur_day->data;
+ if (!strcmp (field, "conversations")) {
+ cur_max = g_slist_length (cur_date->conversation_times);
+ } else if (!strcmp (field, "received")) {
+ cur_max = cur_date->received_msgs;
+ } else if (!strcmp (field, "sent")) {
+ cur_max = cur_date->sent_msgs;
+ } else if (!strcmp (field, "total")) {
+ cur_max = cur_date->sent_msgs + cur_date->received_msgs;
+ } else {
+ cur_max = 0;
+ }
+
+ if (cur_max >= max_so_far) {
+ max_date = cur_date;
+ max_so_far = cur_max;
+ }
+
+ cur_day = cur_day->next;
+ }
+
+ return max_date;
+}
+
+static char *date_string (const char *field)
+{
+ struct log_date *d;
+ char *output;
+ struct tm *t_struct;
+ time_t t;
+ GSList *last_day;
+
+ last_day = g_slist_last (dates);
+
+ if (!last_day)
+ return NULL;
+
+ if (!strcmp (field, "first")) {
+ d = (struct log_date *) last_day->data;
+ } else {
+ d = get_max_date (field);
+ }
+
+ if (!d)
+ return NULL;
+
+ output = (char *)malloc (sizeof(char) * AP_SIZE_MAXIMUM);
+ t_struct = (struct tm *)malloc(sizeof(struct tm));
+ t_struct->tm_year = d->year;
+ t_struct->tm_mon = d->month;
+ t_struct->tm_mday = d->day;
+ t_struct->tm_sec = 0;
+ t_struct->tm_min = 0;
+ t_struct->tm_hour = 0;
+ t = mktime (t_struct);
+ free (t_struct);
+ t_struct = localtime (&t);
+
+ strftime (output, AP_SIZE_MAXIMUM - 1, "%a %b %d, %Y", t_struct);
+ return output;
+}
+
+static int get_max (const char *field)
+{
+ struct log_date *max_date = get_max_date (field);
+
+ if (!max_date)
+ return 0;
+
+ if (!strcmp (field, "conversations")) {
+ return g_slist_length (max_date->conversation_times);
+ } else if (!strcmp (field, "received")) {
+ return max_date->received_msgs;
+ } else if (!strcmp (field, "sent")) {
+ return max_date->sent_msgs;
+ } else if (!strcmp (field, "total")) {
+ return max_date->sent_msgs + max_date->received_msgs;
+ } else {
+ ap_debug ("logstats", "get-max: invalid paramater");
+ return 0;
+ }
+
+}
+
+
+/*--------------------- Signal handlers ----------------------*/
+static void logstats_received_im (PurpleAccount *account, char *sender,
+ char *message, int flags)
+{
+ struct log_date *the_date;
+
+ the_date = get_today ();
+ the_date->received_msgs++;
+ the_date->received_words += string_word_count (message);
+
+ receiving = TRUE;
+}
+
+static void logstats_sent_im (PurpleAccount *account, const char *receiver,
+ const char *message)
+{
+ struct log_date *the_date;
+
+ the_date = get_today ();
+ the_date->sent_msgs++;
+ the_date->sent_words += string_word_count (message);
+
+ receiving = FALSE;
+}
+
+static void logstats_conv_created (PurpleConversation *conv)
+{
+ struct log_date *the_date;
+ struct conversation_time *the_time;
+
+ if (conv->type == PURPLE_CONV_TYPE_IM) {
+ the_time = malloc (sizeof(struct conversation_time));
+ the_time->name = strdup (conv->name);
+ the_time->start_time = malloc (sizeof(time_t));
+ time (the_time->start_time);
+
+ the_date = get_today ();
+ the_date->conversation_times = g_slist_prepend (
+ the_date->conversation_times, the_time);
+
+ logstats_update_dates ();
+ }
+}
+
+/*--------------------------- Main functions -------------------------*/
+
+/* Component load */
+void logstats_load ()
+{
+ int count;
+ char *msg;
+
+ if (!purple_prefs_get_bool (
+ "/plugins/gtk/autoprofile/components/logstat/enabled")) {
+ return;
+ }
+
+ /* Initialize database */
+ dates_table = g_hash_table_new (log_date_hash, log_date_equal);
+ logstats_read_logs ();
+ logstats_update_dates ();
+
+ /* Debug */
+ msg = (char *)malloc (sizeof(char) * AP_SIZE_MAXIMUM);
+ count = get_total ("received_msgs");
+ g_snprintf (msg, AP_SIZE_MAXIMUM, "received msg total is %d", count);
+ ap_debug ("logstats", msg);
+ count = get_total ("sent_msgs");
+ g_snprintf (msg, AP_SIZE_MAXIMUM, "sent msg total is %d", count);
+ ap_debug ("logstats", msg);
+ count = get_total ("received_words");
+ g_snprintf (msg, AP_SIZE_MAXIMUM, "received word total is %d", count);
+ ap_debug ("logstats", msg);
+ count = get_total ("sent_words");
+ g_snprintf (msg, AP_SIZE_MAXIMUM, "sent word total is %d", count);
+ ap_debug ("logstats", msg);
+ count = get_total ("num_convos");
+ g_snprintf (msg, AP_SIZE_MAXIMUM, "num conversations is %d", count);
+ ap_debug ("logstats", msg);
+ count = g_slist_length (dates);
+ g_snprintf (msg, AP_SIZE_MAXIMUM, "num days with conversations is %d", count);
+ ap_debug ("logstats", msg);
+
+ free(msg);
+
+ /* Connect signals */
+ purple_signal_connect (purple_conversations_get_handle (),
+ "received-im-msg", ap_get_plugin_handle (),
+ PURPLE_CALLBACK (logstats_received_im), NULL);
+ purple_signal_connect (purple_conversations_get_handle (),
+ "sent-im-msg", ap_get_plugin_handle (),
+ PURPLE_CALLBACK (logstats_sent_im), NULL);
+ purple_signal_connect (purple_conversations_get_handle (),
+ "conversation-created", ap_get_plugin_handle (),
+ PURPLE_CALLBACK (logstats_conv_created), NULL);
+}
+
+/* Component unload */
+void logstats_unload ()
+{
+ struct log_date *cur_date;
+ struct conversation_time *cur_time;
+ GSList *temp;
+
+ if (!purple_prefs_get_bool (
+ "/plugins/gtk/autoprofile/components/logstat/enabled")) {
+ return;
+ }
+
+ /* Disconnect signals */
+ purple_signal_disconnect (purple_conversations_get_handle (),
+ "received-im-msg", ap_get_plugin_handle (),
+ PURPLE_CALLBACK (logstats_received_im));
+ purple_signal_disconnect (purple_conversations_get_handle (),
+ "sent-im-msg", ap_get_plugin_handle (),
+ PURPLE_CALLBACK (logstats_sent_im));
+ purple_signal_disconnect (purple_conversations_get_handle (),
+ "conversation-created", ap_get_plugin_handle (),
+ PURPLE_CALLBACK (logstats_conv_created));
+
+ logstats_update_dates ();
+
+ /* Free all the memory */
+ while (dates) {
+ cur_date = (struct log_date *)dates->data;
+ while (cur_date->conversation_times) {
+ temp = cur_date->conversation_times;
+ cur_time = (struct conversation_time *)temp->data;
+ cur_date->conversation_times = temp->next;
+ free (cur_time->start_time);
+ free (cur_time->name);
+ free (cur_time);
+ g_slist_free_1 (temp);
+ }
+ free (cur_date);
+ temp = dates;
+ dates = dates->next;
+ g_slist_free_1 (temp);
+ }
+
+ if (cur_receiver) {
+ free (cur_receiver);
+ cur_receiver = NULL;
+ }
+ if (cur_sender) {
+ free (cur_sender);
+ cur_sender = NULL;
+ }
+ g_hash_table_destroy (dates_table);
+ dates_table = NULL;
+}
+
+/* Generate the output */
+static char *logstats_generate ()
+{
+ char *buf, *output, *date;
+ int state;
+ const char *format;
+
+ if (!purple_prefs_get_bool (
+ "/plugins/gtk/autoprofile/components/logstat/enabled")) {
+ return NULL;
+ }
+
+ format = purple_prefs_get_string (
+ "/plugins/gtk/autoprofile/components/logstat/format");
+
+ output = (char *)malloc (sizeof(char)*AP_SIZE_MAXIMUM);
+ *output = '\0';
+ buf = (char *)malloc (sizeof(char)*AP_SIZE_MAXIMUM);
+ *buf = '\0';
+
+ state = 0;
+
+ while (*format) {
+ if (state == 1) {
+ switch (*format) {
+ case '%':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%c", output, *format);
+ break;
+ case 'R':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("received_msgs"));
+ break;
+ case 'r':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("received_words"));
+ break;
+ case 'S':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("sent_msgs"));
+ break;
+ case 's':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("sent_words"));
+ break;
+ case 'T':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
+ get_total ("sent_msgs") + get_total ("received_msgs"));
+ break;
+ case 't':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
+ get_total ("sent_words") + get_total ("received_words"));
+ break;
+ case 'D':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
+ num_days_since_start ());
+ break;
+ case 'd':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, g_slist_length (dates));
+ break;
+ case 'N':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_total ("num_convos"));
+ break;
+ case 'n':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ (double) get_total ("num_convos") / (double) g_slist_length (dates));
+ break;
+ case 'i':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_max ("conversations"));
+ break;
+ case 'I':
+ date = date_string ("conversations");
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date);
+ free (date);
+ break;
+ case 'j':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_max ("sent"));
+ break;
+ case 'J':
+ date = date_string ("sent");
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date);
+ free (date);
+ break;
+ case 'k':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_max ("received"));
+ break;
+ case 'K':
+ date = date_string ("received");
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date);
+ free (date);
+ break;
+ case 'l':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_max ("total"));
+ break;
+ case 'L':
+ date = date_string ("total");
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date);
+ free (date);
+ break;
+ case 'f':
+ date = date_string ("first");
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%s", output, date);
+ free (date);
+ break;
+ case 'u':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ (double) get_total ("received_words") / (double) get_total ("received_msgs"));
+ break;
+ case 'v':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ (double) get_total ("sent_words") / (double) get_total ("sent_msgs"));
+ break;
+ case 'w':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ (double) (get_total ("received_words") + get_total ("sent_words")) / (double) (get_total("received_msgs") + get_total ("sent_msgs")));
+ break;
+ case 'U':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ (double) get_total ("received_msgs") / (double) get_total ("num_convos"));
+ break;
+ case 'V':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ (double) get_total ("sent_msgs") / (double) get_total ("num_convos"));
+ break;
+ case 'W':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ (double) (get_total ("received_msgs") + get_total ("sent_msgs")) / (double) get_total("num_convos"));
+ break;
+ case 'x':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ (double) get_total ("received_words") / (double) g_slist_length (dates));
+ break;
+ case 'y':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ (double) get_total ("sent_words") / (double) g_slist_length (dates));
+ break;
+ case 'z':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ ((double) get_total ("received_words") + (double) get_total ("sent_words")) / (double) g_slist_length (dates));
+ break;
+ case 'X':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ (double) get_total ("received_msgs") / (double) g_slist_length (dates));
+ break;
+ case 'Y':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ (double) get_total ("sent_msgs") / (double) g_slist_length (dates));
+ break;
+ case 'Z':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.2f", output,
+ (double) (get_total ("received_msgs") + get_total ("sent_msgs")) / (double) g_slist_length (dates));
+ break;
+ case 'p':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%.1f", output,
+ 100.0 * (double) g_slist_length (dates) / (double) num_days_since_start ());
+ break;
+ case 'a':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
+ ((struct log_date *) dates->data)->received_msgs);
+ break;
+ case 'b':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
+ ((struct log_date *) dates->data)->sent_msgs);
+ break;
+ case 'c':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
+ g_slist_length (((struct log_date *) dates->data)->conversation_times));
+ break;
+ case 'e':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
+ ((struct log_date *) dates->data)->sent_msgs + ((struct log_date *) dates->data)->received_msgs);
+ break;
+ case 'A':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_recent_total ("received_msgs", 24 * 7));
+ break;
+ case 'B':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_recent_total ("sent_msgs", 24 * 7));
+ break;
+ case 'C':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output, get_recent_total ("num_convos", 24 * 7));
+ break;
+ case 'E':
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%d", output,
+ get_recent_total ("received_msgs", 24 * 7) + get_recent_total ("received_msgs", 24 * 7));
+ break;
+ default:
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%c", output, *format);
+ break;
+ }
+
+ strcpy (output, buf);
+ format++;
+ state = 0;
+ } else {
+ if (*format == '%') {
+ state = 1;
+ } else {
+ g_snprintf (buf, AP_SIZE_MAXIMUM, "%s%c", output, *format);
+ strcpy (output, buf);
+ }
+ format++;
+ }
+
+ }
+
+ free (buf);
+ return output;
+}
+
+/* Initialize preferences */
+static void logstats_init ()
+{
+ purple_prefs_add_none ("/plugins/gtk/autoprofile/components/logstat");
+ purple_prefs_add_bool (
+ "/plugins/gtk/autoprofile/components/logstat/enabled", FALSE);
+ purple_prefs_add_string (
+ "/plugins/gtk/autoprofile/components/logstat/format", "");
+ purple_prefs_add_string_list (
+ "/plugins/gtk/autoprofile/components/logstat/aliases", NULL);
+}
+
+/* The heart of the component */
+static char *identifiers [7] = {
+ N_("logs"),
+ N_("log"),
+ N_("stat"),
+ N_("stats"),
+ N_("logstats"),
+ N_("log statistics"),
+ NULL
+};
+
+struct component logstats =
+{
+ N_("Purple log statistics"),
+ N_("Display various statistics about your message and system logs"),
+ identifiers,
+ &logstats_generate,
+ &logstats_init,
+ &logstats_load,
+ &logstats_unload,
+ NULL,
+ &logstats_prefs
+};
+
============================================================
--- autoprofile/comp_logstats.h 50a352d8577e9c03c3cb9aac4680a4cd589ea8f9
+++ autoprofile/comp_logstats.h 50a352d8577e9c03c3cb9aac4680a4cd589ea8f9
@@ -0,0 +1,28 @@
+/*--------------------------------------------------------------------------*
+ * AUTOPROFILE *
+ * *
+ * A Purple away message and profile manager that supports dynamic text *
+ * *
+ * AutoProfile is the legal property of its developers. Please refer to *
+ * the COPYRIGHT file distributed with this source distribution. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ *--------------------------------------------------------------------------*/
+
+void logstats_load (void);
+void logstats_unload (void);
+GtkWidget *logstats_prefs (void);
+
+
============================================================
--- autoprofile/comp_logstats_gtk.c ac3dbed2a1ad10693f267467eeaf25f2b5ec7147
+++ autoprofile/comp_logstats_gtk.c ac3dbed2a1ad10693f267467eeaf25f2b5ec7147
@@ -0,0 +1,355 @@
+/*--------------------------------------------------------------------------*
+ * AUTOPROFILE *
+ * *
+ * A Purple away message and profile manager that supports dynamic text *
+ * *
+ * AutoProfile is the legal property of its developers. Please refer to *
+ * the COPYRIGHT file distributed with this source distribution. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ *--------------------------------------------------------------------------*/
+
+#include "autoprofile.h"
+#include "comp_logstats.h"
+#include "request.h"
+
+GtkWidget *checkbox = NULL;
+
+GtkListStore *alias_list = NULL;
+GtkWidget *alias_view = NULL;
+
+/* General callbacks from main preferences */
+static void logstats_response_cb (GtkDialog *dialog, gint id,
+ GtkWidget *widget)
+{
+ purple_prefs_set_bool (
+ "/plugins/gtk/autoprofile/components/logstat/enabled", TRUE);
+ logstats_load ();
+ gtk_widget_set_sensitive (widget, TRUE);
+
+ gtk_widget_destroy (GTK_WIDGET(dialog));
+}
+
+static void toggle_enable (GtkButton *button, gpointer data)
+{
+ GtkWidget *popup, *vbox, *label;
+ vbox = data;
+
+ if (purple_prefs_get_bool (
+ "/plugins/gtk/autoprofile/components/logstat/enabled")) {
+ logstats_unload ();
+ purple_prefs_set_bool (
+ "/plugins/gtk/autoprofile/components/logstat/enabled", FALSE);
+ gtk_widget_set_sensitive (vbox, FALSE);
+ } else {
+ popup = gtk_dialog_new_with_buttons (
+ "Enable stats for logs", NULL, 0,
+ GTK_STOCK_OK, 42, NULL);
+ g_signal_connect (G_OBJECT(popup), "response",
+ G_CALLBACK(logstats_response_cb), vbox);
+
+ label = gtk_label_new(NULL);
+ gtk_label_set_markup(GTK_LABEL(label),
+ "\nEnabling this component will have some minor side effects. Doing so "
+ "will cause Purple to take slightly longer to start up because it must "
+ "parse a large amount of data to gather statistics. On average, this "
+ "can take slightly over a second for every 100,000 messages in your "
+ "logs.\n\nThe time from when you press the OK button to the time "
+ "when this dialog vanishes is a good approximation of how much extra "
+ "time will elapse before the login screen is shown.\n"
+ );
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(popup)->vbox), label,
+ FALSE, FALSE, 0);
+
+ gtk_widget_show_all (popup);
+ }
+}
+
+static gboolean logstat_format (GtkWidget *widget, GdkEventFocus *event,
+ gpointer data)
+{
+ purple_prefs_set_string (
+ "/plugins/gtk/autoprofile/components/logstat/format",
+ gtk_entry_get_text (GTK_ENTRY (widget)));
+ return FALSE;
+}
+
+static void new_alias (gpointer data, PurpleRequestFields *fields)
+{
+ GtkTreeIter iter;
+ GList *aliases;
+
+ const char *alias;
+
+ alias = purple_request_fields_get_string (fields, "alias");
+ aliases = purple_prefs_get_string_list (
+ "/plugins/gtk/autoprofile/components/logstat/aliases");
+
+ aliases = g_list_append (aliases, strdup (alias));
+ purple_prefs_set_string_list (
+ "/plugins/gtk/autoprofile/components/logstat/aliases", aliases);
+ free_st