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
25 #include "accountopt.h"
27 #include "blistnode.h"
29 #include "buddyicon.h"
30 #include "buddylist.h"
32 #include "connection.h"
33 #include "conversation.h"
34 #include "conversations.h"
35 #include "conversationtypes.h"
36 #include "glibcompat.h"
38 #include "image-store.h"
44 #include "protocols.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
;
998 gc
= purple_account_get_connection(acct
);
1000 fata
= fb_data_new(gc
);
1001 api
= fb_data_get_api(fata
);
1002 convh
= purple_conversations_get_handle();
1003 purple_connection_set_protocol_data(gc
, fata
);
1005 g_signal_connect(api
,
1007 G_CALLBACK(fb_cb_api_auth
),
1009 g_signal_connect(api
,
1011 G_CALLBACK(fb_cb_api_connect
),
1013 g_signal_connect(api
,
1015 G_CALLBACK(fb_cb_api_contact
),
1017 g_signal_connect(api
,
1019 G_CALLBACK(fb_cb_api_contacts
),
1021 g_signal_connect(api
,
1023 G_CALLBACK(fb_cb_api_contacts_delta
),
1025 g_signal_connect(api
,
1027 G_CALLBACK(fb_cb_api_error
),
1029 g_signal_connect(api
,
1031 G_CALLBACK(fb_cb_api_events
),
1033 g_signal_connect(api
,
1035 G_CALLBACK(fb_cb_api_messages
),
1037 g_signal_connect(api
,
1039 G_CALLBACK(fb_cb_api_presences
),
1041 g_signal_connect(api
,
1043 G_CALLBACK(fb_cb_api_thread
),
1045 g_signal_connect(api
,
1047 G_CALLBACK(fb_cb_api_thread_create
),
1049 g_signal_connect(api
,
1051 G_CALLBACK(fb_cb_api_thread_kicked
),
1053 g_signal_connect(api
,
1055 G_CALLBACK(fb_cb_api_threads
),
1057 g_signal_connect(api
,
1059 G_CALLBACK(fb_cb_api_typing
),
1062 purple_signal_connect(convh
,
1063 "conversation-updated",
1065 G_CALLBACK(fb_cb_conv_updated
),
1067 purple_signal_connect(convh
,
1068 "deleting-conversation",
1070 G_CALLBACK(fb_cb_conv_deleting
),
1073 if (!fb_data_load(fata
) || !purple_account_get_remember_password(acct
)) {
1074 user
= purple_account_get_username(acct
);
1075 pass
= purple_connection_get_password(gc
);
1076 purple_connection_update_progress(gc
, _("Authenticating"),
1078 fb_api_auth(api
, user
, pass
);
1082 purple_connection_update_progress(gc
, _("Fetching contacts"), 2, 4);
1083 fb_api_contacts(api
);
1087 fb_close(PurpleConnection
*gc
)
1092 fata
= purple_connection_get_protocol_data(gc
);
1093 api
= fb_data_get_api(fata
);
1096 fb_api_disconnect(api
);
1097 g_object_unref(fata
);
1099 purple_connection_set_protocol_data(gc
, NULL
);
1100 purple_signals_disconnect_by_handle(gc
);
1104 fb_status_types(PurpleAccount
*acct
)
1106 PurpleStatusType
*type
;
1107 GList
*types
= NULL
;
1109 type
= purple_status_type_new(PURPLE_STATUS_AVAILABLE
,
1111 types
= g_list_prepend(types
, type
);
1113 /* Just a NULL state (as of now) for compatibility */
1114 type
= purple_status_type_new(PURPLE_STATUS_AWAY
,
1116 types
= g_list_prepend(types
, type
);
1118 type
= purple_status_type_new(PURPLE_STATUS_INVISIBLE
,
1120 types
= g_list_prepend(types
, type
);
1122 type
= purple_status_type_new(PURPLE_STATUS_OFFLINE
,
1124 types
= g_list_prepend(types
, type
);
1126 return g_list_reverse(types
);
1130 fb_list_icon(PurpleAccount
*account
, PurpleBuddy
*buddy
)
1136 fb_client_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*info
,
1140 PurplePresence
*pres
;
1141 PurpleStatus
*status
;
1143 pres
= purple_buddy_get_presence(buddy
);
1144 status
= purple_presence_get_active_status(pres
);
1146 if (!PURPLE_BUDDY_IS_ONLINE(buddy
)) {
1147 /* Prevent doubles statues for Offline buddies */
1148 /* See: pidgin_get_tooltip_text() in gtkblist.c */
1149 purple_notify_user_info_remove_last_item(info
);
1152 name
= purple_status_get_name(status
);
1153 purple_notify_user_info_add_pair_plaintext(info
, _("Status"), name
);
1157 fb_client_blist_node_menu(PurpleBlistNode
*node
)
1161 PurpleAccount
*acct
;
1162 PurpleConnection
*gc
;
1163 PurpleActionMenu
*act
;
1165 if (!PURPLE_IS_BUDDY(node
)) {
1169 acct
= purple_buddy_get_account(PURPLE_BUDDY(node
));
1170 gc
= purple_account_get_connection(acct
);
1171 fata
= purple_connection_get_protocol_data(gc
);
1173 act
= purple_action_menu_new(_("Initiate _Chat"),
1174 PURPLE_CALLBACK(fb_blist_chat_init
),
1176 acts
= g_list_prepend(acts
, act
);
1178 return g_list_reverse(acts
);
1182 fb_client_offline_message(const PurpleBuddy
*buddy
)
1188 fb_server_set_status(PurpleAccount
*acct
, PurpleStatus
*status
)
1193 PurpleConnection
*gc
;
1194 PurpleStatusPrimitive pstat
;
1195 PurpleStatusType
*type
;
1197 gc
= purple_account_get_connection(acct
);
1198 fata
= purple_connection_get_protocol_data(gc
);
1199 api
= fb_data_get_api(fata
);
1201 type
= purple_status_get_status_type(status
);
1202 pstat
= purple_status_type_get_primitive(type
);
1203 invis
= fb_api_is_invisible(api
);
1205 if ((pstat
== PURPLE_STATUS_INVISIBLE
) && !invis
) {
1206 fb_api_connect(api
, TRUE
);
1207 } else if ((pstat
!= PURPLE_STATUS_OFFLINE
) && invis
) {
1208 fb_api_connect(api
, FALSE
);
1213 fb_im_send(PurpleConnection
*gc
, PurpleMessage
*msg
)
1222 fata
= purple_connection_get_protocol_data(gc
);
1223 api
= fb_data_get_api(fata
);
1225 name
= purple_message_get_recipient(msg
);
1226 uid
= FB_ID_FROM_STR(name
);
1228 text
= purple_message_get_contents(msg
);
1229 sext
= purple_markup_strip_html(text
);
1230 fb_api_message(api
, uid
, FALSE
, sext
);
1236 fb_im_send_typing(PurpleConnection
*gc
, const gchar
*name
,
1237 PurpleIMTypingState state
)
1243 fata
= purple_connection_get_protocol_data(gc
);
1244 api
= fb_data_get_api(fata
);
1245 uid
= FB_ID_FROM_STR(name
);
1247 fb_api_typing(api
, uid
, state
!= PURPLE_IM_NOT_TYPING
);
1255 PurpleProtocolChatEntry
*pce
;
1257 pce
= g_new0(PurpleProtocolChatEntry
, 1);
1258 pce
->label
= _("Chat _Name:");
1259 pce
->identifier
= "name";
1260 pce
->required
= TRUE
;
1261 pces
= g_list_prepend(pces
, pce
);
1263 return g_list_reverse(pces
);
1267 fb_chat_info_defaults(PurpleConnection
*gc
, const gchar
*name
)
1271 data
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, g_free
);
1272 g_hash_table_insert(data
, "name", g_strdup(name
));
1278 fb_chat_join(PurpleConnection
*gc
, GHashTable
*data
)
1285 PurpleChatConversation
*chat
;
1286 PurpleRequestCommonParameters
*cpar
;
1288 name
= g_hash_table_lookup(data
, "name");
1289 g_return_if_fail(name
!= NULL
);
1291 if (!FB_ID_IS_STR(name
)) {
1292 cpar
= purple_request_cpar_from_connection(gc
);
1293 purple_notify_error(gc
,
1295 _("Failed to Join Chat"),
1296 _("Invalid Facebook identifier."),
1301 tid
= FB_ID_FROM_STR(name
);
1302 id
= fb_id_hash(&tid
);
1303 chat
= purple_conversations_find_chat(gc
, id
);
1305 if ((chat
!= NULL
) && !purple_chat_conversation_has_left(chat
)) {
1306 purple_conversation_present(PURPLE_CONVERSATION(chat
));
1310 fata
= purple_connection_get_protocol_data(gc
);
1311 api
= fb_data_get_api(fata
);
1312 fb_api_thread(api
, tid
);
1316 fb_chat_get_name(GHashTable
*data
)
1320 name
= g_hash_table_lookup(data
, "name");
1321 g_return_val_if_fail(name
!= NULL
, NULL
);
1323 return g_strdup(name
);
1327 fb_chat_invite(PurpleConnection
*gc
, gint id
, const gchar
*msg
,
1335 PurpleChatConversation
*chat
;
1336 PurpleRequestCommonParameters
*cpar
;
1338 if (!FB_ID_IS_STR(who
)) {
1339 cpar
= purple_request_cpar_from_connection(gc
);
1340 purple_notify_error(gc
,
1341 _("Invite Buddy Into Chat Room"),
1342 _("Failed to Invite User"),
1343 _("Invalid Facebook identifier."),
1348 fata
= purple_connection_get_protocol_data(gc
);
1349 api
= fb_data_get_api(fata
);
1350 chat
= purple_conversations_find_chat(gc
, id
);
1352 name
= purple_conversation_get_name(PURPLE_CONVERSATION(chat
));
1353 tid
= FB_ID_FROM_STR(name
);
1354 uid
= FB_ID_FROM_STR(who
);
1356 fb_api_thread_invite(api
, tid
, uid
);
1360 fb_chat_send(PurpleConnection
*gc
, gint id
, PurpleMessage
*msg
)
1368 PurpleAccount
*acct
;
1369 PurpleChatConversation
*chat
;
1371 acct
= purple_connection_get_account(gc
);
1372 fata
= purple_connection_get_protocol_data(gc
);
1373 api
= fb_data_get_api(fata
);
1374 chat
= purple_conversations_find_chat(gc
, id
);
1376 name
= purple_conversation_get_name(PURPLE_CONVERSATION(chat
));
1377 tid
= FB_ID_FROM_STR(name
);
1379 text
= purple_message_get_contents(msg
);
1380 sext
= purple_markup_strip_html(text
);
1381 fb_api_message(api
, tid
, TRUE
, sext
);
1384 name
= purple_account_get_username(acct
);
1385 purple_serv_got_chat_in(gc
, id
, name
,
1386 purple_message_get_flags(msg
),
1387 purple_message_get_contents(msg
),
1393 fb_chat_set_topic(PurpleConnection
*gc
, gint id
, const gchar
*topic
)
1399 PurpleChatConversation
*chat
;
1401 fata
= purple_connection_get_protocol_data(gc
);
1402 api
= fb_data_get_api(fata
);
1403 chat
= purple_conversations_find_chat(gc
, id
);
1405 name
= purple_conversation_get_name(PURPLE_CONVERSATION(chat
));
1406 tid
= FB_ID_FROM_STR(name
);
1407 fb_api_thread_topic(api
, tid
, topic
);
1410 static PurpleRoomlist
*
1411 fb_roomlist_get_list(PurpleConnection
*gc
)
1416 PurpleAccount
*acct
;
1417 PurpleRoomlist
*list
;
1418 PurpleRoomlistField
*fld
;
1420 fata
= purple_connection_get_protocol_data(gc
);
1421 list
= fb_data_get_roomlist(fata
);
1422 g_return_val_if_fail(list
== NULL
, NULL
);
1424 api
= fb_data_get_api(fata
);
1425 acct
= purple_connection_get_account(gc
);
1426 list
= purple_roomlist_new(acct
);
1427 fb_data_set_roomlist(fata
, list
);
1429 fld
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING
,
1430 _("Topic"), "topic", FALSE
);
1431 flds
= g_list_prepend(flds
, fld
);
1433 fld
= purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING
,
1434 _("Users"), "users", FALSE
);
1435 flds
= g_list_prepend(flds
, fld
);
1437 flds
= g_list_reverse(flds
);
1438 purple_roomlist_set_fields(list
, flds
);
1440 purple_roomlist_set_in_progress(list
, TRUE
);
1441 fb_api_threads(api
);
1446 fb_roomlist_cancel(PurpleRoomlist
*list
)
1449 PurpleAccount
*acct
;
1450 PurpleConnection
*gc
;
1451 PurpleRoomlist
*cist
;
1453 acct
= purple_roomlist_get_account(list
);
1454 gc
= purple_account_get_connection(acct
);
1455 fata
= purple_connection_get_protocol_data(gc
);
1456 cist
= fb_data_get_roomlist(fata
);
1458 if (G_LIKELY(cist
== list
)) {
1459 fb_data_set_roomlist(fata
, NULL
);
1462 purple_roomlist_set_in_progress(list
, FALSE
);
1463 g_object_unref(list
);
1467 fb_cmd_kick(PurpleConversation
*conv
, const gchar
*cmd
, gchar
**args
,
1468 gchar
**error
, gpointer data
)
1476 PurpleAccount
*acct
;
1478 PurpleConnection
*gc
;
1479 PurpleChatConversation
*chat
;
1481 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(conv
),
1482 PURPLE_CMD_RET_FAILED
);
1484 gc
= purple_conversation_get_connection(conv
);
1485 acct
= purple_connection_get_account(gc
);
1486 chat
= PURPLE_CHAT_CONVERSATION(conv
);
1487 bdy
= fb_util_account_find_buddy(acct
, chat
, args
[0], &err
);
1490 *error
= g_strdup_printf(_("%s."), err
->message
);
1492 return PURPLE_CMD_RET_FAILED
;
1495 fata
= purple_connection_get_protocol_data(gc
);
1496 api
= fb_data_get_api(fata
);
1498 name
= purple_conversation_get_name(conv
);
1499 tid
= FB_ID_FROM_STR(name
);
1501 name
= purple_buddy_get_name(bdy
);
1502 uid
= FB_ID_FROM_STR(name
);
1504 fb_api_thread_remove(api
, tid
, uid
);
1505 return PURPLE_CMD_RET_OK
;
1509 fb_cmd_leave(PurpleConversation
*conv
, const gchar
*cmd
, gchar
**args
,
1510 gchar
**error
, gpointer data
)
1517 PurpleConnection
*gc
;
1518 PurpleChatConversation
*chat
;
1520 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(conv
),
1521 PURPLE_CMD_RET_FAILED
);
1523 gc
= purple_conversation_get_connection(conv
);
1524 fata
= purple_connection_get_protocol_data(gc
);
1525 api
= fb_data_get_api(fata
);
1527 chat
= PURPLE_CHAT_CONVERSATION(conv
);
1528 id
= purple_chat_conversation_get_id(chat
);
1530 name
= purple_conversation_get_name(conv
);
1531 tid
= FB_ID_FROM_STR(name
);
1533 purple_serv_got_chat_left(gc
, id
);
1534 fb_api_thread_remove(api
, tid
, 0);
1535 return PURPLE_CMD_RET_OK
;
1539 facebook_protocol_init(PurpleProtocol
*protocol
)
1542 PurpleAccountOption
*opt
;
1544 protocol
->id
= FB_PROTOCOL_ID
;
1545 protocol
->name
= "Facebook";
1546 protocol
->options
= OPT_PROTO_CHAT_TOPIC
;
1548 opt
= purple_account_option_int_new(_("Buddy list sync interval"),
1549 "sync-interval", 5);
1550 opts
= g_list_prepend(opts
, opt
);
1552 opt
= purple_account_option_bool_new(_("Mark messages as read on focus"),
1554 opts
= g_list_prepend(opts
, opt
);
1556 opt
= purple_account_option_bool_new(_("Mark messages as read only when available"),
1557 "mark-read-available", FALSE
);
1558 opts
= g_list_prepend(opts
, opt
);
1560 opt
= purple_account_option_bool_new(_("Show self messages"),
1562 opts
= g_list_prepend(opts
, opt
);
1564 opt
= purple_account_option_bool_new(_("Show unread messages"),
1565 "show-unread", TRUE
);
1566 opts
= g_list_prepend(opts
, opt
);
1568 opt
= purple_account_option_bool_new(_("Open new group chats with "
1569 "incoming messages"),
1570 "group-chat-open", TRUE
);
1571 opts
= g_list_prepend(opts
, opt
);
1572 protocol
->account_options
= g_list_reverse(opts
);
1576 facebook_protocol_class_init(PurpleProtocolClass
*klass
)
1578 klass
->login
= fb_login
;
1579 klass
->close
= fb_close
;
1580 klass
->status_types
= fb_status_types
;
1581 klass
->list_icon
= fb_list_icon
;
1585 facebook_protocol_client_iface_init(PurpleProtocolClientInterface
*iface
)
1587 iface
->tooltip_text
= fb_client_tooltip_text
;
1588 iface
->blist_node_menu
= fb_client_blist_node_menu
;
1589 iface
->offline_message
= fb_client_offline_message
;
1593 facebook_protocol_server_iface_init(PurpleProtocolServerInterface
*iface
)
1595 iface
->set_status
= fb_server_set_status
;
1599 facebook_protocol_im_iface_init(PurpleProtocolIMInterface
*iface
)
1601 iface
->send
= fb_im_send
;
1602 iface
->send_typing
= fb_im_send_typing
;
1606 facebook_protocol_chat_iface_init(PurpleProtocolChatInterface
*iface
)
1608 iface
->info
= fb_chat_info
;
1609 iface
->info_defaults
= fb_chat_info_defaults
;
1610 iface
->join
= fb_chat_join
;
1611 iface
->get_name
= fb_chat_get_name
;
1612 iface
->invite
= fb_chat_invite
;
1613 iface
->send
= fb_chat_send
;
1614 iface
->set_topic
= fb_chat_set_topic
;
1618 facebook_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface
*iface
)
1620 iface
->get_list
= fb_roomlist_get_list
;
1621 iface
->cancel
= fb_roomlist_cancel
;
1624 PURPLE_DEFINE_TYPE_EXTENDED(
1625 FacebookProtocol
, facebook_protocol
, PURPLE_TYPE_PROTOCOL
, 0,
1627 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT
,
1628 facebook_protocol_client_iface_init
)
1629 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER
,
1630 facebook_protocol_server_iface_init
)
1631 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM
,
1632 facebook_protocol_im_iface_init
)
1633 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT
,
1634 facebook_protocol_chat_iface_init
)
1635 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST
,
1636 facebook_protocol_roomlist_iface_init
)
1640 fb_cmds_register(void)
1644 static PurpleCmdFlag cflags
=
1645 PURPLE_CMD_FLAG_CHAT
|
1646 PURPLE_CMD_FLAG_PROTOCOL_ONLY
;
1648 g_return_if_fail(fb_cmds
== NULL
);
1650 id
= purple_cmd_register("kick", "s", PURPLE_CMD_P_PROTOCOL
, cflags
,
1651 fb_protocol
->id
, fb_cmd_kick
,
1652 _("kick: Kick someone from the chat"),
1654 fb_cmds
= g_slist_prepend(fb_cmds
, GUINT_TO_POINTER(id
));
1656 id
= purple_cmd_register("leave", "", PURPLE_CMD_P_PROTOCOL
, cflags
,
1657 fb_protocol
->id
, fb_cmd_leave
,
1658 _("leave: Leave the chat"),
1660 fb_cmds
= g_slist_prepend(fb_cmds
, GUINT_TO_POINTER(id
));
1664 fb_cmds_unregister_free(gpointer data
)
1666 PurpleCmdId id
= GPOINTER_TO_UINT(data
);
1667 purple_cmd_unregister(id
);
1671 fb_cmds_unregister(void)
1673 g_slist_free_full(fb_cmds
, fb_cmds_unregister_free
);
1676 static PurplePluginInfo
*
1677 plugin_query(GError
**error
)
1679 return purple_plugin_info_new(
1680 "id", FB_PROTOCOL_ID
,
1681 "name", "Facebook Protocol",
1682 "version", DISPLAY_VERSION
,
1683 "category", N_("Protocol"),
1684 "summary", N_("Facebook Protocol Plugin"),
1685 "description", N_("Facebook Protocol Plugin"),
1686 "website", PURPLE_WEBSITE
,
1687 "abi-version", PURPLE_ABI_VERSION
,
1688 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
|
1689 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD
,
1695 plugin_load(PurplePlugin
*plugin
, GError
**error
)
1697 facebook_protocol_register_type(plugin
);
1698 fb_protocol
= purple_protocols_add(FACEBOOK_TYPE_PROTOCOL
, error
);
1700 if (fb_protocol
== NULL
) {
1709 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
1711 fb_cmds_unregister();
1712 return purple_protocols_remove(fb_protocol
, error
);
1715 PURPLE_PLUGIN_INIT(facebook
, plugin_query
, plugin_load
, plugin_unload
);