pidgin/pidgin

1896a80ff8e3
Route GLib debug logging directly to the Finch debug window

Instead of flowing through purple debug, this merges some bits of the existing GLib log handler, and the purple debug printer.

Testing Done:
Open the Debug window an see some `GLib-*` outputs.

Reviewed at https://reviews.imfreedom.org/r/1057/
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include <glib/gi18n-lib.h>
#include <gplugin.h>
#include <gplugin-native.h>
#include <purple.h>
#include "libpurple/glibcompat.h"
#include "api.h"
#include "data.h"
#include "facebook.h"
#include "http.h"
#include "util.h"
struct _FacebookProtocol {
PurpleProtocol parent;
};
static GSList *fb_cmds = NULL;
static PurpleProtocol *fb_protocol = NULL;
static void
fb_cb_api_messages(FbApi *api, GSList *msgs, gpointer data);
static PurpleGroup *
fb_get_group(gboolean friend)
{
PurpleBlistNode *n;
PurpleBlistNode *node;
PurpleGroup *grp;
const gchar *title;
if (friend) {
title = _("Facebook Friends");
} else {
title = _("Facebook Non-Friends");
}
grp = purple_blist_find_group(title);
if (G_UNLIKELY(grp == NULL)) {
grp = purple_group_new(title);
node = NULL;
for (n = purple_blist_get_default_root(); n != NULL;
n = n->next) {
node = n;
}
/* Append to the end of the buddy list */
purple_blist_add_group(grp, node);
if (!friend) {
node = PURPLE_BLIST_NODE(grp);
purple_blist_node_set_bool(node, "collapsed", TRUE);
}
}
return grp;
}
static void
fb_buddy_add_nonfriend(PurpleAccount *acct, FbApiUser *user)
{
gchar uid[FB_ID_STRMAX];
PurpleBuddy *bdy;
PurpleGroup *grp;
FB_ID_TO_STR(user->uid, uid);
bdy = purple_buddy_new(acct, uid, user->name);
grp = fb_get_group(FALSE);
purple_buddy_set_server_alias(bdy, user->name);
purple_blist_add_buddy(bdy, NULL, grp, NULL);
}
static void
fb_cb_api_auth(FbApi *api, gpointer data)
{
FbData *fata = data;
PurpleConnection *gc;
gc = fb_data_get_connection(fata);
purple_connection_update_progress(gc, _("Fetching contacts"), 2, 4);
fb_data_save(fata);
fb_api_contacts(api);
}
static void
fb_cb_api_connect(FbApi *api, gpointer data)
{
FbData *fata = data;
PurpleAccount *acct;
PurpleConnection *gc;
gc = fb_data_get_connection(fata);
acct = purple_connection_get_account(gc);
fb_data_save(fata);
purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
if (purple_account_get_bool(acct, "show-unread", TRUE)) {
fb_api_unread(api);
}
}
static void
fb_cb_api_contact(FbApi *api, FbApiUser *user, gpointer data)
{
FbData *fata = data;
gchar uid[FB_ID_STRMAX];
GSList *msgs;
PurpleAccount *acct;
PurpleConnection *gc;
gc = fb_data_get_connection(fata);
acct = purple_connection_get_account(gc);
FB_ID_TO_STR(user->uid, uid);
if (purple_blist_find_buddy(acct, uid) == NULL) {
fb_buddy_add_nonfriend(acct, user);
}
msgs = fb_data_take_messages(fata, user->uid);
if (msgs != NULL) {
fb_cb_api_messages(api, msgs, fata);
g_slist_free_full(msgs, (GDestroyNotify) fb_api_message_free);
}
}
static gboolean
fb_cb_sync_contacts(gpointer data)
{
FbApi *api;
FbData *fata = data;
api = fb_data_get_api(fata);
fb_data_clear_timeout(fata, "sync-contacts", FALSE);
fb_api_contacts(api);
return FALSE;
}
static void
fb_cb_icon(FbDataImage *img, GError *error)
{
const gchar *csum;
const gchar *name;
const gchar *str;
FbHttpParams *params;
gsize size;
guint8 *image;
PurpleAccount *acct;
PurpleBuddy *bdy;
bdy = fb_data_image_get_data(img);
acct = purple_buddy_get_account(bdy);
name = purple_buddy_get_name(bdy);
if (G_UNLIKELY(error != NULL)) {
fb_util_debug_warning("Failed to retrieve icon for %s: %s",
name, error->message);
return;
}
str = fb_data_image_get_url(img);
params = fb_http_params_new_parse(str, TRUE);
csum = fb_http_params_get_str(params, "oh", NULL);
image = fb_data_image_dup_image(img, &size);
purple_buddy_icons_set_for_user(acct, name, image, size, csum);
fb_http_params_free(params);
}
static void
fb_sync_contacts_add_timeout(FbData *fata)
{
gint sync;
PurpleConnection *gc;
PurpleAccount *acct;
gc = fb_data_get_connection(fata);
acct = purple_connection_get_account(gc);
sync = purple_account_get_int(acct, "sync-interval", 5);
if (sync < 1) {
purple_account_set_int(acct, "sync-interval", 1);
sync = 1;
}
sync *= 60;
fb_data_add_timeout(fata, "sync-contacts", sync, fb_cb_sync_contacts,
fata);
}
static void
fb_cb_api_contacts(FbApi *api, GSList *users, gboolean complete, gpointer data)
{
const gchar *alias;
const gchar *csum;
FbApiUser *user;
FbData *fata = data;
FbId muid;
gchar uid[FB_ID_STRMAX];
GSList *l;
GValue val = G_VALUE_INIT;
PurpleAccount *acct;
PurpleBuddy *bdy;
PurpleConnection *gc;
PurpleConnectionState state;
PurpleGroup *grp;
PurpleGroup *grpn;
PurpleStatus *status;
PurpleStatusPrimitive pstat;
PurpleStatusType *type;
gc = fb_data_get_connection(fata);
acct = purple_connection_get_account(gc);
grp = fb_get_group(TRUE);
grpn = fb_get_group(FALSE);
alias = purple_account_get_private_alias(acct);
state = purple_connection_get_state(gc);
g_value_init(&val, FB_TYPE_ID);
g_object_get_property(G_OBJECT(api), "uid", &val);
muid = g_value_get_int64(&val);
g_value_unset(&val);
for (l = users; l != NULL; l = l->next) {
user = l->data;
FB_ID_TO_STR(user->uid, uid);
if (G_UNLIKELY(user->uid == muid)) {
if (G_UNLIKELY(alias != NULL)) {
continue;
}
purple_account_set_private_alias(acct, user->name);
continue;
}
bdy = purple_blist_find_buddy(acct, uid);
if ((bdy != NULL) && (purple_buddy_get_group(bdy) == grpn)) {
purple_blist_remove_buddy(bdy);
bdy = NULL;
}
if (bdy == NULL) {
bdy = purple_buddy_new(acct, uid, NULL);
purple_blist_add_buddy(bdy, NULL, grp, NULL);
}
purple_buddy_set_server_alias(bdy, user->name);
csum = purple_buddy_icons_get_checksum_for_user(bdy);
if (!purple_strequal(csum, user->csum)) {
fb_data_image_add(fata, user->icon, fb_cb_icon,
bdy, NULL);
}
}
fb_data_image_queue(fata);
if (!complete) {
return;
}
if (state != PURPLE_CONNECTION_CONNECTED) {
status = purple_account_get_active_status(acct);
type = purple_status_get_status_type(status);
pstat = purple_status_type_get_primitive(type);
purple_connection_update_progress(gc, _("Connecting"), 3, 4);
fb_api_connect(api, pstat == PURPLE_STATUS_INVISIBLE);
}
fb_sync_contacts_add_timeout(fata);
}
static void
fb_cb_api_contacts_delta(FbApi *api, GSList *added, GSList *removed, gpointer data)
{
FbApiUser *user;
FbData *fata = data;
gchar uid[FB_ID_STRMAX];
GSList *l;
PurpleAccount *acct;
PurpleBuddy *bdy;
PurpleConnection *gc;
PurpleGroup *grp;
PurpleGroup *grpn;
gc = fb_data_get_connection(fata);
acct = purple_connection_get_account(gc);
grp = fb_get_group(TRUE);
grpn = fb_get_group(FALSE);
for (l = added; l != NULL; l = l->next) {
user = l->data;
FB_ID_TO_STR(user->uid, uid);
bdy = purple_blist_find_buddy(acct, uid);
if ((bdy != NULL) && (purple_buddy_get_group(bdy) == grpn)) {
purple_blist_remove_buddy(bdy);
}
bdy = purple_buddy_new(acct, uid, NULL);
purple_blist_add_buddy(bdy, NULL, grp, NULL);
purple_buddy_set_server_alias(bdy, user->name);
}
for (l = removed; l != NULL; l = l->next) {
bdy = purple_blist_find_buddy(acct, l->data);
if (bdy != NULL) {
purple_blist_remove_buddy(bdy);
}
}
fb_sync_contacts_add_timeout(fata);
}
static void
fb_cb_api_error(FbApi *api, GError *error, gpointer data)
{
FbData *fata = data;
PurpleConnection *gc;
PurpleConnectionError errc;
gc = fb_data_get_connection(fata);
if (error->domain == G_IO_ERROR) {
purple_connection_g_error(gc, error);
return;
}
if (g_error_matches(error, FB_API_ERROR, FB_API_ERROR_QUEUE)) {
/* Save the reset data */
fb_data_save(fata);
}
if ((error->domain == FB_HTTP_ERROR) &&
(error->code >= 400) &&
(error->code <= 500))
{
errc = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
} else if (g_error_matches(error, FB_API_ERROR, FB_API_ERROR_AUTH)) {
errc = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
} else {
errc = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
}
if (!g_error_matches(error, FB_API_ERROR, FB_API_ERROR_NONFATAL)) {
purple_connection_error(gc, errc, error->message);
}
}
static void
fb_cb_api_events(FbApi *api, GSList *events, gpointer data)
{
FbData *fata = data;
FbApiEvent *event;
gchar uid[FB_ID_STRMAX];
gchar tid[FB_ID_STRMAX];
GHashTable *fetch;
GHashTableIter iter;
GSList *l;
PurpleAccount *acct;
PurpleConversation *chat;
PurpleConversationManager *manager;
PurpleConnection *gc;
gc = fb_data_get_connection(fata);
acct = purple_connection_get_account(gc);
fetch = g_hash_table_new(fb_id_hash, fb_id_equal);
manager = purple_conversation_manager_get_default();
for (l = events; l != NULL; l = l->next) {
event = l->data;
FB_ID_TO_STR(event->tid, tid);
chat = purple_conversation_manager_find_chat(manager, acct, tid);
if (chat == NULL) {
continue;
}
FB_ID_TO_STR(event->uid, uid);
switch (event->type) {
case FB_API_EVENT_TYPE_THREAD_TOPIC:
purple_chat_conversation_set_topic(PURPLE_CHAT_CONVERSATION(chat), uid,
event->text);
break;
case FB_API_EVENT_TYPE_THREAD_USER_ADDED:
if (purple_blist_find_buddy(acct, uid) == NULL) {
if (event->text) {
FbApiUser *user = fb_api_user_dup(NULL, FALSE);
user->uid = event->uid;
user->name = g_strdup(event->text);
fb_buddy_add_nonfriend(acct, user);
fb_api_user_free(user);
} else {
g_hash_table_insert(fetch, &event->tid, event);
break;
}
}
purple_chat_conversation_add_user(PURPLE_CHAT_CONVERSATION(chat), uid, NULL, 0,
TRUE);
break;
case FB_API_EVENT_TYPE_THREAD_USER_REMOVED:
purple_chat_conversation_remove_user(PURPLE_CHAT_CONVERSATION(chat), uid, event->text);
break;
}
}
g_hash_table_iter_init(&iter, fetch);
while (g_hash_table_iter_next(&iter, NULL, (gpointer) &event)) {
fb_api_thread(api, event->tid);
}
g_hash_table_destroy(fetch);
}
static void
fb_cb_image(FbDataImage *img, GError *error)
{
const gchar *url;
FbApi *api;
FbApiMessage *msg;
FbData *fata;
gsize size;
GSList *msgs = NULL;
guint id;
guint8 *image;
PurpleImage *pimg;
fata = fb_data_image_get_fata(img);
msg = fb_data_image_get_data(img);
if (G_UNLIKELY(error != NULL)) {
url = fb_data_image_get_url(img);
fb_util_debug_warning("Failed to retrieve image %s: %s",
url, error->message);
return;
}
api = fb_data_get_api(fata);
image = fb_data_image_dup_image(img, &size);
pimg = purple_image_new_from_data(image, size);
id = purple_image_store_add_weak(pimg);
g_free(msg->text);
msg->text = g_strdup_printf("<img src=\""
PURPLE_IMAGE_STORE_PROTOCOL
"%u\">", id);
msg->flags |= FB_API_MESSAGE_FLAG_DONE;
msgs = g_slist_prepend(msgs, msg);
fb_cb_api_messages(api, msgs, fata);
g_slist_free(msgs);
}
static void
fb_cb_api_messages(FbApi *api, GSList *msgs, gpointer data)
{
const gchar *text;
FbApiMessage *msg;
FbData *fata = data;
gboolean isself;
gboolean mark;
gboolean open;
gboolean self;
gchar *html;
gchar tid[FB_ID_STRMAX];
gchar uid[FB_ID_STRMAX];
gint id;
gint64 tstamp;
GSList *l;
PurpleAccount *acct;
PurpleConversation *chat;
PurpleConversationManager *manager;
PurpleConnection *gc;
PurpleMessageFlags flags;
gc = fb_data_get_connection(fata);
acct = purple_connection_get_account(gc);
mark = purple_account_get_bool(acct, "mark-read", TRUE);
open = purple_account_get_bool(acct, "group-chat-open", TRUE);
self = purple_account_get_bool(acct, "show-self", TRUE);
manager = purple_conversation_manager_get_default();
for (l = msgs; l != NULL; l = l->next) {
msg = l->data;
FB_ID_TO_STR(msg->uid, uid);
if (purple_blist_find_buddy(acct, uid) == NULL) {
msg = fb_api_message_dup(msg, TRUE);
fb_data_add_message(fata, msg);
fb_api_contact(api, msg->uid);
continue;
}
isself = (msg->flags & FB_API_MESSAGE_FLAG_SELF) != 0;
if (isself && !self) {
continue;
}
flags = isself ? PURPLE_MESSAGE_SEND : PURPLE_MESSAGE_RECV;
tstamp = msg->tstamp / 1000;
if (msg->flags & FB_API_MESSAGE_FLAG_IMAGE) {
if (!(msg->flags & FB_API_MESSAGE_FLAG_DONE)) {
msg = fb_api_message_dup(msg, TRUE);
fb_data_image_add(fata, msg->text, fb_cb_image,
msg, (GDestroyNotify)
fb_api_message_free);
fb_data_image_queue(fata);
continue;
}
flags |= PURPLE_MESSAGE_IMAGES;
text = msg->text;
html = NULL;
} else {
html = purple_markup_escape_text(msg->text, -1);
text = html;
}
if (msg->tid == 0) {
if (mark && !isself) {
fb_data_set_unread(fata, msg->uid, TRUE);
}
fb_util_serv_got_im(gc, uid, text, flags, tstamp);
g_free(html);
continue;
}
FB_ID_TO_STR(msg->tid, tid);
chat = purple_conversation_manager_find_chat(manager, acct, tid);
if (chat == NULL) {
if (!open) {
g_free(html);
continue;
}
id = fb_id_hash(&msg->tid);
purple_serv_got_joined_chat(gc, id, tid);
fb_api_thread(api, msg->tid);
} else {
id = purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(chat));
}
if (mark && !isself) {
fb_data_set_unread(fata, msg->tid, TRUE);
}
fb_util_serv_got_chat_in(gc, id, uid, text, flags, tstamp);
g_free(html);
}
}
static void
fb_cb_api_presences(FbApi *api, GSList *presences, gpointer data)
{
const gchar *statid;
FbData *fata = data;
gchar uid[FB_ID_STRMAX];
GSList *l;
PurpleAccount *acct;
PurpleConnection *gc;
PurpleStatusPrimitive pstat;
gc = fb_data_get_connection(fata);
acct = purple_connection_get_account(gc);
for (l = presences; l != NULL; l = l->next) {
FbApiPresence *api_presence = l->data;
if (api_presence->active) {
pstat = PURPLE_STATUS_AVAILABLE;
} else {
pstat = PURPLE_STATUS_OFFLINE;
}
FB_ID_TO_STR(api_presence->uid, uid);
statid = purple_primitive_get_id_from_type(pstat);
purple_protocol_got_user_status(acct, uid, statid, NULL);
}
}
static void
fb_cb_api_thread(FbApi *api, FbApiThread *thrd, gpointer data)
{
const gchar *name;
FbApiUser *user;
FbData *fata = data;
gboolean active;
gchar tid[FB_ID_STRMAX];
gchar uid[FB_ID_STRMAX];
gint id;
GSList *l;
PurpleAccount *acct;
PurpleConversation *conv;
PurpleConversationManager *manager;
PurpleChatConversation *chat;
PurpleConnection *gc;
gc = fb_data_get_connection(fata);
acct = purple_connection_get_account(gc);
id = fb_id_hash(&thrd->tid);
FB_ID_TO_STR(thrd->tid, tid);
manager = purple_conversation_manager_get_default();
conv = purple_conversation_manager_find_chat(manager, acct, tid);
if((conv == NULL) || purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv))) {
conv = purple_serv_got_joined_chat(gc, id, tid);
chat = PURPLE_CHAT_CONVERSATION(conv);
active = FALSE;
} else {
/* If there are no users in the group chat, including
* the local user, then the group chat has yet to be
* setup by this function. As a result, any group chat
* without users is inactive.
*/
chat = PURPLE_CHAT_CONVERSATION(conv);
active = purple_chat_conversation_get_users_count(chat) > 0;
}
if (!active) {
name = purple_account_get_username(acct);
purple_chat_conversation_add_user(chat, name, NULL, 0, FALSE);
}
purple_chat_conversation_set_topic(chat, NULL, thrd->topic);
for (l = thrd->users; l != NULL; l = l->next) {
user = l->data;
FB_ID_TO_STR(user->uid, uid);
if (purple_chat_conversation_has_user(chat, uid)) {
continue;
}
if (purple_blist_find_buddy(acct, uid) == NULL) {
fb_buddy_add_nonfriend(acct, user);
}
purple_chat_conversation_add_user(chat, uid, NULL, 0, active);
}
}
static void
fb_cb_api_thread_create(FbApi *api, FbId tid, gpointer data)
{
FbData *fata = data;
gchar sid[FB_ID_STRMAX];
GHashTable *table;
PurpleConnection *gc;
gc = fb_data_get_connection(fata);
FB_ID_TO_STR(tid, sid);
table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
g_hash_table_insert(table, "name", g_strdup(sid));
purple_serv_join_chat(gc, table);
g_hash_table_destroy(table);
}
static void
fb_cb_api_thread_kicked(FbApi *api, FbApiThread *thrd, gpointer data)
{
FbData *fata = data;
gchar tid[FB_ID_STRMAX];
PurpleAccount *acct;
PurpleConnection *gc;
PurpleConversation *chat;
PurpleConversationManager *manager;
FB_ID_TO_STR(thrd->tid, tid);
gc = fb_data_get_connection(fata);
acct = purple_connection_get_account(gc);
manager = purple_conversation_manager_get_default();
chat = purple_conversation_manager_find_chat(manager, acct, tid);
if (chat == NULL) {
PurpleRequestCommonParameters *cpar;
cpar = purple_request_cpar_from_connection(gc);
purple_notify_error(gc,
_("Join a Chat"),
_("Failed to Join Chat"),
_("You have been removed from this chat"),
cpar);
return;
}
purple_conversation_write_system_message(chat,
_("You have been removed from this chat"), 0);
purple_serv_got_chat_left(gc, purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(chat)));
}
static void
fb_cb_api_threads(FbApi *api, GSList *thrds, gpointer data)
{
const gchar *alias;
FbApiUser *user;
FbData *fata = data;
gchar tid[FB_ID_STRMAX];
gchar uid[FB_ID_STRMAX];
GSList *l;
GSList *m;
GString *gstr;
FbApiThread *thrd;
PurpleAccount *acct;
PurpleBuddy *bdy;
PurpleConnection *gc;
PurpleRoomlist *list;
PurpleRoomlistRoom *room;
list = fb_data_get_roomlist(fata);
if (G_UNLIKELY(list == NULL)) {
return;
}
gc = fb_data_get_connection(fata);
acct = purple_connection_get_account(gc);
gstr = g_string_new(NULL);
for (l = thrds; l != NULL; l = l->next) {
thrd = l->data;
FB_ID_TO_STR(thrd->tid, tid);
g_string_truncate(gstr, 0);
for (m = thrd->users; m != NULL; m = m->next) {
user = m->data;
FB_ID_TO_STR(user->uid, uid);
bdy = purple_blist_find_buddy(acct, uid);
if (bdy != NULL) {
alias = purple_buddy_get_alias(bdy);
} else {
alias = user->name;
}
if (gstr->len > 0) {
g_string_append(gstr, ", ");
}
g_string_append(gstr, alias);
}
room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM,
tid, NULL);
purple_roomlist_room_add_field(list, room, thrd->topic);
purple_roomlist_room_add_field(list, room, gstr->str);
purple_roomlist_room_add(list, room);
}
purple_roomlist_set_in_progress(list, FALSE);
fb_data_set_roomlist(fata, NULL);
g_string_free(gstr, TRUE);
}
static void
fb_cb_api_typing(FbApi *api, FbApiTyping *typg, gpointer data)
{
FbData *fata = data;
gchar uid[FB_ID_STRMAX];
PurpleConnection *gc;
gc = fb_data_get_connection(fata);
FB_ID_TO_STR(typg->uid, uid);
if (typg->state) {
purple_serv_got_typing(gc, uid, 0, PURPLE_IM_TYPING);
} else {
purple_serv_got_typing_stopped(gc, uid);
}
}
static void
fb_mark_read(FbData *fata, FbId id, gboolean thread)
{
FbApi *api;
PurpleAccount *acct;
PurpleConnection *gc;
gc = fb_data_get_connection(fata);
acct = purple_connection_get_account(gc);
api = fb_data_get_api(fata);
if (!fb_data_get_unread(fata, id) ||
(purple_account_get_bool(acct, "mark-read-available", FALSE) &&
fb_api_is_invisible(api)))
{
return;
}
fb_data_set_unread(fata, id, FALSE);
fb_api_read(api, id, thread);
}
static gboolean
fb_cb_conv_read(gpointer data)
{
const gchar *name;
FbData *fata;
FbId id;
gchar *tname;
PurpleConnection *gc;
PurpleConversation *conv = data;
gc = purple_conversation_get_connection(conv);
fata = purple_connection_get_protocol_data(gc);
name = purple_conversation_get_name(conv);
id = FB_ID_FROM_STR(name);
tname = g_strconcat("conv-read-", name, NULL);
fb_data_clear_timeout(fata, tname, FALSE);
g_free(tname);
if (purple_conversation_has_focus(conv)) {
fb_mark_read(fata, id, PURPLE_IS_CHAT_CONVERSATION(conv));
}
return FALSE;
}
static void
fb_cb_conv_updated(PurpleConversation *conv, PurpleConversationUpdateType type,
gpointer data)
{
const gchar *name;
const gchar *pid;
FbData *fata = data;
gchar *tname;
PurpleAccount *acct;
acct = purple_conversation_get_account(conv);
pid = purple_account_get_protocol_id(acct);
if ((type == PURPLE_CONVERSATION_UPDATE_UNSEEN) &&
purple_strequal(pid, FB_PROTOCOL_ID) &&
purple_account_get_bool(acct, "mark-read", TRUE))
{
/* Use event loop for purple_conversation_has_focus() */
name = purple_conversation_get_name(conv);
tname = g_strconcat("conv-read-", name, NULL);
fb_data_add_timeout(fata, tname, 0, fb_cb_conv_read, conv);
g_free(tname);
}
}
static void
fb_cb_conv_deleting(PurpleConversation *conv, gpointer data)
{
const gchar *name;
const gchar *pid;
FbData *fata = data;
gchar *tname;
PurpleAccount *acct;
acct = purple_conversation_get_account(conv);
pid = purple_account_get_protocol_id(acct);
if (!purple_strequal(pid, FB_PROTOCOL_ID)) {
return;
}
name = purple_conversation_get_name(conv);
tname = g_strconcat("conv-read-", name, NULL);
fb_data_clear_timeout(fata, tname, TRUE);
g_free(tname);
}
static void
fb_blist_chat_create(GSList *buddies, gpointer data)
{
const gchar *name;
FbApi *api;
FbData *fata = data;
FbId *did;
FbId uid;
GSList *l;
GSList *uids = NULL;
PurpleConnection *gc;
PurpleRequestCommonParameters *cpar;
gc = fb_data_get_connection(fata);
api = fb_data_get_api(fata);
if (g_slist_length(buddies) < 2) {
cpar = purple_request_cpar_from_connection(gc);
purple_notify_error(gc,
_("Initiate Chat"),
_("Failed to Initiate Chat"),
_("At least two initial chat participants"
" are required."),
cpar);
return;
}
for (l = buddies; l != NULL; l = l->next) {
name = purple_buddy_get_name(l->data);
uid = FB_ID_FROM_STR(name);
did = g_memdup2(&uid, sizeof uid);
uids = g_slist_prepend(uids, did);
}
fb_api_thread_create(api, uids);
g_slist_free_full(uids, g_free);
}
static void
fb_blist_chat_init(PurpleBlistNode *node, gpointer data)
{
FbData *fata = data;
GSList *select = NULL;
PurpleConnection *gc;
if (!PURPLE_IS_BUDDY(node)) {
return;
}
gc = fb_data_get_connection(fata);
select = g_slist_prepend(select, PURPLE_BUDDY(node));
fb_util_request_buddy(gc,
_("Initiate Chat"),
_("Initial Chat Participants"),
_("Select at least two initial participants."),
select, TRUE,
G_CALLBACK(fb_blist_chat_create), NULL,
fata);
g_slist_free(select);
}
static GList *
fb_get_account_options(PurpleProtocol *protocol) {
GList *opts = NULL;
PurpleAccountOption *opt;
opt = purple_account_option_int_new(_("Buddy list sync interval"),
"sync-interval", 5);
opts = g_list_prepend(opts, opt);
opt = purple_account_option_bool_new(_("Mark messages as read on focus"),
"mark-read", TRUE);
opts = g_list_prepend(opts, opt);
opt = purple_account_option_bool_new(_("Mark messages as read only when available"),
"mark-read-available", FALSE);
opts = g_list_prepend(opts, opt);
opt = purple_account_option_bool_new(_("Show self messages"),
"show-self", TRUE);
opts = g_list_prepend(opts, opt);
opt = purple_account_option_bool_new(_("Show unread messages"),
"show-unread", TRUE);
opts = g_list_prepend(opts, opt);
opt = purple_account_option_bool_new(_("Open new group chats with "
"incoming messages"),
"group-chat-open", TRUE);
opts = g_list_prepend(opts, opt);
return g_list_reverse(opts);
}
static void
fb_login(PurpleAccount *acct)
{
const gchar *pass;
const gchar *user;
FbApi *api;
FbData *fata;
gpointer convh;
PurpleConnection *gc;
GProxyResolver *resolver;
GError *error = NULL;
gc = purple_account_get_connection(acct);
resolver = purple_proxy_get_proxy_resolver(acct, &error);
if (resolver == NULL) {
fb_util_debug_error("Unable to get account proxy resolver: %s",
error->message);
purple_connection_take_error(gc, error);
return;
}
fata = fb_data_new(gc, resolver);
api = fb_data_get_api(fata);
convh = purple_conversations_get_handle();
purple_connection_set_protocol_data(gc, fata);
g_signal_connect(api,
"auth",
G_CALLBACK(fb_cb_api_auth),
fata);
g_signal_connect(api,
"connect",
G_CALLBACK(fb_cb_api_connect),
fata);
g_signal_connect(api,
"contact",
G_CALLBACK(fb_cb_api_contact),
fata);
g_signal_connect(api,
"contacts",
G_CALLBACK(fb_cb_api_contacts),
fata);
g_signal_connect(api,
"contacts-delta",
G_CALLBACK(fb_cb_api_contacts_delta),
fata);
g_signal_connect(api,
"error",
G_CALLBACK(fb_cb_api_error),
fata);
g_signal_connect(api,
"events",
G_CALLBACK(fb_cb_api_events),
fata);
g_signal_connect(api,
"messages",
G_CALLBACK(fb_cb_api_messages),
fata);
g_signal_connect(api,
"presences",
G_CALLBACK(fb_cb_api_presences),
fata);
g_signal_connect(api,
"thread",
G_CALLBACK(fb_cb_api_thread),
fata);
g_signal_connect(api,
"thread-create",
G_CALLBACK(fb_cb_api_thread_create),
fata);
g_signal_connect(api,
"thread-kicked",
G_CALLBACK(fb_cb_api_thread_kicked),
fata);
g_signal_connect(api,
"threads",
G_CALLBACK(fb_cb_api_threads),
fata);
g_signal_connect(api,
"typing",
G_CALLBACK(fb_cb_api_typing),
fata);
purple_signal_connect(convh,
"conversation-updated",
gc,
G_CALLBACK(fb_cb_conv_updated),
fata);
purple_signal_connect(convh,
"deleting-conversation",
gc,
G_CALLBACK(fb_cb_conv_deleting),
fata);
if (!fb_data_load(fata) || !purple_account_get_remember_password(acct)) {
user = purple_account_get_username(acct);
pass = purple_connection_get_password(gc);
purple_connection_update_progress(gc, _("Authenticating"),
1, 4);
fb_api_auth(api, user, pass);
return;
}
purple_connection_update_progress(gc, _("Fetching contacts"), 2, 4);
fb_api_contacts(api);
}
static void
fb_close(PurpleConnection *gc)
{
FbApi *api;
FbData *fata;
fata = purple_connection_get_protocol_data(gc);
api = fb_data_get_api(fata);
fb_data_save(fata);
fb_api_disconnect(api);
g_object_unref(fata);
purple_connection_set_protocol_data(gc, NULL);
purple_signals_disconnect_by_handle(gc);
}
static GList *
fb_status_types(PurpleAccount *acct)
{
PurpleStatusType *type;
GList *types = NULL;
type = purple_status_type_new(PURPLE_STATUS_AVAILABLE,
NULL, NULL, TRUE);
types = g_list_prepend(types, type);
/* Just a NULL state (as of now) for compatibility */
type = purple_status_type_new(PURPLE_STATUS_AWAY,
NULL, NULL, TRUE);
types = g_list_prepend(types, type);
type = purple_status_type_new(PURPLE_STATUS_INVISIBLE,
NULL, NULL, TRUE);
types = g_list_prepend(types, type);
type = purple_status_type_new(PURPLE_STATUS_OFFLINE,
NULL, NULL, TRUE);
types = g_list_prepend(types, type);
return g_list_reverse(types);
}
static const char *
fb_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
{
return "facebook";
}
static void
fb_client_tooltip_text(PurpleProtocolClient *client, PurpleBuddy *buddy,
PurpleNotifyUserInfo *info, gboolean full)
{
const gchar *name;
PurplePresence *presence;
PurpleStatus *status;
presence = purple_buddy_get_presence(buddy);
status = purple_presence_get_active_status(presence);
if (!PURPLE_BUDDY_IS_ONLINE(buddy)) {
/* Prevent doubles statues for Offline buddies */
/* See: pidgin_get_tooltip_text() in gtkblist.c */
purple_notify_user_info_remove_last_item(info);
}
name = purple_status_get_name(status);
purple_notify_user_info_add_pair_plaintext(info, _("Status"), name);
}
static GList *
fb_client_blist_node_menu(PurpleProtocolClient *client, PurpleBlistNode *node)
{
FbData *fata;
GList *acts = NULL;
PurpleAccount *acct;
PurpleConnection *gc;
PurpleActionMenu *act;
if (!PURPLE_IS_BUDDY(node)) {
return NULL;
}
acct = purple_buddy_get_account(PURPLE_BUDDY(node));
gc = purple_account_get_connection(acct);
fata = purple_connection_get_protocol_data(gc);
act = purple_action_menu_new(_("Initiate _Chat"),
PURPLE_CALLBACK(fb_blist_chat_init),
fata, NULL);
acts = g_list_prepend(acts, act);
return g_list_reverse(acts);
}
static gboolean
fb_client_offline_message(PurpleProtocolClient *client, PurpleBuddy *buddy)
{
return TRUE;
}
static void
fb_server_set_status(PurpleProtocolServer *protocol_server,
PurpleAccount *acct, PurpleStatus *status)
{
FbApi *api;
FbData *fata;
gboolean invis;
PurpleConnection *gc;
PurpleStatusPrimitive pstat;
PurpleStatusType *type;
gc = purple_account_get_connection(acct);
fata = purple_connection_get_protocol_data(gc);
api = fb_data_get_api(fata);
type = purple_status_get_status_type(status);
pstat = purple_status_type_get_primitive(type);
invis = fb_api_is_invisible(api);
if ((pstat == PURPLE_STATUS_INVISIBLE) && !invis) {
fb_api_connect(api, TRUE);
} else if ((pstat != PURPLE_STATUS_OFFLINE) && invis) {
fb_api_connect(api, FALSE);
}
}
static gint
fb_im_send(PurpleProtocolIM *im, PurpleConnection *gc, PurpleMessage *msg)
{
const gchar *name;
const gchar *text;
FbApi *api;
FbData *fata;
FbId uid;
gchar *sext;
fata = purple_connection_get_protocol_data(gc);
api = fb_data_get_api(fata);
name = purple_message_get_recipient(msg);
uid = FB_ID_FROM_STR(name);
text = purple_message_get_contents(msg);
sext = purple_markup_strip_html(text);
fb_api_message(api, uid, FALSE, sext);
g_free(sext);
return 1;
}
static guint
fb_im_send_typing(PurpleProtocolIM *im, PurpleConnection *gc,
const gchar *name, PurpleIMTypingState state)
{
FbApi *api;
FbData *fata;
FbId uid;
fata = purple_connection_get_protocol_data(gc);
api = fb_data_get_api(fata);
uid = FB_ID_FROM_STR(name);
fb_api_typing(api, uid, state != PURPLE_IM_NOT_TYPING);
return 0;
}
static GList *
fb_chat_info(PurpleProtocolChat *protocol_chat, PurpleConnection *connection)
{
GList *pces = NULL;
PurpleProtocolChatEntry *pce;
pce = g_new0(PurpleProtocolChatEntry, 1);
pce->label = _("Chat _Name:");
pce->identifier = "name";
pce->required = TRUE;
pces = g_list_prepend(pces, pce);
return g_list_reverse(pces);
}
static GHashTable *
fb_chat_info_defaults(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
const gchar *name)
{
GHashTable *data;
data = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
g_hash_table_insert(data, "name", g_strdup(name));
return data;
}
static void
fb_chat_join(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
GHashTable *data)
{
const gchar *name;
FbApi *api;
FbData *fata;
FbId tid;
gint id;
PurpleConversation *chat;
PurpleConversationManager *manager;
PurpleRequestCommonParameters *cpar;
name = g_hash_table_lookup(data, "name");
g_return_if_fail(name != NULL);
if (!FB_ID_IS_STR(name)) {
cpar = purple_request_cpar_from_connection(gc);
purple_notify_error(gc,
_("Join a Chat"),
_("Failed to Join Chat"),
_("Invalid Facebook identifier."),
cpar);
return;
}
tid = FB_ID_FROM_STR(name);
id = fb_id_hash(&tid);
manager = purple_conversation_manager_get_default();
chat = purple_conversation_manager_find_chat_by_id(manager,
purple_connection_get_account(gc),
id);
if ((chat != NULL) && !purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(chat))) {
purple_conversation_present(chat);
return;
}
fata = purple_connection_get_protocol_data(gc);
api = fb_data_get_api(fata);
fb_api_thread(api, tid);
}
static gchar *
fb_chat_get_name(PurpleProtocolChat *protocol_chat, GHashTable *data)
{
const gchar *name;
name = g_hash_table_lookup(data, "name");
g_return_val_if_fail(name != NULL, NULL);
return g_strdup(name);
}
static void
fb_chat_invite(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
gint id, const gchar *msg, const gchar *who)
{
const gchar *name;
FbApi *api;
FbData *fata;
FbId tid;
FbId uid;
PurpleConversation *chat;
PurpleConversationManager *manager;
PurpleRequestCommonParameters *cpar;
if (!FB_ID_IS_STR(who)) {
cpar = purple_request_cpar_from_connection(gc);
purple_notify_error(gc,
_("Invite Buddy Into Chat Room"),
_("Failed to Invite User"),
_("Invalid Facebook identifier."),
cpar);
return;
}
fata = purple_connection_get_protocol_data(gc);
api = fb_data_get_api(fata);
manager = purple_conversation_manager_get_default();
chat = purple_conversation_manager_find_chat_by_id(manager,
purple_connection_get_account(gc),
id);
name = purple_conversation_get_name(chat);
tid = FB_ID_FROM_STR(name);
uid = FB_ID_FROM_STR(who);
fb_api_thread_invite(api, tid, uid);
}
static gint
fb_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, gint id,
PurpleMessage *msg)
{
const gchar *name;
const gchar *text;
FbApi *api;
FbData *fata;
FbId tid;
gchar *sext;
PurpleAccount *acct;
PurpleConversation *chat;
PurpleConversationManager *manager;
acct = purple_connection_get_account(gc);
fata = purple_connection_get_protocol_data(gc);
api = fb_data_get_api(fata);
manager = purple_conversation_manager_get_default();
chat = purple_conversation_manager_find_chat_by_id(manager, acct, id);
name = purple_conversation_get_name(chat);
tid = FB_ID_FROM_STR(name);
text = purple_message_get_contents(msg);
sext = purple_markup_strip_html(text);
fb_api_message(api, tid, TRUE, sext);
g_free(sext);
name = purple_account_get_username(acct);
purple_serv_got_chat_in(gc, id, name,
purple_message_get_flags(msg),
purple_message_get_contents(msg),
time(NULL));
return 0;
}
static void
fb_chat_set_topic(PurpleProtocolChat *protocol_chat, PurpleConnection *gc,
gint id, const gchar *topic)
{
const gchar *name;
FbApi *api;
FbData *fata;
FbId tid;
PurpleConversation *chat;
PurpleConversationManager *manager;
fata = purple_connection_get_protocol_data(gc);
api = fb_data_get_api(fata);
manager = purple_conversation_manager_get_default();
chat = purple_conversation_manager_find_chat_by_id(manager,
purple_connection_get_account(gc),
id);
name = purple_conversation_get_name(chat);
tid = FB_ID_FROM_STR(name);
fb_api_thread_topic(api, tid, topic);
}
static PurpleRoomlist *
fb_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist,
PurpleConnection *gc)
{
FbApi *api;
FbData *fata;
GList *flds = NULL;
PurpleAccount *acct;
PurpleRoomlist *list;
PurpleRoomlistField *fld;
fata = purple_connection_get_protocol_data(gc);
list = fb_data_get_roomlist(fata);
g_return_val_if_fail(list == NULL, NULL);
api = fb_data_get_api(fata);
acct = purple_connection_get_account(gc);
list = purple_roomlist_new(acct);
fb_data_set_roomlist(fata, list);
fld = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING,
_("Topic"), "topic", FALSE);
flds = g_list_prepend(flds, fld);
fld = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING,
_("Users"), "users", FALSE);
flds = g_list_prepend(flds, fld);
flds = g_list_reverse(flds);
purple_roomlist_set_fields(list, flds);
purple_roomlist_set_in_progress(list, TRUE);
fb_api_threads(api);
return list;
}
static void
fb_roomlist_cancel(PurpleProtocolRoomlist *protocol_roomlist,
PurpleRoomlist *list)
{
FbData *fata;
PurpleAccount *acct;
PurpleConnection *gc;
PurpleRoomlist *cist;
acct = purple_roomlist_get_account(list);
gc = purple_account_get_connection(acct);
fata = purple_connection_get_protocol_data(gc);
cist = fb_data_get_roomlist(fata);
if (G_LIKELY(cist == list)) {
fb_data_set_roomlist(fata, NULL);
}
purple_roomlist_set_in_progress(list, FALSE);
g_object_unref(list);
}
static PurpleCmdRet
fb_cmd_kick(PurpleConversation *conv, const gchar *cmd, gchar **args,
gchar **error, gpointer data)
{
const gchar *name;
FbApi *api;
FbData *fata;
FbId tid;
FbId uid;
GError *err = NULL;
PurpleAccount *acct;
PurpleBuddy *bdy;
PurpleConnection *gc;
PurpleChatConversation *chat;
g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(conv),
PURPLE_CMD_RET_FAILED);
gc = purple_conversation_get_connection(conv);
acct = purple_connection_get_account(gc);
chat = PURPLE_CHAT_CONVERSATION(conv);
bdy = fb_util_account_find_buddy(acct, chat, args[0], &err);
if (err != NULL) {
*error = g_strdup_printf(_("%s."), err->message);
g_error_free(err);
return PURPLE_CMD_RET_FAILED;
}
fata = purple_connection_get_protocol_data(gc);
api = fb_data_get_api(fata);
name = purple_conversation_get_name(conv);
tid = FB_ID_FROM_STR(name);
name = purple_buddy_get_name(bdy);
uid = FB_ID_FROM_STR(name);
fb_api_thread_remove(api, tid, uid);
return PURPLE_CMD_RET_OK;
}
static PurpleCmdRet
fb_cmd_leave(PurpleConversation *conv, const gchar *cmd, gchar **args,
gchar **error, gpointer data)
{
const gchar *name;
FbApi *api;
FbData *fata;
FbId tid;
gint id;
PurpleConnection *gc;
PurpleChatConversation *chat;
g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(conv),
PURPLE_CMD_RET_FAILED);
gc = purple_conversation_get_connection(conv);
fata = purple_connection_get_protocol_data(gc);
api = fb_data_get_api(fata);
chat = PURPLE_CHAT_CONVERSATION(conv);
id = purple_chat_conversation_get_id(chat);
name = purple_conversation_get_name(conv);
tid = FB_ID_FROM_STR(name);
purple_serv_got_chat_left(gc, id);
fb_api_thread_remove(api, tid, 0);
return PURPLE_CMD_RET_OK;
}
static void
facebook_protocol_init(FacebookProtocol *self) {
}
static void
facebook_protocol_class_init(FacebookProtocolClass *klass)
{
PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
protocol_class->get_account_options = fb_get_account_options;
protocol_class->login = fb_login;
protocol_class->close = fb_close;
protocol_class->status_types = fb_status_types;
protocol_class->list_icon = fb_list_icon;
}
static void
facebook_protocol_class_finalize(G_GNUC_UNUSED FacebookProtocolClass *klass)
{
}
static void
facebook_protocol_client_iface_init(PurpleProtocolClientInterface *iface)
{
iface->tooltip_text = fb_client_tooltip_text;
iface->blist_node_menu = fb_client_blist_node_menu;
iface->offline_message = fb_client_offline_message;
}
static void
facebook_protocol_server_init(PurpleProtocolServerInterface *iface)
{
iface->set_status = fb_server_set_status;
}
static void
facebook_protocol_im_iface_init(PurpleProtocolIMInterface *iface)
{
iface->send = fb_im_send;
iface->send_typing = fb_im_send_typing;
}
static void
facebook_protocol_chat_iface_init(PurpleProtocolChatInterface *iface)
{
iface->info = fb_chat_info;
iface->info_defaults = fb_chat_info_defaults;
iface->join = fb_chat_join;
iface->get_name = fb_chat_get_name;
iface->invite = fb_chat_invite;
iface->send = fb_chat_send;
iface->set_topic = fb_chat_set_topic;
}
static void
facebook_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface *iface)
{
iface->get_list = fb_roomlist_get_list;
iface->cancel = fb_roomlist_cancel;
}
G_DEFINE_DYNAMIC_TYPE_EXTENDED(
FacebookProtocol, facebook_protocol, PURPLE_TYPE_PROTOCOL, 0,
G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
facebook_protocol_client_iface_init)
G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
facebook_protocol_server_init)
G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
facebook_protocol_im_iface_init)
G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
facebook_protocol_chat_iface_init)
G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST,
facebook_protocol_roomlist_iface_init));
static void
fb_cmds_register(void)
{
PurpleCmdId id;
static PurpleCmdFlag cflags =
PURPLE_CMD_FLAG_CHAT |
PURPLE_CMD_FLAG_PROTOCOL_ONLY;
g_return_if_fail(fb_cmds == NULL);
id = purple_cmd_register("kick", "s", PURPLE_CMD_P_PROTOCOL, cflags,
FB_PROTOCOL_ID, fb_cmd_kick,
_("kick: Kick someone from the chat"),
NULL);
fb_cmds = g_slist_prepend(fb_cmds, GUINT_TO_POINTER(id));
id = purple_cmd_register("leave", "", PURPLE_CMD_P_PROTOCOL, cflags,
FB_PROTOCOL_ID, fb_cmd_leave,
_("leave: Leave the chat"),
NULL);
fb_cmds = g_slist_prepend(fb_cmds, GUINT_TO_POINTER(id));
}
static void
fb_cmds_unregister_free(gpointer data)
{
PurpleCmdId id = GPOINTER_TO_UINT(data);
purple_cmd_unregister(id);
}
static void
fb_cmds_unregister(void)
{
g_slist_free_full(fb_cmds, fb_cmds_unregister_free);
}
static GPluginPluginInfo *
facebook_query(GError **error)
{
return purple_plugin_info_new(
"id", FB_PROTOCOL_ID,
"name", "Facebook Protocol",
"version", DISPLAY_VERSION,
"category", N_("Protocol"),
"summary", N_("Facebook Protocol Plugin"),
"description", N_("Facebook Protocol Plugin"),
"website", PURPLE_WEBSITE,
"abi-version", PURPLE_ABI_VERSION,
"flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
NULL
);
}
static gboolean
facebook_load(GPluginPlugin *plugin, GError **error)
{
PurpleProtocolManager *manager = purple_protocol_manager_get_default();
facebook_protocol_register_type(G_TYPE_MODULE(plugin));
fb_protocol = g_object_new(FACEBOOK_TYPE_PROTOCOL,
"id", FB_PROTOCOL_ID,
"name", "Facebook",
"description", "Facebook is a chat protocol, developed by Facebook",
"icon-name", "im-facebook",
"icon-resource-path", "/im/pidgin/libpurple/facebook/icons",
"options", OPT_PROTO_CHAT_TOPIC,
NULL);
if(!purple_protocol_manager_register(manager, fb_protocol, error)) {
g_clear_object(&fb_protocol);
return FALSE;
}
fb_cmds_register();
return TRUE;
}
static gboolean
facebook_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error)
{
PurpleProtocolManager *manager = purple_protocol_manager_get_default();
if(!purple_protocol_manager_unregister(manager, fb_protocol, error)) {
return FALSE;
}
fb_cmds_unregister();
g_clear_object(&fb_protocol);
return TRUE;
}
GPLUGIN_NATIVE_PLUGIN_DECLARE(facebook)