grim/guifications2

This was compatibility code for Pidgin 1.x.y; because it was written as a check
for "not 2.0.0" this code would be compiled in for 3.0.0. It looks like the
supporting code elsewhere was removed, causing this to leave an unresolved
symbol in guifications.so, thus causing Pidgin to refuse to load. Now we load
in Pidgin 3.0.0. Whether it works or not, I have no clue.
/*
* Guifications - The end all, be all, toaster popup plugin
* Copyright (C) 2003-2008 Gary Kramlich
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
*/
#include <glib.h>
#include <string.h>
#ifdef HAVE_CONFIG_H
# include "../gf_config.h"
#endif
#include "gf_internal.h"
#include <debug.h>
#include <account.h>
#include <blist.h>
#include <connection.h>
#include <conversation.h>
#include <notify.h>
#include <plugin.h>
#include <prefs.h>
#include <status.h>
#include <util.h>
#include <version.h>
#include "gf_blist.h"
#include "gf_display.h"
#include "gf_event.h"
#include "gf_notification.h"
#include "gf_preferences.h"
struct _GfEvent {
gchar *n_type;
gchar *name;
gchar *description;
GfEventPriority priority;
gchar *tokens;
gboolean show;
};
static GList *events = NULL;
void *(*real_notify_email)(PurpleConnection *gc, const char *subject, const char *from,
const char *to, const char *url);
void *(*real_notify_emails)(PurpleConnection *gc, size_t count, gboolean detailed,
const char **subject, const char **from,
const char **to, const char **url);
/*******************************************************************************
* API
******************************************************************************/
GfEvent *
gf_event_new(const gchar *notification_type, const gchar *tokens,
const gchar *name, const gchar *description,
GfEventPriority priority)
{
GfEvent *event;
g_return_val_if_fail(notification_type, NULL);
g_return_val_if_fail(name, NULL);
g_return_val_if_fail(description, NULL);
event = g_new0(GfEvent, 1);
event->priority = priority;
event->n_type = g_strdup(notification_type);
if(tokens)
event->tokens = g_strdup(tokens);
else
event->tokens = g_strdup(TOKENS_DEFAULT);
event->name = g_strdup(name);
event->description = g_strdup(description);
if(!g_list_find(events, event))
events = g_list_append(events, event);
else {
purple_debug_info("Guifications", "Event already exists\n");
gf_event_destroy(event);
}
return event;
}
GfEvent *
gf_event_find_for_notification(const gchar *type) {
GfEvent *event;
GList *l;
for(l = events; l; l = l->next) {
event = GF_EVENT(l->data);
if(!g_ascii_strcasecmp(event->n_type, type))
return event;
}
return NULL;
}
void
gf_event_destroy(GfEvent *event) {
g_return_if_fail(event);
events = g_list_remove(events, event);
g_free(event->n_type);
g_free(event->name);
g_free(event->description);
g_free(event->tokens);
g_free(event);
}
const gchar *
gf_event_get_notification_type(GfEvent *event) {
g_return_val_if_fail(event, NULL);
return event->n_type;
}
const gchar *
gf_event_get_tokens(GfEvent *event) {
g_return_val_if_fail(event, NULL);
return event->tokens;
}
const gchar *
gf_event_get_name(GfEvent *event) {
g_return_val_if_fail(event, NULL);
return event->name;
}
const gchar *
gf_event_get_description(GfEvent *event) {
g_return_val_if_fail(event, NULL);
return event->description;
}
GfEventPriority
gf_event_get_priority(GfEvent *event) {
g_return_val_if_fail(event, GF_EVENT_PRIORITY_NORMAL);
return event->priority;
}
void
gf_event_set_show(GfEvent *event, gboolean show) {
g_return_if_fail(event);
event->show = show;
}
gboolean
gf_event_get_show(GfEvent *event) {
g_return_val_if_fail(event, FALSE);
return event->show;
}
gboolean
gf_event_show_notification(const gchar *n_type) {
GfEvent *event;
g_return_val_if_fail(n_type, FALSE);
event = gf_event_find_for_notification(n_type);
if(event)
return event->show;
return FALSE;
}
const GList *gf_events_get() {
return events;
}
void
gf_events_save() {
GfEvent *event;
GList *l = NULL, *e;
for(e = events; e; e = e->next) {
event = GF_EVENT(e->data);
if(event->show)
l = g_list_append(l, event->n_type);
}
purple_prefs_set_string_list(GF_PREF_BEHAVIOR_NOTIFICATIONS, l);
g_list_free(l);
}
gint
gf_events_count() {
return g_list_length(events);
}
const gchar *
gf_events_get_nth_name(gint nth) {
GfEvent *event = g_list_nth_data(events, nth);
return event->name;
}
const gchar *
gf_events_get_nth_notification(gint nth) {
GfEvent *event = g_list_nth_data(events, nth);
return event->n_type;
}
/*******************************************************************************
* Signon flood be gone!
******************************************************************************/
static GList *accounts = NULL;
static gboolean
gf_event_connection_throttle_cb(gpointer data) {
PurpleAccount *account = (PurpleAccount *)data;
if(!account)
return FALSE;
if(!purple_account_get_connection(account)) {
accounts = g_list_remove(accounts, account);
return FALSE;
}
if(!purple_account_is_connected(account))
return TRUE;
accounts = g_list_remove(accounts, account);
return FALSE;
}
static void
gf_event_connection_throttle(PurpleConnection *gc, gpointer data) {
PurpleAccount *account;
if(!gc)
return;
account = purple_connection_get_account(gc);
if(!account)
return;
accounts = g_list_append(accounts, account);
g_timeout_add(10000, gf_event_connection_throttle_cb, (gpointer)account);
}
/*******************************************************************************
* Chat Join Flooding
******************************************************************************/
static GList *chats = NULL;
static gboolean
gf_event_conversation_throttle_cb(gpointer data) {
PurpleConversation *conv = (PurpleConversation *)data;
if(conv)
chats = g_list_remove(chats, conv);
return FALSE;
}
static void
gf_event_conversation_throttle(PurpleConversation *conv, gpointer data) {
if(!conv)
return;
if(purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT)
return;
chats = g_list_append(chats, conv);
g_timeout_add(5000, gf_event_conversation_throttle_cb, (gpointer)conv);
}
/*******************************************************************************
* Helpers
******************************************************************************/
/* first pass to see if a notification should be shown.. */
static gboolean
gf_event_should_show(const gchar *notification, PurpleAccount *account) {
if(gf_display_screen_saver_is_running())
return FALSE;
if(!purple_account_is_connected(account))
return FALSE;
if(g_list_find(accounts, account))
return FALSE;
if(!gf_event_show_notification(notification))
return FALSE;
if(!purple_prefs_get_bool(GF_PREF_BEHAVIOR_SHOW_WHILE_AWAY))
if(!purple_presence_is_available(account->presence))
return FALSE;
return TRUE;
}
/* second pass to see if a conversation notification should be shown */
static gboolean
gf_event_conversation_show(PurpleConversation *conv, const gchar *sender) {
if(!conv)
return FALSE;
if(purple_conversation_has_focus(conv))
return FALSE;
if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
if(g_list_find(chats, conv))
return FALSE;
if(sender) {
const gchar *nick;
nick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(conv));
if(!strcmp(nick, sender))
return FALSE;
}
}
return TRUE;
}
static void
gf_event_common(const gchar *n_type, PurpleAccount *account, PurpleBuddy *buddy,
PurpleConversation *conv, const gchar *target,
const gchar *message, PurpleConvChatBuddyFlags flags,
const GHashTable *components, const gchar *extra)
{
GfNotification *notification = NULL;
GfEventInfo *info = NULL;
g_return_if_fail(n_type);
g_return_if_fail(account);
if(!gf_event_should_show(n_type, account))
return;
if(conv && target) {
if(!gf_event_conversation_show(conv, target))
return;
}
if(buddy) {
notification = gf_blist_get_notification_for_buddy(buddy, n_type);
} else {
notification = gf_notification_find_for_event(n_type);
}
if(!notification)
return;
info = gf_event_info_new(n_type);
gf_event_info_set_account(info, account);
if(buddy)
gf_event_info_set_buddy(info, buddy);
if(conv)
gf_event_info_set_conversation(info, conv);
if(target)
gf_event_info_set_target(info, target);
if(message)
gf_event_info_set_message(info, message);
gf_event_info_set_conv_chat_buddy_flags(info, flags);
if(components)
gf_event_info_set_components(info, components);
if(extra)
gf_event_info_set_extra(info, extra);
gf_display_show_event(info, notification);
}
/*******************************************************************************
* Buddy callbacks
******************************************************************************/
static void
gf_event_buddy(PurpleBuddy *buddy, gpointer data) {
const gchar *notification = (const gchar *)data;
if(!gf_event_should_show(notification, buddy->account))
return;
gf_event_common(notification, buddy->account, buddy, NULL, buddy->name,
NULL, PURPLE_CBFLAGS_NONE, NULL, NULL);
}
static void
gf_event_buddy_status(PurpleBuddy *buddy, PurpleStatus *oldstatus,
PurpleStatus *newstatus, gpointer data)
{
const gchar *notification;
if (purple_status_is_available(oldstatus) &&
!purple_status_is_available(newstatus)) {
notification = "away";
} else if (!purple_status_is_available(oldstatus) &&
purple_status_is_available(newstatus)) {
notification = "back";
} else
return;
gf_event_common(notification, buddy->account, buddy, NULL, buddy->name,
NULL, PURPLE_CBFLAGS_NONE, NULL, NULL);
}
static void
gf_event_buddy_idle(PurpleBuddy *buddy, gboolean oldidle, gboolean newidle,
gpointer data)
{
const gchar *notification;
notification = (newidle) ? "idle" : "unidle";
gf_event_common(notification, buddy->account, buddy, NULL, buddy->name,
NULL, PURPLE_CBFLAGS_NONE, NULL, NULL);
}
/*******************************************************************************
* Conversation callbacks
******************************************************************************/
static void
gf_event_im_message(PurpleAccount *account, const gchar *sender,
const gchar *message, PurpleConversation *conv, gint flags,
gpointer data)
{
PurpleBuddy *buddy = NULL;
const gchar *notification = (const gchar *)data;
gchar *plain_message = NULL;
buddy = purple_find_buddy(account, sender);
plain_message = purple_markup_strip_html(message);
gf_event_common(notification, account, buddy, conv, sender, plain_message,
PURPLE_CBFLAGS_NONE, NULL, NULL);
g_free(plain_message);
}
static void
gf_event_chat_message(PurpleAccount *account, const gchar *sender,
const gchar *message, PurpleConversation *conv, gint flags,
gpointer data)
{
PurpleBuddy *buddy = NULL;
const gchar *notification = (const gchar *)data;
gchar *plain_message = NULL;
plain_message = purple_markup_strip_html(message);
buddy = purple_find_buddy(account, sender);
gf_event_common(notification, account, buddy, conv, sender, plain_message,
PURPLE_CBFLAGS_NONE, NULL, NULL);
g_free(plain_message);
}
static void
gf_event_chat_nick(PurpleAccount *account, const gchar *sender,
const gchar *message, PurpleConversation *conv, PurpleMessageFlags flags,
gpointer data)
{
PurpleBuddy *buddy = NULL;
const gchar *notification = (const gchar *)data;
gchar *plain_message = NULL;
const gchar *nick = NULL;
nick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(conv));
if (nick && !strcmp(sender, nick))
return;
if(!purple_utf8_has_word(message, nick))
return;
plain_message = purple_markup_strip_html(message);
buddy = purple_find_buddy(account, sender);
gf_event_common(notification, account, buddy, conv, sender, plain_message,
PURPLE_CBFLAGS_NONE, NULL, NULL);
g_free(plain_message);
}
static void
gf_event_typing(PurpleAccount *account, const char *name, gpointer data) {
PurpleBuddy *buddy = NULL;
const gchar *notification = (const gchar *)data;
PurpleConversation *conv;
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account);
buddy = purple_find_buddy(account, name);
gf_event_common(notification, account, buddy, conv, name, NULL,
PURPLE_CBFLAGS_NONE, NULL, NULL);
}
static void
gf_event_chat_join(PurpleConversation *conv, const gchar *name,
PurpleConvChatBuddyFlags flags, gboolean *new_arrival,
gpointer data) {
PurpleAccount *account = NULL;
PurpleBuddy *buddy = NULL;
const gchar *notification = (const gchar *)data;
account = purple_conversation_get_account(conv);
buddy = purple_find_buddy(account, name);
gf_event_common(notification, account, buddy, conv, name, NULL, flags,
NULL, NULL);
}
static void
gf_event_chat_part(PurpleConversation *conv, const gchar *name,
const gchar *reason, gpointer data)
{
PurpleAccount *account = NULL;
PurpleBuddy *buddy = NULL;
const gchar *notification = (const gchar *)data;
gchar *plain_message = NULL;
account = purple_conversation_get_account(conv);
plain_message = (reason) ? purple_markup_strip_html(reason) : g_strdup("");
buddy = purple_find_buddy(account, name);
gf_event_common(notification, account, buddy, conv, name, plain_message,
PURPLE_CBFLAGS_NONE, NULL, NULL);
g_free(plain_message);
}
static gint
gf_event_chat_invite(PurpleAccount *account, const gchar *inviter,
const gchar *chat, const gchar *invite_message,
const GHashTable *components, gpointer data)
{
PurpleBuddy *buddy = NULL;
const gchar *notification = (const gchar *)data;
gchar *plain_message = NULL;
plain_message = (invite_message) ? purple_markup_strip_html(invite_message) :
g_strdup("");
buddy = purple_find_buddy(account, inviter);
gf_event_common(notification, account, buddy, NULL, inviter, plain_message,
PURPLE_CBFLAGS_NONE, components, chat);
g_free(plain_message);
return 0;
}
static void
gf_event_topic_changed(PurpleConversation *conv, const gchar *who,
const gchar *topic, gpointer data)
{
PurpleAccount *account = NULL;
PurpleBuddy *buddy = NULL;
const gchar *notification = (const gchar *)data;
gchar *plain_message = NULL;
account = purple_conversation_get_account(conv);
plain_message = (topic) ? purple_markup_strip_html(topic) : g_strdup("");
if(who)
buddy = purple_find_buddy(account, who);
gf_event_common(notification, account, buddy, conv, who, plain_message,
PURPLE_CBFLAGS_NONE, NULL, NULL);
g_free(plain_message);
}
/*******************************************************************************
* Mail Stuff
******************************************************************************/
static void *
gf_event_email(PurpleConnection *gc, const char *subject, const char *from,
const char *to, const char *url)
{
gf_event_common("new-email", gc->account,
purple_find_buddy(gc->account, from),
NULL, from, NULL, PURPLE_CBFLAGS_NONE, NULL, subject);
return real_notify_email(gc, subject, from, to, url);
}
static void *
gf_event_emails(PurpleConnection *gc, size_t count, gboolean detailed,
const char **subject, const char **from,
const char **to, const char **url)
{
if(count == 0) {
return NULL;
} else if(count == 1) {
if(subject && *subject) {
return gf_event_email(gc, *subject, *from, *to, *url);
}
} else {
gchar *c = g_strdup_printf("%d", (gint)count);
gf_event_common("new-emails", gc->account, NULL, NULL, *to, NULL,
PURPLE_CBFLAGS_NONE, NULL, c);
g_free(c);
}
return real_notify_emails(gc, count, detailed, subject, from, to, url);
}
static void
gf_event_email_init() {
g_return_if_fail(!real_notify_email);
PurpleNotifyUiOps *notify_ops_orig = purple_notify_get_ui_ops();
real_notify_email = notify_ops_orig->notify_email;
notify_ops_orig->notify_email = gf_event_email;
real_notify_emails = notify_ops_orig->notify_emails;
notify_ops_orig->notify_emails = gf_event_emails;
}
static void gf_event_email_uninit() {
g_return_if_fail(real_notify_email);
PurpleNotifyUiOps *notify_ops_orig = purple_notify_get_ui_ops();
notify_ops_orig->notify_email = real_notify_email;
notify_ops_orig->notify_emails = real_notify_emails;
}
/*******************************************************************************
* File transfer stuff
******************************************************************************/
static void
gf_event_file_recv_cancel(PurpleXfer *xfer, gpointer data)
{
const gchar *notification = data;
gf_event_common(notification, xfer->account, NULL, NULL, xfer->who,
NULL, PURPLE_CBFLAGS_NONE, NULL, xfer->local_filename);
}
/*******************************************************************************
* Subsystem
******************************************************************************/
void
gf_events_init(PurplePlugin *plugin) {
GList *l, *ll = NULL;
void *blist_handle, *accounts_handle, *conv_handle;
g_return_if_fail(plugin);
/* create all of our default events */
gf_event_new("sign-on", TOKENS_DEFAULT "n", _("Sign on"),
_("Displayed when a buddy comes online."),
GF_EVENT_PRIORITY_HIGHER);
gf_event_new("sign-off", TOKENS_DEFAULT "n", _("Sign off"),
_("Displayed when a buddy goes offline."),
GF_EVENT_PRIORITY_HIGHER);
gf_event_new("away", TOKENS_DEFAULT "n", _("Away"),
_("Displayed when a buddy goes away."),
GF_EVENT_PRIORITY_HIGH);
gf_event_new("back", TOKENS_DEFAULT "n", _("Back"),
_("Displayed when a buddy returns from away."),
GF_EVENT_PRIORITY_HIGH);
gf_event_new("idle", TOKENS_DEFAULT "n", _("Idle"),
_("Displayed when a buddy goes idle."),
GF_EVENT_PRIORITY_NORMAL);
gf_event_new("unidle", TOKENS_DEFAULT "n", _("Unidle"),
_("Displayed when a buddy returns from idle."),
GF_EVENT_PRIORITY_NORMAL);
gf_event_new("im-message", TOKENS_DEFAULT "Ccnr", _("IM message"),
_("Displayed when someone sends you a message."),
GF_EVENT_PRIORITY_HIGHEST);
gf_event_new("typing", TOKENS_DEFAULT "Ccnr", _("Typing"),
_("Displayed when someone is typing a message to you."),
GF_EVENT_PRIORITY_HIGHER);
gf_event_new("typing-stopped", TOKENS_DEFAULT "Ccnr", _("Stopped typing"),
_("Displayed when someone has stopped typing a message to you."),
GF_EVENT_PRIORITY_HIGHER);
gf_event_new("chat-message", TOKENS_DEFAULT "Ccnr", _("Chat message"),
_("Displayed when someone talks in a chat."),
GF_EVENT_PRIORITY_HIGHER);
gf_event_new("nick-highlight", TOKENS_DEFAULT "Ccnr", _("Name spoken"),
_("Displayed when someone says your nick in a chat"),
GF_EVENT_PRIORITY_HIGHEST);
gf_event_new("chat-join", TOKENS_DEFAULT "Ccnr", _("Join"),
_("Displayed when someone joins a chat."),
GF_EVENT_PRIORITY_LOW);
gf_event_new("chat-part", TOKENS_DEFAULT "Ccnr", _("Leave"),
_("Displayed when someone leaves a chat."),
GF_EVENT_PRIORITY_LOW);
gf_event_new("chat-invite", TOKENS_DEFAULT "Ccnr", _("Invited"),
_("Displayed when someone invites you to a chat."),
GF_EVENT_PRIORITY_HIGHEST);
gf_event_new("topic-changed", TOKENS_DEFAULT "Ccnr", _("Topic changed"),
_("Displayed when a topic is changed in a chat."),
GF_EVENT_PRIORITY_LOW);
gf_event_new("new-email", TOKENS_DEFAULT "c", _("Email"),
_("Displayed when you receive new email."),
GF_EVENT_PRIORITY_NORMAL);
gf_event_new("new-emails", TOKENS_DEFAULT "c", _("Emails"),
_("Displaed when you receive multiple new emails."),
GF_EVENT_PRIORITY_NORMAL);
gf_event_new(GF_NOTIFICATION_MASTER, TOKENS_DEFAULT "Ccnr",
_("Master"), _("Master notification for the theme editor."),
GF_EVENT_PRIORITY_NORMAL);
gf_event_new("file-remote-cancel", TOKENS_DEFAULT "nX", _("File receive cancelled"),
_("Displayed when the buddy cancels the file transfer."),
GF_EVENT_PRIORITY_NORMAL);
gf_event_new("file-recv-complete", TOKENS_DEFAULT "nX", _("File receive completed"),
_("Displayed when file transfer completes for a file you are receiving."),
GF_EVENT_PRIORITY_NORMAL);
gf_event_new("file-send-complete", TOKENS_DEFAULT "nX", _("File send completed"),
_("Displayed when file transfer completes for a file you are sending."),
GF_EVENT_PRIORITY_NORMAL);
/* add our string list pref */
for(l = events; l; l = l->next) {
GfEvent *event = GF_EVENT(l->data);
ll = g_list_append(ll, event->n_type);
}
purple_prefs_add_string_list(GF_PREF_BEHAVIOR_NOTIFICATIONS, ll);
g_list_free(ll);
/* now that the pref is created, set the events that are supposed to be displayed
* to allow displaying
*/
l = purple_prefs_get_string_list(GF_PREF_BEHAVIOR_NOTIFICATIONS);
for(ll = l; ll; ll = ll->next) {
gchar *event_name = (gchar *)ll->data;
if(event_name) {
GfEvent *event = gf_event_find_for_notification(event_name);
g_free(ll->data);
if(event)
event->show = TRUE;
}
}
g_list_free(l);
/* connect all of our signals */
blist_handle = purple_blist_get_handle();
accounts_handle = purple_accounts_get_handle();
conv_handle = purple_conversations_get_handle();
/* blist signals */
purple_signal_connect(blist_handle, "buddy-signed-on", plugin,
PURPLE_CALLBACK(gf_event_buddy), "sign-on");
purple_signal_connect(blist_handle, "buddy-signed-off", plugin,
PURPLE_CALLBACK(gf_event_buddy), "sign-off");
purple_signal_connect(blist_handle, "buddy-status-changed", plugin,
PURPLE_CALLBACK(gf_event_buddy_status), NULL);
purple_signal_connect(blist_handle, "buddy-idle-changed", plugin,
PURPLE_CALLBACK(gf_event_buddy_idle), NULL);
/* conversation signals */
purple_signal_connect(conv_handle, "received-im-msg", plugin,
PURPLE_CALLBACK(gf_event_im_message), "im-message");
purple_signal_connect(conv_handle, "received-chat-msg", plugin,
PURPLE_CALLBACK(gf_event_chat_message), "chat-message");
purple_signal_connect(conv_handle, "received-chat-msg", plugin,
PURPLE_CALLBACK(gf_event_chat_nick), "nick-highlight");
purple_signal_connect(conv_handle, "chat-buddy-joined", plugin,
PURPLE_CALLBACK(gf_event_chat_join), "chat-join");
purple_signal_connect(conv_handle, "chat-buddy-left", plugin,
PURPLE_CALLBACK(gf_event_chat_part), "chat-part");
purple_signal_connect(conv_handle, "chat-invited", plugin,
PURPLE_CALLBACK(gf_event_chat_invite), "chat-invite");
purple_signal_connect(conv_handle, "buddy-typing", plugin,
PURPLE_CALLBACK(gf_event_typing), "typing");
purple_signal_connect(conv_handle, "buddy-typing-stopped", plugin,
PURPLE_CALLBACK(gf_event_typing), "typed");
purple_signal_connect(conv_handle, "chat-topic-changed", plugin,
PURPLE_CALLBACK(gf_event_topic_changed), "topic-changed");
/* both of these are used just to not display the flood of guifications we'd get if
* we weren't throttling them. Makes it look much prettier im my opinion :)
*/
purple_signal_connect(purple_connections_get_handle(), "signed-on", plugin,
PURPLE_CALLBACK(gf_event_connection_throttle), NULL);
purple_signal_connect(conv_handle, "chat-joined", plugin,
PURPLE_CALLBACK(gf_event_conversation_throttle), NULL);
/* mail stuff */
gf_event_email_init();
purple_signal_connect(purple_xfers_get_handle(), "file-recv-cancel", plugin,
PURPLE_CALLBACK(gf_event_file_recv_cancel), "file-remote-cancel");
purple_signal_connect(purple_xfers_get_handle(), "file-recv-complete", plugin,
PURPLE_CALLBACK(gf_event_file_recv_cancel), "file-recv-complete");
purple_signal_connect(purple_xfers_get_handle(), "file-send-complete", plugin,
PURPLE_CALLBACK(gf_event_file_recv_cancel), "file-send-complete");
}
void
gf_events_uninit() {
GList *l, *ll;
gf_event_email_uninit();
for(l = events; l; l = ll) {
ll = l->next;
gf_event_destroy(GF_EVENT(l->data));
}
g_list_free(events);
events = NULL;
}