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
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
26 #include "blistnode.h"
28 #include "buddyicon.h"
29 #include "buddylist.h"
31 #include "connection.h"
32 #include "conversation.h"
33 #include "conversations.h"
34 #include "conversationtypes.h"
35 #include "glibcompat.h"
37 #include "image-store.h"
43 #include "protocols.h"
44 #include "purpleaccountoption.h"
60 static GSList
*fb_cmds
= NULL
;
61 static PurpleProtocol
*fb_protocol
= NULL
;
64 fb_cb_api_messages(FbApi
*api
, GSList
*msgs
, gpointer data
);
67 fb_get_group(gboolean
friend)
70 PurpleBlistNode
*node
;
75 title
= _("Facebook Friends");
77 title
= _("Facebook Non-Friends");
80 grp
= purple_blist_find_group(title
);
82 if (G_UNLIKELY(grp
== NULL
)) {
83 grp
= purple_group_new(title
);
86 for (n
= purple_blist_get_default_root(); n
!= NULL
;
91 /* Append to the end of the buddy list */
92 purple_blist_add_group(grp
, node
);
95 node
= PURPLE_BLIST_NODE(grp
);
96 purple_blist_node_set_bool(node
, "collapsed", TRUE
);
104 fb_buddy_add_nonfriend(PurpleAccount
*acct
, FbApiUser
*user
)
106 gchar uid
[FB_ID_STRMAX
];
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
);
119 fb_cb_api_auth(FbApi
*api
, gpointer data
)
122 PurpleConnection
*gc
;
124 gc
= fb_data_get_connection(fata
);
126 purple_connection_update_progress(gc
, _("Fetching contacts"), 2, 4);
128 fb_api_contacts(api
);
132 fb_cb_api_connect(FbApi
*api
, gpointer data
)
136 PurpleConnection
*gc
;
138 gc
= fb_data_get_connection(fata
);
139 acct
= purple_connection_get_account(gc
);
142 purple_connection_set_state(gc
, PURPLE_CONNECTION_CONNECTED
);
144 if (purple_account_get_bool(acct
, "show-unread", TRUE
)) {
150 fb_cb_api_contact(FbApi
*api
, FbApiUser
*user
, gpointer data
)
153 gchar uid
[FB_ID_STRMAX
];
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
);
169 fb_cb_api_messages(api
, msgs
, fata
);
170 g_slist_free_full(msgs
, (GDestroyNotify
) fb_api_message_free
);
175 fb_cb_sync_contacts(gpointer data
)
180 api
= fb_data_get_api(fata
);
181 fb_data_clear_timeout(fata
, "sync-contacts", FALSE
);
182 fb_api_contacts(api
);
187 fb_cb_icon(FbDataImage
*img
, GError
*error
)
192 FbHttpParams
*params
;
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
);
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
);
218 fb_sync_contacts_add_timeout(FbData
*fata
)
221 PurpleConnection
*gc
;
224 gc
= fb_data_get_connection(fata
);
225 acct
= purple_connection_get_account(gc
);
227 sync
= purple_account_get_int(acct
, "sync-interval", 5);
230 purple_account_set_int(acct
, "sync-interval", 1);
235 fb_data_add_timeout(fata
, "sync-contacts", sync
, fb_cb_sync_contacts
,
240 fb_cb_api_contacts(FbApi
*api
, GSList
*users
, gboolean complete
, gpointer data
)
247 gchar uid
[FB_ID_STRMAX
];
249 GValue val
= G_VALUE_INIT
;
252 PurpleConnection
*gc
;
253 PurpleConnectionState state
;
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
);
272 for (l
= users
; l
!= NULL
; l
= l
->next
) {
274 FB_ID_TO_STR(user
->uid
, uid
);
276 if (G_UNLIKELY(user
->uid
== muid
)) {
277 if (G_UNLIKELY(alias
!= NULL
)) {
281 purple_account_set_private_alias(acct
, user
->name
);
285 bdy
= purple_blist_find_buddy(acct
, uid
);
287 if ((bdy
!= NULL
) && (purple_buddy_get_group(bdy
) == grpn
)) {
288 purple_blist_remove_buddy(bdy
);
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
,
306 fb_data_image_queue(fata
);
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
);
325 fb_cb_api_contacts_delta(FbApi
*api
, GSList
*added
, GSList
*removed
, gpointer data
)
329 gchar uid
[FB_ID_STRMAX
];
333 PurpleConnection
*gc
;
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
) {
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
);
362 purple_blist_remove_buddy(bdy
);
366 fb_sync_contacts_add_timeout(fata
);
370 fb_cb_api_error(FbApi
*api
, GError
*error
, gpointer 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
);
383 if (g_error_matches(error
, FB_API_ERROR
, FB_API_ERROR_QUEUE
)) {
384 /* Save the reset data */
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
;
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
);
406 fb_cb_api_events(FbApi
*api
, GSList
*events
, gpointer data
)
410 gchar uid
[FB_ID_STRMAX
];
411 gchar tid
[FB_ID_STRMAX
];
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
) {
426 FB_ID_TO_STR(event
->tid
, tid
);
427 chat
= purple_conversations_find_chat_with_account(tid
, acct
);
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
,
441 case FB_API_EVENT_TYPE_THREAD_USER_ADDED
:
442 if (purple_blist_find_buddy(acct
, uid
) == NULL
) {
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
);
452 g_hash_table_insert(fetch
, &event
->tid
, event
);
457 purple_chat_conversation_add_user(chat
, uid
, NULL
, 0,
461 case FB_API_EVENT_TYPE_THREAD_USER_REMOVED
:
462 purple_chat_conversation_remove_user(chat
, uid
, event
->text
);
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
);
477 fb_cb_image(FbDataImage
*img
, GError
*error
)
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
);
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
);
505 msg
->text
= g_strdup_printf("<img src=\""
506 PURPLE_IMAGE_STORE_PROTOCOL
508 msg
->flags
|= FB_API_MESSAGE_FLAG_DONE
;
510 msgs
= g_slist_prepend(msgs
, msg
);
511 fb_cb_api_messages(api
, msgs
, fata
);
516 fb_cb_api_messages(FbApi
*api
, GSList
*msgs
, gpointer data
)
526 gchar tid
[FB_ID_STRMAX
];
527 gchar uid
[FB_ID_STRMAX
];
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
) {
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
);
553 isself
= (msg
->flags
& FB_API_MESSAGE_FLAG_SELF
) != 0;
555 if (isself
&& !self
) {
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
);
572 flags
|= PURPLE_MESSAGE_IMAGES
;
576 html
= purple_markup_escape_text(msg
->text
, -1);
581 if (mark
&& !isself
) {
582 fb_data_set_unread(fata
, msg
->uid
, TRUE
);
585 fb_util_serv_got_im(gc
, uid
, text
, flags
, tstamp
);
590 FB_ID_TO_STR(msg
->tid
, tid
);
591 chat
= purple_conversations_find_chat_with_account(tid
, acct
);
599 id
= fb_id_hash(&msg
->tid
);
600 purple_serv_got_joined_chat(gc
, id
, tid
);
601 fb_api_thread(api
, msg
->tid
);
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
);
616 fb_cb_api_presences(FbApi
*api
, GSList
*press
, gpointer data
)
621 gchar uid
[FB_ID_STRMAX
];
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
) {
634 pstat
= PURPLE_STATUS_AVAILABLE
;
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
);
646 fb_cb_api_thread(FbApi
*api
, FbApiThread
*thrd
, gpointer data
)
652 gchar tid
[FB_ID_STRMAX
];
653 gchar uid
[FB_ID_STRMAX
];
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
);
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;
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
) {
688 FB_ID_TO_STR(user
->uid
, uid
);
690 if (purple_chat_conversation_has_user(chat
, uid
)) {
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
);
703 fb_cb_api_thread_create(FbApi
*api
, FbId tid
, gpointer data
)
706 gchar sid
[FB_ID_STRMAX
];
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
);
720 fb_cb_api_thread_kicked(FbApi
*api
, FbApiThread
*thrd
, gpointer data
)
723 gchar tid
[FB_ID_STRMAX
];
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
);
735 PurpleRequestCommonParameters
*cpar
;
737 cpar
= purple_request_cpar_from_connection(gc
);
738 purple_notify_error(gc
,
740 _("Failed to Join Chat"),
741 _("You have been removed from this chat"),
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
));
753 fb_cb_api_threads(FbApi
*api
, GSList
*thrds
, gpointer data
)
758 gchar tid
[FB_ID_STRMAX
];
759 gchar uid
[FB_ID_STRMAX
];
766 PurpleConnection
*gc
;
767 PurpleRoomlist
*list
;
768 PurpleRoomlistRoom
*room
;
770 list
= fb_data_get_roomlist(fata
);
772 if (G_UNLIKELY(list
== NULL
)) {
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
) {
782 FB_ID_TO_STR(thrd
->tid
, tid
);
783 g_string_truncate(gstr
, 0);
785 for (m
= thrd
->users
; m
!= NULL
; m
= m
->next
) {
787 FB_ID_TO_STR(user
->uid
, uid
);
788 bdy
= purple_blist_find_buddy(acct
, uid
);
791 alias
= purple_buddy_get_alias(bdy
);
797 g_string_append(gstr
, ", ");
800 g_string_append(gstr
, alias
);
803 room
= purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM
,
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
);
816 fb_cb_api_typing(FbApi
*api
, FbApiTyping
*typg
, gpointer 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
);
826 purple_serv_got_typing(gc
, uid
, 0, PURPLE_IM_TYPING
);
828 purple_serv_got_typing_stopped(gc
, uid
);
833 fb_mark_read(FbData
*fata
, FbId id
, gboolean thread
)
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
)))
850 fb_data_set_unread(fata
, id
, FALSE
);
851 fb_api_read(api
, id
, thread
);
855 fb_cb_conv_read(gpointer data
)
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
);
873 if (purple_conversation_has_focus(conv
)) {
874 fb_mark_read(fata
, id
, PURPLE_IS_CHAT_CONVERSATION(conv
));
880 fb_cb_conv_updated(PurpleConversation
*conv
, PurpleConversationUpdateType type
,
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
);
905 fb_cb_conv_deleting(PurpleConversation
*conv
, gpointer data
)
913 acct
= purple_conversation_get_account(conv
);
914 pid
= purple_account_get_protocol_id(acct
);
916 if (!purple_strequal(pid
, FB_PROTOCOL_ID
)) {
920 name
= purple_conversation_get_name(conv
);
921 tname
= g_strconcat("conv-read-", name
, NULL
);
922 fb_data_clear_timeout(fata
, tname
, TRUE
);
927 fb_blist_chat_create(GSList
*buddies
, gpointer data
)
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
,
946 _("Failed to Initiate Chat"),
947 _("At least two initial chat participants"
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
);
965 fb_blist_chat_init(PurpleBlistNode
*node
, gpointer data
)
968 GSList
*select
= NULL
;
969 PurpleConnection
*gc
;
971 if (!PURPLE_IS_BUDDY(node
)) {
975 gc
= fb_data_get_connection(fata
);
976 select
= g_slist_prepend(select
, PURPLE_BUDDY(node
));
978 fb_util_request_buddy(gc
,
980 _("Initial Chat Participants"),
981 _("Select at least two initial participants."),
983 G_CALLBACK(fb_blist_chat_create
), NULL
,
985 g_slist_free(select
);
989 fb_login(PurpleAccount
*acct
)
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",
1006 purple_connection_g_error(gc
, error
);
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
,
1017 G_CALLBACK(fb_cb_api_auth
),
1019 g_signal_connect(api
,
1021 G_CALLBACK(fb_cb_api_connect
),
1023 g_signal_connect(api
,
1025 G_CALLBACK(fb_cb_api_contact
),
1027 g_signal_connect(api
,
1029 G_CALLBACK(fb_cb_api_contacts
),
1031 g_signal_connect(api
,
1033 G_CALLBACK(fb_cb_api_contacts_delta
),
1035 g_signal_connect(api
,
1037 G_CALLBACK(fb_cb_api_error
),
1039 g_signal_connect(api
,
1041 G_CALLBACK(fb_cb_api_events
),
1043 g_signal_connect(api
,
1045 G_CALLBACK(fb_cb_api_messages
),
1047 g_signal_connect(api
,
1049 G_CALLBACK(fb_cb_api_presences
),
1051 g_signal_connect(api
,
1053 G_CALLBACK(fb_cb_api_thread
),
1055 g_signal_connect(api
,
1057 G_CALLBACK(fb_cb_api_thread_create
),
1059 g_signal_connect(api
,
1061 G_CALLBACK(fb_cb_api_thread_kicked
),
1063 g_signal_connect(api
,
1065 G_CALLBACK(fb_cb_api_threads
),
1067 g_signal_connect(api
,
1069 G_CALLBACK(fb_cb_api_typing
),
1072 purple_signal_connect(convh
,
1073 "conversation-updated",
1075 G_CALLBACK(fb_cb_conv_updated
),
1077 purple_signal_connect(convh
,
1078 "deleting-conversation",
1080 G_CALLBACK(fb_cb_conv_deleting
),
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"),
1088 fb_api_auth(api
, user
, pass
);
1092 purple_connection_update_progress(gc
, _("Fetching contacts"), 2, 4);
1093 fb_api_contacts(api
);
1097 fb_close(PurpleConnection
*gc
)
1102 fata
= purple_connection_get_protocol_data(gc
);
1103 api
= fb_data_get_api(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
);
1114 fb_status_types(PurpleAccount
*acct
)
1116 PurpleStatusType
*type
;
1117 GList
*types
= NULL
;
1119 type
= purple_status_type_new(PURPLE_STATUS_AVAILABLE
,
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
,
1126 types
= g_list_prepend(types
, type
);
1128 type
= purple_status_type_new(PURPLE_STATUS_INVISIBLE
,
1130 types
= g_list_prepend(types
, type
);
1132 type
= purple_status_type_new(PURPLE_STATUS_OFFLINE
,
1134 types
= g_list_prepend(types
, type
);
1136 return g_list_reverse(types
);
1140 fb_list_icon(PurpleAccount
*account
, PurpleBuddy
*buddy
)
1146 fb_client_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*info
,
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
);
1167 fb_client_blist_node_menu(PurpleBlistNode
*node
)
1171 PurpleAccount
*acct
;
1172 PurpleConnection
*gc
;
1173 PurpleActionMenu
*act
;
1175 if (!PURPLE_IS_BUDDY(node
)) {
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
),
1186 acts
= g_list_prepend(acts
, act
);
1188 return g_list_reverse(acts
);
1192 fb_client_offline_message(const PurpleBuddy
*buddy
)
1198 fb_server_set_status(PurpleAccount
*acct
, PurpleStatus
*status
)
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
);
1223 fb_im_send(PurpleConnection
*gc
, PurpleMessage
*msg
)
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
);
1246 fb_im_send_typing(PurpleConnection
*gc
, const gchar
*name
,
1247 PurpleIMTypingState state
)
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
);
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
);
1277 fb_chat_info_defaults(PurpleConnection
*gc
, const gchar
*name
)
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
));
1288 fb_chat_join(PurpleConnection
*gc
, GHashTable
*data
)
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
,
1305 _("Failed to Join Chat"),
1306 _("Invalid Facebook identifier."),
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
));
1320 fata
= purple_connection_get_protocol_data(gc
);
1321 api
= fb_data_get_api(fata
);
1322 fb_api_thread(api
, tid
);
1326 fb_chat_get_name(GHashTable
*data
)
1330 name
= g_hash_table_lookup(data
, "name");
1331 g_return_val_if_fail(name
!= NULL
, NULL
);
1333 return g_strdup(name
);
1337 fb_chat_invite(PurpleConnection
*gc
, gint id
, const gchar
*msg
,
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."),
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
);
1370 fb_chat_send(PurpleConnection
*gc
, gint id
, PurpleMessage
*msg
)
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
);
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
),
1403 fb_chat_set_topic(PurpleConnection
*gc
, gint id
, const gchar
*topic
)
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
)
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
);
1456 fb_roomlist_cancel(PurpleRoomlist
*list
)
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
);
1477 fb_cmd_kick(PurpleConversation
*conv
, const gchar
*cmd
, gchar
**args
,
1478 gchar
**error
, gpointer data
)
1486 PurpleAccount
*acct
;
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
);
1500 *error
= g_strdup_printf(_("%s."), err
->message
);
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
;
1519 fb_cmd_leave(PurpleConversation
*conv
, const gchar
*cmd
, gchar
**args
,
1520 gchar
**error
, gpointer data
)
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
;
1549 facebook_protocol_init(FacebookProtocol
*self
)
1551 PurpleProtocol
*protocol
= PURPLE_PROTOCOL(self
);
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"),
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"),
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
);
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
;
1598 facebook_protocol_class_finalize(G_GNUC_UNUSED FacebookProtocolClass
*klass
)
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
;
1611 facebook_protocol_server_iface_init(PurpleProtocolServerInterface
*iface
)
1613 iface
->set_status
= fb_server_set_status
;
1617 facebook_protocol_im_iface_init(PurpleProtocolIMInterface
*iface
)
1619 iface
->send
= fb_im_send
;
1620 iface
->send_typing
= fb_im_send_typing
;
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
;
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
));
1657 fb_cmds_register(void)
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"),
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"),
1677 fb_cmds
= g_slist_prepend(fb_cmds
, GUINT_TO_POINTER(id
));
1681 fb_cmds_unregister_free(gpointer data
)
1683 PurpleCmdId id
= GPOINTER_TO_UINT(data
);
1684 purple_cmd_unregister(id
);
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
,
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
) {
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
);