Replace functions which called once with their bodies
[pidgin-git.git] / libpurple / protocols / facebook / facebook.c
blob6889142091ec26e05ed7ea4550fbec98b4ec290b
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
24 #include "account.h"
25 #include "action.h"
26 #include "blistnode.h"
27 #include "buddy.h"
28 #include "buddyicon.h"
29 #include "buddylist.h"
30 #include "cmds.h"
31 #include "connection.h"
32 #include "conversation.h"
33 #include "conversations.h"
34 #include "conversationtypes.h"
35 #include "glibcompat.h"
36 #include "image.h"
37 #include "image-store.h"
38 #include "message.h"
39 #include "notify.h"
40 #include "plugins.h"
41 #include "presence.h"
42 #include "protocol.h"
43 #include "protocols.h"
44 #include "purpleaccountoption.h"
45 #include "request.h"
46 #include "roomlist.h"
47 #include "server.h"
48 #include "signals.h"
49 #include "sslconn.h"
50 #include "status.h"
51 #include "util.h"
52 #include "version.h"
54 #include "api.h"
55 #include "data.h"
56 #include "facebook.h"
57 #include "http.h"
58 #include "util.h"
60 static GSList *fb_cmds = NULL;
61 static PurpleProtocol *fb_protocol = NULL;
63 static void
64 fb_cb_api_messages(FbApi *api, GSList *msgs, gpointer data);
66 static PurpleGroup *
67 fb_get_group(gboolean friend)
69 PurpleBlistNode *n;
70 PurpleBlistNode *node;
71 PurpleGroup *grp;
72 const gchar *title;
74 if (friend) {
75 title = _("Facebook Friends");
76 } else {
77 title = _("Facebook Non-Friends");
80 grp = purple_blist_find_group(title);
82 if (G_UNLIKELY(grp == NULL)) {
83 grp = purple_group_new(title);
84 node = NULL;
86 for (n = purple_blist_get_default_root(); n != NULL;
87 n = n->next) {
88 node = n;
91 /* Append to the end of the buddy list */
92 purple_blist_add_group(grp, node);
94 if (!friend) {
95 node = PURPLE_BLIST_NODE(grp);
96 purple_blist_node_set_bool(node, "collapsed", TRUE);
100 return grp;
103 static void
104 fb_buddy_add_nonfriend(PurpleAccount *acct, FbApiUser *user)
106 gchar uid[FB_ID_STRMAX];
107 PurpleBuddy *bdy;
108 PurpleGroup *grp;
110 FB_ID_TO_STR(user->uid, uid);
111 bdy = purple_buddy_new(acct, uid, user->name);
112 grp = fb_get_group(FALSE);
114 purple_buddy_set_server_alias(bdy, user->name);
115 purple_blist_add_buddy(bdy, NULL, grp, NULL);
118 static void
119 fb_cb_api_auth(FbApi *api, gpointer data)
121 FbData *fata = data;
122 PurpleConnection *gc;
124 gc = fb_data_get_connection(fata);
126 purple_connection_update_progress(gc, _("Fetching contacts"), 2, 4);
127 fb_data_save(fata);
128 fb_api_contacts(api);
131 static void
132 fb_cb_api_connect(FbApi *api, gpointer data)
134 FbData *fata = data;
135 PurpleAccount *acct;
136 PurpleConnection *gc;
138 gc = fb_data_get_connection(fata);
139 acct = purple_connection_get_account(gc);
141 fb_data_save(fata);
142 purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
144 if (purple_account_get_bool(acct, "show-unread", TRUE)) {
145 fb_api_unread(api);
149 static void
150 fb_cb_api_contact(FbApi *api, FbApiUser *user, gpointer data)
152 FbData *fata = data;
153 gchar uid[FB_ID_STRMAX];
154 GSList *msgs;
155 PurpleAccount *acct;
156 PurpleConnection *gc;
158 gc = fb_data_get_connection(fata);
159 acct = purple_connection_get_account(gc);
160 FB_ID_TO_STR(user->uid, uid);
162 if (purple_blist_find_buddy(acct, uid) == NULL) {
163 fb_buddy_add_nonfriend(acct, user);
166 msgs = fb_data_take_messages(fata, user->uid);
168 if (msgs != NULL) {
169 fb_cb_api_messages(api, msgs, fata);
170 g_slist_free_full(msgs, (GDestroyNotify) fb_api_message_free);
174 static gboolean
175 fb_cb_sync_contacts(gpointer data)
177 FbApi *api;
178 FbData *fata = data;
180 api = fb_data_get_api(fata);
181 fb_data_clear_timeout(fata, "sync-contacts", FALSE);
182 fb_api_contacts(api);
183 return FALSE;
186 static void
187 fb_cb_icon(FbDataImage *img, GError *error)
189 const gchar *csum;
190 const gchar *name;
191 const gchar *str;
192 FbHttpParams *params;
193 gsize size;
194 guint8 *image;
195 PurpleAccount *acct;
196 PurpleBuddy *bdy;
198 bdy = fb_data_image_get_data(img);
199 acct = purple_buddy_get_account(bdy);
200 name = purple_buddy_get_name(bdy);
202 if (G_UNLIKELY(error != NULL)) {
203 fb_util_debug_warning("Failed to retrieve icon for %s: %s",
204 name, error->message);
205 return;
208 str = fb_data_image_get_url(img);
209 params = fb_http_params_new_parse(str, TRUE);
210 csum = fb_http_params_get_str(params, "oh", NULL);
212 image = fb_data_image_dup_image(img, &size);
213 purple_buddy_icons_set_for_user(acct, name, image, size, csum);
214 fb_http_params_free(params);
217 static void
218 fb_sync_contacts_add_timeout(FbData *fata)
220 gint sync;
221 PurpleConnection *gc;
222 PurpleAccount *acct;
224 gc = fb_data_get_connection(fata);
225 acct = purple_connection_get_account(gc);
227 sync = purple_account_get_int(acct, "sync-interval", 5);
229 if (sync < 1) {
230 purple_account_set_int(acct, "sync-interval", 1);
231 sync = 1;
234 sync *= 60 * 1000;
235 fb_data_add_timeout(fata, "sync-contacts", sync, fb_cb_sync_contacts,
236 fata);
239 static void
240 fb_cb_api_contacts(FbApi *api, GSList *users, gboolean complete, gpointer data)
242 const gchar *alias;
243 const gchar *csum;
244 FbApiUser *user;
245 FbData *fata = data;
246 FbId muid;
247 gchar uid[FB_ID_STRMAX];
248 GSList *l;
249 GValue val = G_VALUE_INIT;
250 PurpleAccount *acct;
251 PurpleBuddy *bdy;
252 PurpleConnection *gc;
253 PurpleConnectionState state;
254 PurpleGroup *grp;
255 PurpleGroup *grpn;
256 PurpleStatus *status;
257 PurpleStatusPrimitive pstat;
258 PurpleStatusType *type;
260 gc = fb_data_get_connection(fata);
261 acct = purple_connection_get_account(gc);
262 grp = fb_get_group(TRUE);
263 grpn = fb_get_group(FALSE);
264 alias = purple_account_get_private_alias(acct);
265 state = purple_connection_get_state(gc);
267 g_value_init(&val, FB_TYPE_ID);
268 g_object_get_property(G_OBJECT(api), "uid", &val);
269 muid = g_value_get_int64(&val);
270 g_value_unset(&val);
272 for (l = users; l != NULL; l = l->next) {
273 user = l->data;
274 FB_ID_TO_STR(user->uid, uid);
276 if (G_UNLIKELY(user->uid == muid)) {
277 if (G_UNLIKELY(alias != NULL)) {
278 continue;
281 purple_account_set_private_alias(acct, user->name);
282 continue;
285 bdy = purple_blist_find_buddy(acct, uid);
287 if ((bdy != NULL) && (purple_buddy_get_group(bdy) == grpn)) {
288 purple_blist_remove_buddy(bdy);
289 bdy = NULL;
292 if (bdy == NULL) {
293 bdy = purple_buddy_new(acct, uid, NULL);
294 purple_blist_add_buddy(bdy, NULL, grp, NULL);
297 purple_buddy_set_server_alias(bdy, user->name);
298 csum = purple_buddy_icons_get_checksum_for_user(bdy);
300 if (!purple_strequal(csum, user->csum)) {
301 fb_data_image_add(fata, user->icon, fb_cb_icon,
302 bdy, NULL);
306 fb_data_image_queue(fata);
308 if (!complete) {
309 return;
312 if (state != PURPLE_CONNECTION_CONNECTED) {
313 status = purple_account_get_active_status(acct);
314 type = purple_status_get_status_type(status);
315 pstat = purple_status_type_get_primitive(type);
317 purple_connection_update_progress(gc, _("Connecting"), 3, 4);
318 fb_api_connect(api, pstat == PURPLE_STATUS_INVISIBLE);
321 fb_sync_contacts_add_timeout(fata);
324 static void
325 fb_cb_api_contacts_delta(FbApi *api, GSList *added, GSList *removed, gpointer data)
327 FbApiUser *user;
328 FbData *fata = data;
329 gchar uid[FB_ID_STRMAX];
330 GSList *l;
331 PurpleAccount *acct;
332 PurpleBuddy *bdy;
333 PurpleConnection *gc;
334 PurpleGroup *grp;
335 PurpleGroup *grpn;
337 gc = fb_data_get_connection(fata);
338 acct = purple_connection_get_account(gc);
339 grp = fb_get_group(TRUE);
340 grpn = fb_get_group(FALSE);
342 for (l = added; l != NULL; l = l->next) {
343 user = l->data;
344 FB_ID_TO_STR(user->uid, uid);
346 bdy = purple_blist_find_buddy(acct, uid);
348 if ((bdy != NULL) && (purple_buddy_get_group(bdy) == grpn)) {
349 purple_blist_remove_buddy(bdy);
352 bdy = purple_buddy_new(acct, uid, NULL);
353 purple_blist_add_buddy(bdy, NULL, grp, NULL);
355 purple_buddy_set_server_alias(bdy, user->name);
358 for (l = removed; l != NULL; l = l->next) {
359 bdy = purple_blist_find_buddy(acct, l->data);
361 if (bdy != NULL) {
362 purple_blist_remove_buddy(bdy);
366 fb_sync_contacts_add_timeout(fata);
369 static void
370 fb_cb_api_error(FbApi *api, GError *error, gpointer data)
372 FbData *fata = data;
373 PurpleConnection *gc;
374 PurpleConnectionError errc;
376 gc = fb_data_get_connection(fata);
378 if (error->domain == G_IO_ERROR) {
379 purple_connection_g_error(gc, error);
380 return;
383 if (g_error_matches(error, FB_API_ERROR, FB_API_ERROR_QUEUE)) {
384 /* Save the reset data */
385 fb_data_save(fata);
388 if ((error->domain == FB_HTTP_ERROR) &&
389 (error->code >= 400) &&
390 (error->code <= 500))
392 errc = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
393 } else if (g_error_matches(error, FB_API_ERROR, FB_API_ERROR_AUTH)) {
394 errc = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
395 } else {
396 errc = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
400 if (!g_error_matches(error, FB_API_ERROR, FB_API_ERROR_NONFATAL)) {
401 purple_connection_error(gc, errc, error->message);
405 static void
406 fb_cb_api_events(FbApi *api, GSList *events, gpointer data)
408 FbData *fata = data;
409 FbApiEvent *event;
410 gchar uid[FB_ID_STRMAX];
411 gchar tid[FB_ID_STRMAX];
412 GHashTable *fetch;
413 GHashTableIter iter;
414 GSList *l;
415 PurpleAccount *acct;
416 PurpleChatConversation *chat;
417 PurpleConnection *gc;
419 gc = fb_data_get_connection(fata);
420 acct = purple_connection_get_account(gc);
421 fetch = g_hash_table_new(fb_id_hash, fb_id_equal);
423 for (l = events; l != NULL; l = l->next) {
424 event = l->data;
426 FB_ID_TO_STR(event->tid, tid);
427 chat = purple_conversations_find_chat_with_account(tid, acct);
429 if (chat == NULL) {
430 continue;
433 FB_ID_TO_STR(event->uid, uid);
435 switch (event->type) {
436 case FB_API_EVENT_TYPE_THREAD_TOPIC:
437 purple_chat_conversation_set_topic(chat, uid,
438 event->text);
439 break;
441 case FB_API_EVENT_TYPE_THREAD_USER_ADDED:
442 if (purple_blist_find_buddy(acct, uid) == NULL) {
443 if (event->text) {
444 FbApiUser *user = fb_api_user_dup(NULL, FALSE);
445 user->uid = event->uid;
446 user->name = g_strdup(event->text);
448 fb_buddy_add_nonfriend(acct, user);
450 fb_api_user_free(user);
451 } else {
452 g_hash_table_insert(fetch, &event->tid, event);
453 break;
457 purple_chat_conversation_add_user(chat, uid, NULL, 0,
458 TRUE);
459 break;
461 case FB_API_EVENT_TYPE_THREAD_USER_REMOVED:
462 purple_chat_conversation_remove_user(chat, uid, event->text);
463 break;
467 g_hash_table_iter_init(&iter, fetch);
469 while (g_hash_table_iter_next(&iter, NULL, (gpointer) &event)) {
470 fb_api_thread(api, event->tid);
473 g_hash_table_destroy(fetch);
476 static void
477 fb_cb_image(FbDataImage *img, GError *error)
479 const gchar *url;
480 FbApi *api;
481 FbApiMessage *msg;
482 FbData *fata;
483 gsize size;
484 GSList *msgs = NULL;
485 guint id;
486 guint8 *image;
487 PurpleImage *pimg;
489 fata = fb_data_image_get_fata(img);
490 msg = fb_data_image_get_data(img);
492 if (G_UNLIKELY(error != NULL)) {
493 url = fb_data_image_get_url(img);
494 fb_util_debug_warning("Failed to retrieve image %s: %s",
495 url, error->message);
496 return;
499 api = fb_data_get_api(fata);
500 image = fb_data_image_dup_image(img, &size);
501 pimg = purple_image_new_from_data(image, size);
502 id = purple_image_store_add_weak(pimg);
504 g_free(msg->text);
505 msg->text = g_strdup_printf("<img src=\""
506 PURPLE_IMAGE_STORE_PROTOCOL
507 "%u\">", id);
508 msg->flags |= FB_API_MESSAGE_FLAG_DONE;
510 msgs = g_slist_prepend(msgs, msg);
511 fb_cb_api_messages(api, msgs, fata);
512 g_slist_free(msgs);
515 static void
516 fb_cb_api_messages(FbApi *api, GSList *msgs, gpointer data)
518 const gchar *text;
519 FbApiMessage *msg;
520 FbData *fata = data;
521 gboolean isself;
522 gboolean mark;
523 gboolean open;
524 gboolean self;
525 gchar *html;
526 gchar tid[FB_ID_STRMAX];
527 gchar uid[FB_ID_STRMAX];
528 gint id;
529 gint64 tstamp;
530 GSList *l;
531 PurpleAccount *acct;
532 PurpleChatConversation *chat;
533 PurpleConnection *gc;
534 PurpleMessageFlags flags;
536 gc = fb_data_get_connection(fata);
537 acct = purple_connection_get_account(gc);
538 mark = purple_account_get_bool(acct, "mark-read", TRUE);
539 open = purple_account_get_bool(acct, "group-chat-open", TRUE);
540 self = purple_account_get_bool(acct, "show-self", TRUE);
542 for (l = msgs; l != NULL; l = l->next) {
543 msg = l->data;
544 FB_ID_TO_STR(msg->uid, uid);
546 if (purple_blist_find_buddy(acct, uid) == NULL) {
547 msg = fb_api_message_dup(msg, TRUE);
548 fb_data_add_message(fata, msg);
549 fb_api_contact(api, msg->uid);
550 continue;
553 isself = (msg->flags & FB_API_MESSAGE_FLAG_SELF) != 0;
555 if (isself && !self) {
556 continue;
559 flags = isself ? PURPLE_MESSAGE_SEND : PURPLE_MESSAGE_RECV;
560 tstamp = msg->tstamp / 1000;
562 if (msg->flags & FB_API_MESSAGE_FLAG_IMAGE) {
563 if (!(msg->flags & FB_API_MESSAGE_FLAG_DONE)) {
564 msg = fb_api_message_dup(msg, TRUE);
565 fb_data_image_add(fata, msg->text, fb_cb_image,
566 msg, (GDestroyNotify)
567 fb_api_message_free);
568 fb_data_image_queue(fata);
569 continue;
572 flags |= PURPLE_MESSAGE_IMAGES;
573 text = msg->text;
574 html = NULL;
575 } else {
576 html = purple_markup_escape_text(msg->text, -1);
577 text = html;
580 if (msg->tid == 0) {
581 if (mark && !isself) {
582 fb_data_set_unread(fata, msg->uid, TRUE);
585 fb_util_serv_got_im(gc, uid, text, flags, tstamp);
586 g_free(html);
587 continue;
590 FB_ID_TO_STR(msg->tid, tid);
591 chat = purple_conversations_find_chat_with_account(tid, acct);
593 if (chat == NULL) {
594 if (!open) {
595 g_free(html);
596 continue;
599 id = fb_id_hash(&msg->tid);
600 purple_serv_got_joined_chat(gc, id, tid);
601 fb_api_thread(api, msg->tid);
602 } else {
603 id = purple_chat_conversation_get_id(chat);
606 if (mark && !isself) {
607 fb_data_set_unread(fata, msg->tid, TRUE);
610 fb_util_serv_got_chat_in(gc, id, uid, text, flags, tstamp);
611 g_free(html);
615 static void
616 fb_cb_api_presences(FbApi *api, GSList *press, gpointer data)
618 const gchar *statid;
619 FbApiPresence *pres;
620 FbData *fata = data;
621 gchar uid[FB_ID_STRMAX];
622 GSList *l;
623 PurpleAccount *acct;
624 PurpleConnection *gc;
625 PurpleStatusPrimitive pstat;
627 gc = fb_data_get_connection(fata);
628 acct = purple_connection_get_account(gc);
630 for (l = press; l != NULL; l = l->next) {
631 pres = l->data;
633 if (pres->active) {
634 pstat = PURPLE_STATUS_AVAILABLE;
635 } else {
636 pstat = PURPLE_STATUS_OFFLINE;
639 FB_ID_TO_STR(pres->uid, uid);
640 statid = purple_primitive_get_id_from_type(pstat);
641 purple_protocol_got_user_status(acct, uid, statid, NULL);
645 static void
646 fb_cb_api_thread(FbApi *api, FbApiThread *thrd, gpointer data)
648 const gchar *name;
649 FbApiUser *user;
650 FbData *fata = data;
651 gboolean active;
652 gchar tid[FB_ID_STRMAX];
653 gchar uid[FB_ID_STRMAX];
654 gint id;
655 GSList *l;
656 PurpleAccount *acct;
657 PurpleChatConversation *chat;
658 PurpleConnection *gc;
660 gc = fb_data_get_connection(fata);
661 acct = purple_connection_get_account(gc);
662 id = fb_id_hash(&thrd->tid);
663 FB_ID_TO_STR(thrd->tid, tid);
665 chat = purple_conversations_find_chat_with_account(tid, acct);
667 if ((chat == NULL) || purple_chat_conversation_has_left(chat)) {
668 chat = purple_serv_got_joined_chat(gc, id, tid);
669 active = FALSE;
670 } else {
671 /* If there are no users in the group chat, including
672 * the local user, then the group chat has yet to be
673 * setup by this function. As a result, any group chat
674 * without users is inactive.
676 active = purple_chat_conversation_get_users_count(chat) > 0;
679 if (!active) {
680 name = purple_account_get_username(acct);
681 purple_chat_conversation_add_user(chat, name, NULL, 0, FALSE);
684 purple_chat_conversation_set_topic(chat, NULL, thrd->topic);
686 for (l = thrd->users; l != NULL; l = l->next) {
687 user = l->data;
688 FB_ID_TO_STR(user->uid, uid);
690 if (purple_chat_conversation_has_user(chat, uid)) {
691 continue;
694 if (purple_blist_find_buddy(acct, uid) == NULL) {
695 fb_buddy_add_nonfriend(acct, user);
698 purple_chat_conversation_add_user(chat, uid, NULL, 0, active);
702 static void
703 fb_cb_api_thread_create(FbApi *api, FbId tid, gpointer data)
705 FbData *fata = data;
706 gchar sid[FB_ID_STRMAX];
707 GHashTable *table;
708 PurpleConnection *gc;
710 gc = fb_data_get_connection(fata);
711 FB_ID_TO_STR(tid, sid);
713 table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
714 g_hash_table_insert(table, "name", g_strdup(sid));
715 purple_serv_join_chat(gc, table);
716 g_hash_table_destroy(table);
719 static void
720 fb_cb_api_thread_kicked(FbApi *api, FbApiThread *thrd, gpointer data)
722 FbData *fata = data;
723 gchar tid[FB_ID_STRMAX];
724 PurpleAccount *acct;
725 PurpleConnection *gc;
726 PurpleChatConversation *chat;
728 FB_ID_TO_STR(thrd->tid, tid);
730 gc = fb_data_get_connection(fata);
731 acct = purple_connection_get_account(gc);
732 chat = purple_conversations_find_chat_with_account(tid, acct);
734 if (chat == NULL) {
735 PurpleRequestCommonParameters *cpar;
737 cpar = purple_request_cpar_from_connection(gc);
738 purple_notify_error(gc,
739 _("Join a Chat"),
740 _("Failed to Join Chat"),
741 _("You have been removed from this chat"),
742 cpar);
743 return;
746 purple_conversation_write_system_message(PURPLE_CONVERSATION(chat),
747 _("You have been removed from this chat"), 0);
749 purple_serv_got_chat_left(gc, purple_chat_conversation_get_id(chat));
752 static void
753 fb_cb_api_threads(FbApi *api, GSList *thrds, gpointer data)
755 const gchar *alias;
756 FbApiUser *user;
757 FbData *fata = data;
758 gchar tid[FB_ID_STRMAX];
759 gchar uid[FB_ID_STRMAX];
760 GSList *l;
761 GSList *m;
762 GString *gstr;
763 FbApiThread *thrd;
764 PurpleAccount *acct;
765 PurpleBuddy *bdy;
766 PurpleConnection *gc;
767 PurpleRoomlist *list;
768 PurpleRoomlistRoom *room;
770 list = fb_data_get_roomlist(fata);
772 if (G_UNLIKELY(list == NULL)) {
773 return;
776 gc = fb_data_get_connection(fata);
777 acct = purple_connection_get_account(gc);
778 gstr = g_string_new(NULL);
780 for (l = thrds; l != NULL; l = l->next) {
781 thrd = l->data;
782 FB_ID_TO_STR(thrd->tid, tid);
783 g_string_truncate(gstr, 0);
785 for (m = thrd->users; m != NULL; m = m->next) {
786 user = m->data;
787 FB_ID_TO_STR(user->uid, uid);
788 bdy = purple_blist_find_buddy(acct, uid);
790 if (bdy != NULL) {
791 alias = purple_buddy_get_alias(bdy);
792 } else {
793 alias = user->name;
796 if (gstr->len > 0) {
797 g_string_append(gstr, ", ");
800 g_string_append(gstr, alias);
803 room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM,
804 tid, NULL);
805 purple_roomlist_room_add_field(list, room, thrd->topic);
806 purple_roomlist_room_add_field(list, room, gstr->str);
807 purple_roomlist_room_add(list, room);
810 purple_roomlist_set_in_progress(list, FALSE);
811 fb_data_set_roomlist(fata, NULL);
812 g_string_free(gstr, TRUE);
815 static void
816 fb_cb_api_typing(FbApi *api, FbApiTyping *typg, gpointer data)
818 FbData *fata = data;
819 gchar uid[FB_ID_STRMAX];
820 PurpleConnection *gc;
822 gc = fb_data_get_connection(fata);
823 FB_ID_TO_STR(typg->uid, uid);
825 if (typg->state) {
826 purple_serv_got_typing(gc, uid, 0, PURPLE_IM_TYPING);
827 } else {
828 purple_serv_got_typing_stopped(gc, uid);
832 static void
833 fb_mark_read(FbData *fata, FbId id, gboolean thread)
835 FbApi *api;
836 PurpleAccount *acct;
837 PurpleConnection *gc;
839 gc = fb_data_get_connection(fata);
840 acct = purple_connection_get_account(gc);
841 api = fb_data_get_api(fata);
843 if (!fb_data_get_unread(fata, id) ||
844 (purple_account_get_bool(acct, "mark-read-available", FALSE) &&
845 fb_api_is_invisible(api)))
847 return;
850 fb_data_set_unread(fata, id, FALSE);
851 fb_api_read(api, id, thread);
854 static gboolean
855 fb_cb_conv_read(gpointer data)
857 const gchar *name;
858 FbData *fata;
859 FbId id;
860 gchar *tname;
861 PurpleConnection *gc;
862 PurpleConversation *conv = data;
864 gc = purple_conversation_get_connection(conv);
865 fata = purple_connection_get_protocol_data(gc);
866 name = purple_conversation_get_name(conv);
867 id = FB_ID_FROM_STR(name);
869 tname = g_strconcat("conv-read-", name, NULL);
870 fb_data_clear_timeout(fata, tname, FALSE);
871 g_free(tname);
873 if (purple_conversation_has_focus(conv)) {
874 fb_mark_read(fata, id, PURPLE_IS_CHAT_CONVERSATION(conv));
876 return FALSE;
879 static void
880 fb_cb_conv_updated(PurpleConversation *conv, PurpleConversationUpdateType type,
881 gpointer data)
883 const gchar *name;
884 const gchar *pid;
885 FbData *fata = data;
886 gchar *tname;
887 PurpleAccount *acct;
889 acct = purple_conversation_get_account(conv);
890 pid = purple_account_get_protocol_id(acct);
892 if ((type == PURPLE_CONVERSATION_UPDATE_UNSEEN) &&
893 purple_strequal(pid, FB_PROTOCOL_ID) &&
894 purple_account_get_bool(acct, "mark-read", TRUE))
896 /* Use event loop for purple_conversation_has_focus() */
897 name = purple_conversation_get_name(conv);
898 tname = g_strconcat("conv-read-", name, NULL);
899 fb_data_add_timeout(fata, tname, 1, fb_cb_conv_read, conv);
900 g_free(tname);
904 static void
905 fb_cb_conv_deleting(PurpleConversation *conv, gpointer data)
907 const gchar *name;
908 const gchar *pid;
909 FbData *fata = data;
910 gchar *tname;
911 PurpleAccount *acct;
913 acct = purple_conversation_get_account(conv);
914 pid = purple_account_get_protocol_id(acct);
916 if (!purple_strequal(pid, FB_PROTOCOL_ID)) {
917 return;
920 name = purple_conversation_get_name(conv);
921 tname = g_strconcat("conv-read-", name, NULL);
922 fb_data_clear_timeout(fata, tname, TRUE);
923 g_free(tname);
926 static void
927 fb_blist_chat_create(GSList *buddies, gpointer data)
929 const gchar *name;
930 FbApi *api;
931 FbData *fata = data;
932 FbId *did;
933 FbId uid;
934 GSList *l;
935 GSList *uids = NULL;
936 PurpleConnection *gc;
937 PurpleRequestCommonParameters *cpar;
939 gc = fb_data_get_connection(fata);
940 api = fb_data_get_api(fata);
942 if (g_slist_length(buddies) < 2) {
943 cpar = purple_request_cpar_from_connection(gc);
944 purple_notify_error(gc,
945 _("Initiate Chat"),
946 _("Failed to Initiate Chat"),
947 _("At least two initial chat participants"
948 " are required."),
949 cpar);
950 return;
953 for (l = buddies; l != NULL; l = l->next) {
954 name = purple_buddy_get_name(l->data);
955 uid = FB_ID_FROM_STR(name);
956 did = g_memdup(&uid, sizeof uid);
957 uids = g_slist_prepend(uids, did);
960 fb_api_thread_create(api, uids);
961 g_slist_free_full(uids, g_free);
964 static void
965 fb_blist_chat_init(PurpleBlistNode *node, gpointer data)
967 FbData *fata = data;
968 GSList *select = NULL;
969 PurpleConnection *gc;
971 if (!PURPLE_IS_BUDDY(node)) {
972 return;
975 gc = fb_data_get_connection(fata);
976 select = g_slist_prepend(select, PURPLE_BUDDY(node));
978 fb_util_request_buddy(gc,
979 _("Initiate Chat"),
980 _("Initial Chat Participants"),
981 _("Select at least two initial participants."),
982 select, TRUE,
983 G_CALLBACK(fb_blist_chat_create), NULL,
984 fata);
985 g_slist_free(select);
988 static void
989 fb_login(PurpleAccount *acct)
991 const gchar *pass;
992 const gchar *user;
993 FbApi *api;
994 FbData *fata;
995 gpointer convh;
996 PurpleConnection *gc;
997 GProxyResolver *resolver;
998 GError *error = NULL;
1000 gc = purple_account_get_connection(acct);
1002 resolver = purple_proxy_get_proxy_resolver(acct, &error);
1003 if (resolver == NULL) {
1004 fb_util_debug_error("Unable to get account proxy resolver: %s",
1005 error->message);
1006 purple_connection_g_error(gc, error);
1007 return;
1010 fata = fb_data_new(gc, resolver);
1011 api = fb_data_get_api(fata);
1012 convh = purple_conversations_get_handle();
1013 purple_connection_set_protocol_data(gc, fata);
1015 g_signal_connect(api,
1016 "auth",
1017 G_CALLBACK(fb_cb_api_auth),
1018 fata);
1019 g_signal_connect(api,
1020 "connect",
1021 G_CALLBACK(fb_cb_api_connect),
1022 fata);
1023 g_signal_connect(api,
1024 "contact",
1025 G_CALLBACK(fb_cb_api_contact),
1026 fata);
1027 g_signal_connect(api,
1028 "contacts",
1029 G_CALLBACK(fb_cb_api_contacts),
1030 fata);
1031 g_signal_connect(api,
1032 "contacts-delta",
1033 G_CALLBACK(fb_cb_api_contacts_delta),
1034 fata);
1035 g_signal_connect(api,
1036 "error",
1037 G_CALLBACK(fb_cb_api_error),
1038 fata);
1039 g_signal_connect(api,
1040 "events",
1041 G_CALLBACK(fb_cb_api_events),
1042 fata);
1043 g_signal_connect(api,
1044 "messages",
1045 G_CALLBACK(fb_cb_api_messages),
1046 fata);
1047 g_signal_connect(api,
1048 "presences",
1049 G_CALLBACK(fb_cb_api_presences),
1050 fata);
1051 g_signal_connect(api,
1052 "thread",
1053 G_CALLBACK(fb_cb_api_thread),
1054 fata);
1055 g_signal_connect(api,
1056 "thread-create",
1057 G_CALLBACK(fb_cb_api_thread_create),
1058 fata);
1059 g_signal_connect(api,
1060 "thread-kicked",
1061 G_CALLBACK(fb_cb_api_thread_kicked),
1062 fata);
1063 g_signal_connect(api,
1064 "threads",
1065 G_CALLBACK(fb_cb_api_threads),
1066 fata);
1067 g_signal_connect(api,
1068 "typing",
1069 G_CALLBACK(fb_cb_api_typing),
1070 fata);
1072 purple_signal_connect(convh,
1073 "conversation-updated",
1075 G_CALLBACK(fb_cb_conv_updated),
1076 fata);
1077 purple_signal_connect(convh,
1078 "deleting-conversation",
1080 G_CALLBACK(fb_cb_conv_deleting),
1081 fata);
1083 if (!fb_data_load(fata) || !purple_account_get_remember_password(acct)) {
1084 user = purple_account_get_username(acct);
1085 pass = purple_connection_get_password(gc);
1086 purple_connection_update_progress(gc, _("Authenticating"),
1087 1, 4);
1088 fb_api_auth(api, user, pass);
1089 return;
1092 purple_connection_update_progress(gc, _("Fetching contacts"), 2, 4);
1093 fb_api_contacts(api);
1096 static void
1097 fb_close(PurpleConnection *gc)
1099 FbApi *api;
1100 FbData *fata;
1102 fata = purple_connection_get_protocol_data(gc);
1103 api = fb_data_get_api(fata);
1105 fb_data_save(fata);
1106 fb_api_disconnect(api);
1107 g_object_unref(fata);
1109 purple_connection_set_protocol_data(gc, NULL);
1110 purple_signals_disconnect_by_handle(gc);
1113 static GList *
1114 fb_status_types(PurpleAccount *acct)
1116 PurpleStatusType *type;
1117 GList *types = NULL;
1119 type = purple_status_type_new(PURPLE_STATUS_AVAILABLE,
1120 NULL, NULL, TRUE);
1121 types = g_list_prepend(types, type);
1123 /* Just a NULL state (as of now) for compatibility */
1124 type = purple_status_type_new(PURPLE_STATUS_AWAY,
1125 NULL, NULL, TRUE);
1126 types = g_list_prepend(types, type);
1128 type = purple_status_type_new(PURPLE_STATUS_INVISIBLE,
1129 NULL, NULL, TRUE);
1130 types = g_list_prepend(types, type);
1132 type = purple_status_type_new(PURPLE_STATUS_OFFLINE,
1133 NULL, NULL, TRUE);
1134 types = g_list_prepend(types, type);
1136 return g_list_reverse(types);
1139 static const char *
1140 fb_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
1142 return "facebook";
1145 static void
1146 fb_client_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *info,
1147 gboolean full)
1149 const gchar *name;
1150 PurplePresence *pres;
1151 PurpleStatus *status;
1153 pres = purple_buddy_get_presence(buddy);
1154 status = purple_presence_get_active_status(pres);
1156 if (!PURPLE_BUDDY_IS_ONLINE(buddy)) {
1157 /* Prevent doubles statues for Offline buddies */
1158 /* See: pidgin_get_tooltip_text() in gtkblist.c */
1159 purple_notify_user_info_remove_last_item(info);
1162 name = purple_status_get_name(status);
1163 purple_notify_user_info_add_pair_plaintext(info, _("Status"), name);
1166 static GList *
1167 fb_client_blist_node_menu(PurpleBlistNode *node)
1169 FbData *fata;
1170 GList *acts = NULL;
1171 PurpleAccount *acct;
1172 PurpleConnection *gc;
1173 PurpleActionMenu *act;
1175 if (!PURPLE_IS_BUDDY(node)) {
1176 return NULL;
1179 acct = purple_buddy_get_account(PURPLE_BUDDY(node));
1180 gc = purple_account_get_connection(acct);
1181 fata = purple_connection_get_protocol_data(gc);
1183 act = purple_action_menu_new(_("Initiate _Chat"),
1184 PURPLE_CALLBACK(fb_blist_chat_init),
1185 fata, NULL);
1186 acts = g_list_prepend(acts, act);
1188 return g_list_reverse(acts);
1191 static gboolean
1192 fb_client_offline_message(const PurpleBuddy *buddy)
1194 return TRUE;
1197 static void
1198 fb_server_set_status(PurpleAccount *acct, PurpleStatus *status)
1200 FbApi *api;
1201 FbData *fata;
1202 gboolean invis;
1203 PurpleConnection *gc;
1204 PurpleStatusPrimitive pstat;
1205 PurpleStatusType *type;
1207 gc = purple_account_get_connection(acct);
1208 fata = purple_connection_get_protocol_data(gc);
1209 api = fb_data_get_api(fata);
1211 type = purple_status_get_status_type(status);
1212 pstat = purple_status_type_get_primitive(type);
1213 invis = fb_api_is_invisible(api);
1215 if ((pstat == PURPLE_STATUS_INVISIBLE) && !invis) {
1216 fb_api_connect(api, TRUE);
1217 } else if ((pstat != PURPLE_STATUS_OFFLINE) && invis) {
1218 fb_api_connect(api, FALSE);
1222 static gint
1223 fb_im_send(PurpleConnection *gc, PurpleMessage *msg)
1225 const gchar *name;
1226 const gchar *text;
1227 FbApi *api;
1228 FbData *fata;
1229 FbId uid;
1230 gchar *sext;
1232 fata = purple_connection_get_protocol_data(gc);
1233 api = fb_data_get_api(fata);
1235 name = purple_message_get_recipient(msg);
1236 uid = FB_ID_FROM_STR(name);
1238 text = purple_message_get_contents(msg);
1239 sext = purple_markup_strip_html(text);
1240 fb_api_message(api, uid, FALSE, sext);
1241 g_free(sext);
1242 return 1;
1245 static guint
1246 fb_im_send_typing(PurpleConnection *gc, const gchar *name,
1247 PurpleIMTypingState state)
1249 FbApi *api;
1250 FbData *fata;
1251 FbId uid;
1253 fata = purple_connection_get_protocol_data(gc);
1254 api = fb_data_get_api(fata);
1255 uid = FB_ID_FROM_STR(name);
1257 fb_api_typing(api, uid, state != PURPLE_IM_NOT_TYPING);
1258 return 0;
1261 static GList *
1262 fb_chat_info()
1264 GList *pces = NULL;
1265 PurpleProtocolChatEntry *pce;
1267 pce = g_new0(PurpleProtocolChatEntry, 1);
1268 pce->label = _("Chat _Name:");
1269 pce->identifier = "name";
1270 pce->required = TRUE;
1271 pces = g_list_prepend(pces, pce);
1273 return g_list_reverse(pces);
1276 static GHashTable *
1277 fb_chat_info_defaults(PurpleConnection *gc, const gchar *name)
1279 GHashTable *data;
1281 data = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
1282 g_hash_table_insert(data, "name", g_strdup(name));
1284 return data;
1287 static void
1288 fb_chat_join(PurpleConnection *gc, GHashTable *data)
1290 const gchar *name;
1291 FbApi *api;
1292 FbData *fata;
1293 FbId tid;
1294 gint id;
1295 PurpleChatConversation *chat;
1296 PurpleRequestCommonParameters *cpar;
1298 name = g_hash_table_lookup(data, "name");
1299 g_return_if_fail(name != NULL);
1301 if (!FB_ID_IS_STR(name)) {
1302 cpar = purple_request_cpar_from_connection(gc);
1303 purple_notify_error(gc,
1304 _("Join a Chat"),
1305 _("Failed to Join Chat"),
1306 _("Invalid Facebook identifier."),
1307 cpar);
1308 return;
1311 tid = FB_ID_FROM_STR(name);
1312 id = fb_id_hash(&tid);
1313 chat = purple_conversations_find_chat(gc, id);
1315 if ((chat != NULL) && !purple_chat_conversation_has_left(chat)) {
1316 purple_conversation_present(PURPLE_CONVERSATION(chat));
1317 return;
1320 fata = purple_connection_get_protocol_data(gc);
1321 api = fb_data_get_api(fata);
1322 fb_api_thread(api, tid);
1325 static gchar *
1326 fb_chat_get_name(GHashTable *data)
1328 const gchar *name;
1330 name = g_hash_table_lookup(data, "name");
1331 g_return_val_if_fail(name != NULL, NULL);
1333 return g_strdup(name);
1336 static void
1337 fb_chat_invite(PurpleConnection *gc, gint id, const gchar *msg,
1338 const gchar *who)
1340 const gchar *name;
1341 FbApi *api;
1342 FbData *fata;
1343 FbId tid;
1344 FbId uid;
1345 PurpleChatConversation *chat;
1346 PurpleRequestCommonParameters *cpar;
1348 if (!FB_ID_IS_STR(who)) {
1349 cpar = purple_request_cpar_from_connection(gc);
1350 purple_notify_error(gc,
1351 _("Invite Buddy Into Chat Room"),
1352 _("Failed to Invite User"),
1353 _("Invalid Facebook identifier."),
1354 cpar);
1355 return;
1358 fata = purple_connection_get_protocol_data(gc);
1359 api = fb_data_get_api(fata);
1360 chat = purple_conversations_find_chat(gc, id);
1362 name = purple_conversation_get_name(PURPLE_CONVERSATION(chat));
1363 tid = FB_ID_FROM_STR(name);
1364 uid = FB_ID_FROM_STR(who);
1366 fb_api_thread_invite(api, tid, uid);
1369 static gint
1370 fb_chat_send(PurpleConnection *gc, gint id, PurpleMessage *msg)
1372 const gchar *name;
1373 const gchar *text;
1374 FbApi *api;
1375 FbData *fata;
1376 FbId tid;
1377 gchar *sext;
1378 PurpleAccount *acct;
1379 PurpleChatConversation *chat;
1381 acct = purple_connection_get_account(gc);
1382 fata = purple_connection_get_protocol_data(gc);
1383 api = fb_data_get_api(fata);
1384 chat = purple_conversations_find_chat(gc, id);
1386 name = purple_conversation_get_name(PURPLE_CONVERSATION(chat));
1387 tid = FB_ID_FROM_STR(name);
1389 text = purple_message_get_contents(msg);
1390 sext = purple_markup_strip_html(text);
1391 fb_api_message(api, tid, TRUE, sext);
1392 g_free(sext);
1394 name = purple_account_get_username(acct);
1395 purple_serv_got_chat_in(gc, id, name,
1396 purple_message_get_flags(msg),
1397 purple_message_get_contents(msg),
1398 time(NULL));
1399 return 0;
1402 static void
1403 fb_chat_set_topic(PurpleConnection *gc, gint id, const gchar *topic)
1405 const gchar *name;
1406 FbApi *api;
1407 FbData *fata;
1408 FbId tid;
1409 PurpleChatConversation *chat;
1411 fata = purple_connection_get_protocol_data(gc);
1412 api = fb_data_get_api(fata);
1413 chat = purple_conversations_find_chat(gc, id);
1415 name = purple_conversation_get_name(PURPLE_CONVERSATION(chat));
1416 tid = FB_ID_FROM_STR(name);
1417 fb_api_thread_topic(api, tid, topic);
1420 static PurpleRoomlist *
1421 fb_roomlist_get_list(PurpleConnection *gc)
1423 FbApi *api;
1424 FbData *fata;
1425 GList *flds = NULL;
1426 PurpleAccount *acct;
1427 PurpleRoomlist *list;
1428 PurpleRoomlistField *fld;
1430 fata = purple_connection_get_protocol_data(gc);
1431 list = fb_data_get_roomlist(fata);
1432 g_return_val_if_fail(list == NULL, NULL);
1434 api = fb_data_get_api(fata);
1435 acct = purple_connection_get_account(gc);
1436 list = purple_roomlist_new(acct);
1437 fb_data_set_roomlist(fata, list);
1439 fld = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING,
1440 _("Topic"), "topic", FALSE);
1441 flds = g_list_prepend(flds, fld);
1443 fld = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING,
1444 _("Users"), "users", FALSE);
1445 flds = g_list_prepend(flds, fld);
1447 flds = g_list_reverse(flds);
1448 purple_roomlist_set_fields(list, flds);
1450 purple_roomlist_set_in_progress(list, TRUE);
1451 fb_api_threads(api);
1452 return list;
1455 static void
1456 fb_roomlist_cancel(PurpleRoomlist *list)
1458 FbData *fata;
1459 PurpleAccount *acct;
1460 PurpleConnection *gc;
1461 PurpleRoomlist *cist;
1463 acct = purple_roomlist_get_account(list);
1464 gc = purple_account_get_connection(acct);
1465 fata = purple_connection_get_protocol_data(gc);
1466 cist = fb_data_get_roomlist(fata);
1468 if (G_LIKELY(cist == list)) {
1469 fb_data_set_roomlist(fata, NULL);
1472 purple_roomlist_set_in_progress(list, FALSE);
1473 g_object_unref(list);
1476 static PurpleCmdRet
1477 fb_cmd_kick(PurpleConversation *conv, const gchar *cmd, gchar **args,
1478 gchar **error, gpointer data)
1480 const gchar *name;
1481 FbApi *api;
1482 FbData *fata;
1483 FbId tid;
1484 FbId uid;
1485 GError *err = NULL;
1486 PurpleAccount *acct;
1487 PurpleBuddy *bdy;
1488 PurpleConnection *gc;
1489 PurpleChatConversation *chat;
1491 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(conv),
1492 PURPLE_CMD_RET_FAILED);
1494 gc = purple_conversation_get_connection(conv);
1495 acct = purple_connection_get_account(gc);
1496 chat = PURPLE_CHAT_CONVERSATION(conv);
1497 bdy = fb_util_account_find_buddy(acct, chat, args[0], &err);
1499 if (err != NULL) {
1500 *error = g_strdup_printf(_("%s."), err->message);
1501 g_error_free(err);
1502 return PURPLE_CMD_RET_FAILED;
1505 fata = purple_connection_get_protocol_data(gc);
1506 api = fb_data_get_api(fata);
1508 name = purple_conversation_get_name(conv);
1509 tid = FB_ID_FROM_STR(name);
1511 name = purple_buddy_get_name(bdy);
1512 uid = FB_ID_FROM_STR(name);
1514 fb_api_thread_remove(api, tid, uid);
1515 return PURPLE_CMD_RET_OK;
1518 static PurpleCmdRet
1519 fb_cmd_leave(PurpleConversation *conv, const gchar *cmd, gchar **args,
1520 gchar **error, gpointer data)
1522 const gchar *name;
1523 FbApi *api;
1524 FbData *fata;
1525 FbId tid;
1526 gint id;
1527 PurpleConnection *gc;
1528 PurpleChatConversation *chat;
1530 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(conv),
1531 PURPLE_CMD_RET_FAILED);
1533 gc = purple_conversation_get_connection(conv);
1534 fata = purple_connection_get_protocol_data(gc);
1535 api = fb_data_get_api(fata);
1537 chat = PURPLE_CHAT_CONVERSATION(conv);
1538 id = purple_chat_conversation_get_id(chat);
1540 name = purple_conversation_get_name(conv);
1541 tid = FB_ID_FROM_STR(name);
1543 purple_serv_got_chat_left(gc, id);
1544 fb_api_thread_remove(api, tid, 0);
1545 return PURPLE_CMD_RET_OK;
1548 static void
1549 facebook_protocol_init(FacebookProtocol *self)
1551 PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
1552 GList *opts = NULL;
1553 PurpleAccountOption *opt;
1555 protocol->id = FB_PROTOCOL_ID;
1556 protocol->name = "Facebook";
1557 protocol->options = OPT_PROTO_CHAT_TOPIC;
1559 opt = purple_account_option_int_new(_("Buddy list sync interval"),
1560 "sync-interval", 5);
1561 opts = g_list_prepend(opts, opt);
1563 opt = purple_account_option_bool_new(_("Mark messages as read on focus"),
1564 "mark-read", TRUE);
1565 opts = g_list_prepend(opts, opt);
1567 opt = purple_account_option_bool_new(_("Mark messages as read only when available"),
1568 "mark-read-available", FALSE);
1569 opts = g_list_prepend(opts, opt);
1571 opt = purple_account_option_bool_new(_("Show self messages"),
1572 "show-self", TRUE);
1573 opts = g_list_prepend(opts, opt);
1575 opt = purple_account_option_bool_new(_("Show unread messages"),
1576 "show-unread", TRUE);
1577 opts = g_list_prepend(opts, opt);
1579 opt = purple_account_option_bool_new(_("Open new group chats with "
1580 "incoming messages"),
1581 "group-chat-open", TRUE);
1582 opts = g_list_prepend(opts, opt);
1583 protocol->account_options = g_list_reverse(opts);
1586 static void
1587 facebook_protocol_class_init(FacebookProtocolClass *klass)
1589 PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
1591 protocol_class->login = fb_login;
1592 protocol_class->close = fb_close;
1593 protocol_class->status_types = fb_status_types;
1594 protocol_class->list_icon = fb_list_icon;
1597 static void
1598 facebook_protocol_class_finalize(G_GNUC_UNUSED FacebookProtocolClass *klass)
1602 static void
1603 facebook_protocol_client_iface_init(PurpleProtocolClientInterface *iface)
1605 iface->tooltip_text = fb_client_tooltip_text;
1606 iface->blist_node_menu = fb_client_blist_node_menu;
1607 iface->offline_message = fb_client_offline_message;
1610 static void
1611 facebook_protocol_server_iface_init(PurpleProtocolServerInterface *iface)
1613 iface->set_status = fb_server_set_status;
1616 static void
1617 facebook_protocol_im_iface_init(PurpleProtocolIMInterface *iface)
1619 iface->send = fb_im_send;
1620 iface->send_typing = fb_im_send_typing;
1623 static void
1624 facebook_protocol_chat_iface_init(PurpleProtocolChatInterface *iface)
1626 iface->info = fb_chat_info;
1627 iface->info_defaults = fb_chat_info_defaults;
1628 iface->join = fb_chat_join;
1629 iface->get_name = fb_chat_get_name;
1630 iface->invite = fb_chat_invite;
1631 iface->send = fb_chat_send;
1632 iface->set_topic = fb_chat_set_topic;
1635 static void
1636 facebook_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface *iface)
1638 iface->get_list = fb_roomlist_get_list;
1639 iface->cancel = fb_roomlist_cancel;
1642 G_DEFINE_DYNAMIC_TYPE_EXTENDED(
1643 FacebookProtocol, facebook_protocol, PURPLE_TYPE_PROTOCOL, 0,
1645 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
1646 facebook_protocol_client_iface_init)
1647 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
1648 facebook_protocol_server_iface_init)
1649 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
1650 facebook_protocol_im_iface_init)
1651 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
1652 facebook_protocol_chat_iface_init)
1653 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST,
1654 facebook_protocol_roomlist_iface_init));
1656 static void
1657 fb_cmds_register(void)
1659 PurpleCmdId id;
1661 static PurpleCmdFlag cflags =
1662 PURPLE_CMD_FLAG_CHAT |
1663 PURPLE_CMD_FLAG_PROTOCOL_ONLY;
1665 g_return_if_fail(fb_cmds == NULL);
1667 id = purple_cmd_register("kick", "s", PURPLE_CMD_P_PROTOCOL, cflags,
1668 fb_protocol->id, fb_cmd_kick,
1669 _("kick: Kick someone from the chat"),
1670 NULL);
1671 fb_cmds = g_slist_prepend(fb_cmds, GUINT_TO_POINTER(id));
1673 id = purple_cmd_register("leave", "", PURPLE_CMD_P_PROTOCOL, cflags,
1674 fb_protocol->id, fb_cmd_leave,
1675 _("leave: Leave the chat"),
1676 NULL);
1677 fb_cmds = g_slist_prepend(fb_cmds, GUINT_TO_POINTER(id));
1680 static void
1681 fb_cmds_unregister_free(gpointer data)
1683 PurpleCmdId id = GPOINTER_TO_UINT(data);
1684 purple_cmd_unregister(id);
1687 static void
1688 fb_cmds_unregister(void)
1690 g_slist_free_full(fb_cmds, fb_cmds_unregister_free);
1693 static PurplePluginInfo *
1694 plugin_query(GError **error)
1696 return purple_plugin_info_new(
1697 "id", FB_PROTOCOL_ID,
1698 "name", "Facebook Protocol",
1699 "version", DISPLAY_VERSION,
1700 "category", N_("Protocol"),
1701 "summary", N_("Facebook Protocol Plugin"),
1702 "description", N_("Facebook Protocol Plugin"),
1703 "website", PURPLE_WEBSITE,
1704 "abi-version", PURPLE_ABI_VERSION,
1705 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
1706 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
1707 NULL
1711 static gboolean
1712 plugin_load(PurplePlugin *plugin, GError **error)
1714 facebook_protocol_register_type(G_TYPE_MODULE(plugin));
1715 fb_protocol = purple_protocols_add(FACEBOOK_TYPE_PROTOCOL, error);
1717 if (fb_protocol == NULL) {
1718 return FALSE;
1721 fb_cmds_register();
1722 return TRUE;
1725 static gboolean
1726 plugin_unload(PurplePlugin *plugin, GError **error)
1728 fb_cmds_unregister();
1729 return purple_protocols_remove(fb_protocol, error);
1732 PURPLE_PLUGIN_INIT(facebook, plugin_query, plugin_load, plugin_unload);