4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 #include "conversation.h"
29 #include "purpleaccountoption.h"
33 static GHashTable
*protocols
= NULL
;
35 /**************************************************************************
36 * GBoxed code for PurpleProtocolChatEntry
37 **************************************************************************/
39 static PurpleProtocolChatEntry
*
40 purple_protocol_chat_entry_copy(PurpleProtocolChatEntry
*pce
)
42 PurpleProtocolChatEntry
*pce_copy
;
44 g_return_val_if_fail(pce
!= NULL
, NULL
);
46 pce_copy
= g_new(PurpleProtocolChatEntry
, 1);
53 purple_protocol_chat_entry_get_type(void)
55 static GType type
= 0;
58 type
= g_boxed_type_register_static("PurpleProtocolChatEntry",
59 (GBoxedCopyFunc
)purple_protocol_chat_entry_copy
,
60 (GBoxedFreeFunc
)g_free
);
66 /**************************************************************************/
68 /**************************************************************************/
70 purple_protocol_got_account_idle(PurpleAccount
*account
, gboolean idle
,
73 g_return_if_fail(account
!= NULL
);
74 g_return_if_fail(purple_account_is_connected(account
));
76 purple_presence_set_idle(purple_account_get_presence(account
),
81 purple_protocol_got_account_login_time(PurpleAccount
*account
, time_t login_time
)
83 PurplePresence
*presence
;
85 g_return_if_fail(account
!= NULL
);
86 g_return_if_fail(purple_account_is_connected(account
));
89 login_time
= time(NULL
);
91 presence
= purple_account_get_presence(account
);
93 purple_presence_set_login_time(presence
, login_time
);
97 purple_protocol_got_account_status(PurpleAccount
*account
, const char *status_id
, ...)
99 PurplePresence
*presence
;
100 PurpleStatus
*status
;
103 g_return_if_fail(account
!= NULL
);
104 g_return_if_fail(status_id
!= NULL
);
105 g_return_if_fail(purple_account_is_connected(account
));
107 presence
= purple_account_get_presence(account
);
108 status
= purple_presence_get_status(presence
, status_id
);
110 g_return_if_fail(status
!= NULL
);
112 va_start(args
, status_id
);
113 purple_status_set_active_with_attrs(status
, TRUE
, args
);
118 purple_protocol_got_account_actions(PurpleAccount
*account
)
121 g_return_if_fail(account
!= NULL
);
122 g_return_if_fail(purple_account_is_connected(account
));
124 purple_signal_emit(purple_accounts_get_handle(), "account-actions-changed",
129 purple_protocol_got_user_idle(PurpleAccount
*account
, const char *name
,
130 gboolean idle
, time_t idle_time
)
132 PurplePresence
*presence
;
135 g_return_if_fail(account
!= NULL
);
136 g_return_if_fail(name
!= NULL
);
137 g_return_if_fail(purple_account_is_connected(account
) || purple_account_is_connecting(account
));
139 if ((list
= purple_blist_find_buddies(account
, name
)) == NULL
)
143 presence
= purple_buddy_get_presence(list
->data
);
144 list
= g_slist_delete_link(list
, list
);
145 purple_presence_set_idle(presence
, idle
, idle_time
);
150 purple_protocol_got_user_login_time(PurpleAccount
*account
, const char *name
,
154 PurplePresence
*presence
;
156 g_return_if_fail(account
!= NULL
);
157 g_return_if_fail(name
!= NULL
);
159 if ((list
= purple_blist_find_buddies(account
, name
)) == NULL
)
163 login_time
= time(NULL
);
166 PurpleBuddy
*buddy
= list
->data
;
167 presence
= purple_buddy_get_presence(buddy
);
168 list
= g_slist_delete_link(list
, list
);
170 if (purple_presence_get_login_time(presence
) != login_time
)
172 purple_presence_set_login_time(presence
, login_time
);
174 purple_signal_emit(purple_blist_get_handle(), "buddy-got-login-time", buddy
);
180 purple_protocol_got_user_status(PurpleAccount
*account
, const char *name
,
181 const char *status_id
, ...)
185 PurplePresence
*presence
;
186 PurpleStatus
*status
;
187 PurpleStatus
*old_status
;
190 g_return_if_fail(account
!= NULL
);
191 g_return_if_fail(name
!= NULL
);
192 g_return_if_fail(status_id
!= NULL
);
193 g_return_if_fail(purple_account_is_connected(account
) || purple_account_is_connecting(account
));
195 if((list
= purple_blist_find_buddies(account
, name
)) == NULL
)
198 for(l
= list
; l
!= NULL
; l
= l
->next
) {
201 presence
= purple_buddy_get_presence(buddy
);
202 status
= purple_presence_get_status(presence
, status_id
);
206 * TODO: This should never happen, right? We should call
207 * g_warning() or something.
211 old_status
= purple_presence_get_active_status(presence
);
213 va_start(args
, status_id
);
214 purple_status_set_active_with_attrs(status
, TRUE
, args
);
217 purple_buddy_update_status(buddy
, old_status
);
222 /* The buddy is no longer online, they are therefore by definition not
223 * still typing to us. */
224 if (!purple_status_is_online(status
)) {
225 purple_serv_got_typing_stopped(purple_account_get_connection(account
), name
);
226 purple_protocol_got_media_caps(account
, name
);
230 void purple_protocol_got_user_status_deactive(PurpleAccount
*account
, const char *name
,
231 const char *status_id
)
235 PurplePresence
*presence
;
236 PurpleStatus
*status
;
238 g_return_if_fail(account
!= NULL
);
239 g_return_if_fail(name
!= NULL
);
240 g_return_if_fail(status_id
!= NULL
);
241 g_return_if_fail(purple_account_is_connected(account
) || purple_account_is_connecting(account
));
243 if((list
= purple_blist_find_buddies(account
, name
)) == NULL
)
246 for(l
= list
; l
!= NULL
; l
= l
->next
) {
249 presence
= purple_buddy_get_presence(buddy
);
250 status
= purple_presence_get_status(presence
, status_id
);
255 if (purple_status_is_active(status
)) {
256 purple_status_set_active(status
, FALSE
);
257 purple_buddy_update_status(buddy
, status
);
265 do_protocol_change_account_status(PurpleAccount
*account
,
266 PurpleStatus
*old_status
, PurpleStatus
*new_status
)
268 PurpleProtocol
*protocol
;
270 if (purple_status_is_online(new_status
) &&
271 purple_account_is_disconnected(account
) &&
272 purple_network_is_available())
274 purple_account_connect(account
);
278 if (!purple_status_is_online(new_status
))
280 if (!purple_account_is_disconnected(account
))
281 purple_account_disconnect(account
);
282 /* Clear out the unsaved password if we switch to offline status */
283 if (!purple_account_get_remember_password(account
))
284 purple_account_set_password(account
, NULL
, NULL
, NULL
);
289 if (purple_account_is_connecting(account
))
291 * We don't need to call the set_status protocol function because
292 * the protocol will take care of setting its status during the
293 * connection process.
297 protocol
= purple_protocols_find(purple_account_get_protocol_id(account
));
299 if (protocol
== NULL
)
302 if (!purple_account_is_disconnected(account
))
303 purple_protocol_server_iface_set_status(protocol
, account
, new_status
);
307 purple_protocol_change_account_status(PurpleAccount
*account
,
308 PurpleStatus
*old_status
, PurpleStatus
*new_status
)
310 g_return_if_fail(account
!= NULL
);
311 g_return_if_fail(new_status
!= NULL
);
312 g_return_if_fail(!purple_status_is_exclusive(new_status
) || old_status
!= NULL
);
314 purple_signal_emit(purple_accounts_get_handle(), "account-status-changing",
315 account
, old_status
, new_status
);
317 do_protocol_change_account_status(account
, old_status
, new_status
);
319 purple_signal_emit(purple_accounts_get_handle(), "account-status-changed",
320 account
, old_status
, new_status
);
324 purple_protocol_get_statuses(PurpleAccount
*account
, PurplePresence
*presence
)
326 GList
*statuses
= NULL
;
328 PurpleStatus
*status
;
330 g_return_val_if_fail(account
!= NULL
, NULL
);
331 g_return_val_if_fail(presence
!= NULL
, NULL
);
333 for (l
= purple_account_get_status_types(account
); l
!= NULL
; l
= l
->next
)
335 status
= purple_status_new((PurpleStatusType
*)l
->data
, presence
);
336 statuses
= g_list_prepend(statuses
, status
);
339 statuses
= g_list_reverse(statuses
);
345 purple_protocol_attention(PurpleConversation
*conv
, const char *who
,
346 guint type
, PurpleMessageFlags flags
, time_t mtime
)
348 PurpleAccount
*account
= purple_conversation_get_account(conv
);
349 purple_signal_emit(purple_conversations_get_handle(),
350 flags
== PURPLE_MESSAGE_SEND
? "sent-attention" : "got-attention",
351 account
, who
, conv
, type
);
355 purple_protocol_send_attention(PurpleConnection
*gc
, const char *who
, guint type_code
)
357 PurpleAttentionType
*attn
;
358 PurpleProtocol
*protocol
;
359 PurpleIMConversation
*im
;
364 g_return_if_fail(gc
!= NULL
);
365 g_return_if_fail(who
!= NULL
);
367 protocol
= purple_protocols_find(purple_account_get_protocol_id(purple_connection_get_account(gc
)));
368 g_return_if_fail(PURPLE_IS_PROTOCOL_ATTENTION(protocol
));
370 attn
= purple_get_attention_type_from_code(purple_connection_get_account(gc
), type_code
);
372 if ((buddy
= purple_blist_find_buddy(purple_connection_get_account(gc
), who
)) != NULL
)
373 alias
= purple_buddy_get_contact_alias(buddy
);
377 if (attn
&& purple_attention_type_get_outgoing_desc(attn
)) {
378 description
= g_strdup_printf(purple_attention_type_get_outgoing_desc(attn
), alias
);
380 description
= g_strdup_printf(_("Requesting %s's attention..."), alias
);
383 purple_debug_info("server", "serv_send_attention: sending '%s' to %s\n",
386 if (!purple_protocol_attention_send(PURPLE_PROTOCOL_ATTENTION(protocol
), gc
, who
, type_code
))
389 im
= purple_im_conversation_new(purple_connection_get_account(gc
), who
);
390 purple_conversation_write_system_message(PURPLE_CONVERSATION(im
), description
, 0);
391 purple_protocol_attention(PURPLE_CONVERSATION(im
), who
, type_code
, PURPLE_MESSAGE_SEND
, time(NULL
));
397 got_attention(PurpleConnection
*gc
, int id
, const char *who
, guint type_code
)
399 PurpleMessageFlags flags
;
400 PurpleAttentionType
*attn
;
408 attn
= purple_get_attention_type_from_code(purple_connection_get_account(gc
), type_code
);
410 /* PURPLE_MESSAGE_NOTIFY is for attention messages. */
411 flags
= PURPLE_MESSAGE_SYSTEM
| PURPLE_MESSAGE_NOTIFY
| PURPLE_MESSAGE_RECV
;
413 /* TODO: if (attn->icon_name) is non-null, use it to lookup an emoticon and display
414 * it next to the attention command. And if it is null, display a generic icon. */
416 if ((buddy
= purple_blist_find_buddy(purple_connection_get_account(gc
), who
)) != NULL
)
417 alias
= purple_buddy_get_contact_alias(buddy
);
421 if (attn
&& purple_attention_type_get_incoming_desc(attn
)) {
422 description
= g_strdup_printf(purple_attention_type_get_incoming_desc(attn
), alias
);
424 description
= g_strdup_printf(_("%s has requested your attention!"), alias
);
427 purple_debug_info("server", "got_attention: got '%s' from %s\n",
431 purple_serv_got_im(gc
, who
, description
, flags
, mtime
);
433 purple_serv_got_chat_in(gc
, id
, who
, flags
, description
, mtime
);
435 /* TODO: sounds (depending on PurpleAttentionType), shaking, etc. */
441 purple_protocol_got_attention(PurpleConnection
*gc
, const char *who
, guint type_code
)
443 PurpleConversation
*conv
= NULL
;
444 PurpleAccount
*account
= purple_connection_get_account(gc
);
446 got_attention(gc
, -1, who
, type_code
);
448 purple_conversations_find_with_account(who
, account
);
450 purple_protocol_attention(conv
, who
, type_code
, PURPLE_MESSAGE_RECV
,
455 purple_protocol_got_attention_in_chat(PurpleConnection
*gc
, int id
, const char *who
, guint type_code
)
457 got_attention(gc
, id
, who
, type_code
);
461 purple_protocol_initiate_media(PurpleAccount
*account
,
463 PurpleMediaSessionType type
)
466 PurpleConnection
*gc
= NULL
;
467 PurpleProtocol
*protocol
= NULL
;
470 gc
= purple_account_get_connection(account
);
472 protocol
= purple_connection_get_protocol(gc
);
475 /* should check that the protocol supports this media type here? */
476 return purple_protocol_media_iface_initiate_session(protocol
, account
, who
, type
);
483 purple_protocol_get_media_caps(PurpleAccount
*account
, const char *who
)
486 PurpleConnection
*gc
= NULL
;
487 PurpleProtocol
*protocol
= NULL
;
490 gc
= purple_account_get_connection(account
);
492 protocol
= purple_connection_get_protocol(gc
);
495 return purple_protocol_media_iface_get_caps(protocol
, account
, who
);
497 return PURPLE_MEDIA_CAPS_NONE
;
501 purple_protocol_got_media_caps(PurpleAccount
*account
, const char *name
)
506 g_return_if_fail(account
!= NULL
);
507 g_return_if_fail(name
!= NULL
);
509 if ((list
= purple_blist_find_buddies(account
, name
)) == NULL
)
513 PurpleBuddy
*buddy
= list
->data
;
514 PurpleMediaCaps oldcaps
= purple_buddy_get_media_caps(buddy
);
515 PurpleMediaCaps newcaps
= 0;
516 const gchar
*bname
= purple_buddy_get_name(buddy
);
517 list
= g_slist_delete_link(list
, list
);
520 newcaps
= purple_protocol_get_media_caps(account
, bname
);
521 purple_buddy_set_media_caps(buddy
, newcaps
);
523 if (oldcaps
== newcaps
)
526 purple_signal_emit(purple_blist_get_handle(),
527 "buddy-caps-changed", buddy
,
534 purple_protocol_get_max_message_size(PurpleProtocol
*protocol
)
536 g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol
), 0);
538 return purple_protocol_client_iface_get_max_message_size(protocol
, NULL
);
541 /**************************************************************************
543 **************************************************************************/
545 * Negative if a before b, 0 if equal, positive if a after b.
548 compare_protocol(PurpleProtocol
*a
, PurpleProtocol
*b
)
550 const gchar
*aname
= purple_protocol_get_name(a
);
551 const gchar
*bname
= purple_protocol_get_name(b
);
555 return strcmp(aname
, bname
);
567 purple_protocols_find(const char *id
)
569 g_return_val_if_fail(protocols
!= NULL
&& id
!= NULL
, NULL
);
571 return g_hash_table_lookup(protocols
, id
);
575 purple_protocols_add(GType protocol_type
, GError
**error
)
577 PurpleProtocol
*protocol
;
578 PurpleProtocolClass
*klass
;
580 if (protocol_type
== G_TYPE_INVALID
) {
581 g_set_error_literal(error
, PURPLE_PROTOCOLS_DOMAIN
, 0,
582 _("Protocol type is not registered"));
586 if (!g_type_is_a(protocol_type
, PURPLE_TYPE_PROTOCOL
)) {
587 g_set_error_literal(error
, PURPLE_PROTOCOLS_DOMAIN
, 0,
588 _("Protocol type does not inherit PurpleProtocol"));
592 if (G_TYPE_IS_ABSTRACT(protocol_type
)) {
593 g_set_error_literal(error
, PURPLE_PROTOCOLS_DOMAIN
, 0,
594 _("Protocol type is abstract"));
598 protocol
= g_object_new(protocol_type
, NULL
);
600 g_set_error_literal(error
, PURPLE_PROTOCOLS_DOMAIN
, 0,
601 _("Could not create protocol instance"));
605 if (!purple_protocol_get_id(protocol
)) {
606 g_set_error_literal(error
, PURPLE_PROTOCOLS_DOMAIN
, 0,
607 _("Protocol does not provide an ID"));
609 g_object_unref(protocol
);
613 if (purple_protocols_find(purple_protocol_get_id(protocol
))) {
614 g_set_error(error
, PURPLE_PROTOCOLS_DOMAIN
, 0,
615 _("A protocol with the ID %s is already added."),
616 purple_protocol_get_id(protocol
));
618 g_object_unref(protocol
);
622 /* Make sure the protocol implements the required functions */
623 klass
= PURPLE_PROTOCOL_GET_CLASS(protocol
);
625 if (!klass
->login
|| !klass
->close
||
626 !klass
->status_types
|| !klass
->list_icon
)
628 g_set_error(error
, PURPLE_PROTOCOLS_DOMAIN
, 0,
629 _("Protocol %s does not implement all the functions in "
630 "PurpleProtocolClass"), purple_protocol_get_id(protocol
));
632 g_object_unref(protocol
);
636 g_hash_table_insert(protocols
, g_strdup(purple_protocol_get_id(protocol
)),
639 purple_debug_info("protocols", "Added protocol %s\n",
640 purple_protocol_get_id(protocol
));
642 purple_signal_emit(purple_protocols_get_handle(), "protocol-added",
647 gboolean
purple_protocols_remove(PurpleProtocol
*protocol
, GError
**error
)
649 g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol
), FALSE
);
650 g_return_val_if_fail(purple_protocol_get_id(protocol
) != NULL
, FALSE
);
652 if (purple_protocols_find(purple_protocol_get_id(protocol
)) == NULL
) {
653 g_set_error(error
, PURPLE_PROTOCOLS_DOMAIN
, 0,
654 _("Protocol %s is not added."),
655 purple_protocol_get_id(protocol
));
660 purple_debug_info("protocols", "Removing protocol %s\n",
661 purple_protocol_get_id(protocol
));
663 purple_signal_emit(purple_protocols_get_handle(), "protocol-removed",
666 g_hash_table_remove(protocols
, purple_protocol_get_id(protocol
));
671 purple_protocols_get_all(void)
674 PurpleProtocol
*protocol
;
677 g_hash_table_iter_init(&iter
, protocols
);
678 while (g_hash_table_iter_next(&iter
, NULL
, (gpointer
*)&protocol
))
679 ret
= g_list_insert_sorted(ret
, protocol
, (GCompareFunc
)compare_protocol
);
684 /**************************************************************************
685 * Protocols Subsystem API
686 **************************************************************************/
688 purple_protocols_init(void)
690 void *handle
= purple_protocols_get_handle();
692 protocols
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
,
693 (GDestroyNotify
)g_object_unref
);
695 purple_signal_register(handle
, "protocol-added",
696 purple_marshal_VOID__POINTER
,
697 G_TYPE_NONE
, 1, PURPLE_TYPE_PROTOCOL
);
698 purple_signal_register(handle
, "protocol-removed",
699 purple_marshal_VOID__POINTER
,
700 G_TYPE_NONE
, 1, PURPLE_TYPE_PROTOCOL
);
704 purple_protocols_get_handle(void)
712 purple_protocols_uninit(void)
714 g_hash_table_destroy(protocols
);