Respect -c option
[pidgin-git.git] / libpurple / protocols.c
blob4f310f9e7c93d8f0a4718b8686b1a8b9a93209dd
1 /*
2 * purple
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
6 * source distribution.
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
23 #include "internal.h"
24 #include "accountopt.h"
25 #include "conversation.h"
26 #include "debug.h"
27 #include "network.h"
28 #include "notify.h"
29 #include "protocol.h"
30 #include "request.h"
31 #include "util.h"
33 static GHashTable *protocols = NULL;
35 /**************************************************************************/
36 /* Attention Type API */
37 /**************************************************************************/
39 struct _PurpleAttentionType
41 const char *name; /* Shown in GUI elements */
42 const char *incoming_description; /* Shown when sent */
43 const char *outgoing_description; /* Shown when receied */
44 const char *icon_name; /* Icon to display (optional) */
45 const char *unlocalized_name; /* Unlocalized name for UIs needing it */
49 PurpleAttentionType *
50 purple_attention_type_new(const char *ulname, const char *name,
51 const char *inc_desc, const char *out_desc)
53 PurpleAttentionType *attn = g_new0(PurpleAttentionType, 1);
55 purple_attention_type_set_name(attn, name);
56 purple_attention_type_set_incoming_desc(attn, inc_desc);
57 purple_attention_type_set_outgoing_desc(attn, out_desc);
58 purple_attention_type_set_unlocalized_name(attn, ulname);
60 return attn;
64 void
65 purple_attention_type_set_name(PurpleAttentionType *type, const char *name)
67 g_return_if_fail(type != NULL);
69 type->name = name;
72 void
73 purple_attention_type_set_incoming_desc(PurpleAttentionType *type, const char *desc)
75 g_return_if_fail(type != NULL);
77 type->incoming_description = desc;
80 void
81 purple_attention_type_set_outgoing_desc(PurpleAttentionType *type, const char *desc)
83 g_return_if_fail(type != NULL);
85 type->outgoing_description = desc;
88 void
89 purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name)
91 g_return_if_fail(type != NULL);
93 type->icon_name = name;
96 void
97 purple_attention_type_set_unlocalized_name(PurpleAttentionType *type, const char *ulname)
99 g_return_if_fail(type != NULL);
101 type->unlocalized_name = ulname;
104 const char *
105 purple_attention_type_get_name(const PurpleAttentionType *type)
107 g_return_val_if_fail(type != NULL, NULL);
109 return type->name;
112 const char *
113 purple_attention_type_get_incoming_desc(const PurpleAttentionType *type)
115 g_return_val_if_fail(type != NULL, NULL);
117 return type->incoming_description;
120 const char *
121 purple_attention_type_get_outgoing_desc(const PurpleAttentionType *type)
123 g_return_val_if_fail(type != NULL, NULL);
125 return type->outgoing_description;
128 const char *
129 purple_attention_type_get_icon_name(const PurpleAttentionType *type)
131 g_return_val_if_fail(type != NULL, NULL);
133 if(type->icon_name == NULL || *(type->icon_name) == '\0')
134 return NULL;
136 return type->icon_name;
139 const char *
140 purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type)
142 g_return_val_if_fail(type != NULL, NULL);
144 return type->unlocalized_name;
147 /**************************************************************************
148 * GBoxed code for PurpleAttentionType
149 **************************************************************************/
151 static PurpleAttentionType *
152 purple_attention_type_copy(PurpleAttentionType *attn)
154 PurpleAttentionType *attn_copy;
156 g_return_val_if_fail(attn != NULL, NULL);
158 attn_copy = g_new(PurpleAttentionType, 1);
159 *attn_copy = *attn;
161 return attn_copy;
164 GType
165 purple_attention_type_get_type(void)
167 static GType type = 0;
169 if (type == 0) {
170 type = g_boxed_type_register_static("PurpleAttentionType",
171 (GBoxedCopyFunc)purple_attention_type_copy,
172 (GBoxedFreeFunc)g_free);
175 return type;
178 /**************************************************************************
179 * GBoxed code for PurpleProtocolChatEntry
180 **************************************************************************/
182 static PurpleProtocolChatEntry *
183 purple_protocol_chat_entry_copy(PurpleProtocolChatEntry *pce)
185 PurpleProtocolChatEntry *pce_copy;
187 g_return_val_if_fail(pce != NULL, NULL);
189 pce_copy = g_new(PurpleProtocolChatEntry, 1);
190 *pce_copy = *pce;
192 return pce_copy;
195 GType
196 purple_protocol_chat_entry_get_type(void)
198 static GType type = 0;
200 if (type == 0) {
201 type = g_boxed_type_register_static("PurpleProtocolChatEntry",
202 (GBoxedCopyFunc)purple_protocol_chat_entry_copy,
203 (GBoxedFreeFunc)g_free);
206 return type;
209 /**************************************************************************/
210 /* Protocol API */
211 /**************************************************************************/
212 void
213 purple_protocol_got_account_idle(PurpleAccount *account, gboolean idle,
214 time_t idle_time)
216 g_return_if_fail(account != NULL);
217 g_return_if_fail(purple_account_is_connected(account));
219 purple_presence_set_idle(purple_account_get_presence(account),
220 idle, idle_time);
223 void
224 purple_protocol_got_account_login_time(PurpleAccount *account, time_t login_time)
226 PurplePresence *presence;
228 g_return_if_fail(account != NULL);
229 g_return_if_fail(purple_account_is_connected(account));
231 if (login_time == 0)
232 login_time = time(NULL);
234 presence = purple_account_get_presence(account);
236 purple_presence_set_login_time(presence, login_time);
239 void
240 purple_protocol_got_account_status(PurpleAccount *account, const char *status_id, ...)
242 PurplePresence *presence;
243 PurpleStatus *status;
244 va_list args;
246 g_return_if_fail(account != NULL);
247 g_return_if_fail(status_id != NULL);
248 g_return_if_fail(purple_account_is_connected(account));
250 presence = purple_account_get_presence(account);
251 status = purple_presence_get_status(presence, status_id);
253 g_return_if_fail(status != NULL);
255 va_start(args, status_id);
256 purple_status_set_active_with_attrs(status, TRUE, args);
257 va_end(args);
260 void
261 purple_protocol_got_account_actions(PurpleAccount *account)
264 g_return_if_fail(account != NULL);
265 g_return_if_fail(purple_account_is_connected(account));
267 purple_signal_emit(purple_accounts_get_handle(), "account-actions-changed",
268 account);
271 void
272 purple_protocol_got_user_idle(PurpleAccount *account, const char *name,
273 gboolean idle, time_t idle_time)
275 PurplePresence *presence;
276 GSList *list;
278 g_return_if_fail(account != NULL);
279 g_return_if_fail(name != NULL);
280 g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
282 if ((list = purple_blist_find_buddies(account, name)) == NULL)
283 return;
285 while (list) {
286 presence = purple_buddy_get_presence(list->data);
287 list = g_slist_delete_link(list, list);
288 purple_presence_set_idle(presence, idle, idle_time);
292 void
293 purple_protocol_got_user_login_time(PurpleAccount *account, const char *name,
294 time_t login_time)
296 GSList *list;
297 PurplePresence *presence;
299 g_return_if_fail(account != NULL);
300 g_return_if_fail(name != NULL);
302 if ((list = purple_blist_find_buddies(account, name)) == NULL)
303 return;
305 if (login_time == 0)
306 login_time = time(NULL);
308 while (list) {
309 PurpleBuddy *buddy = list->data;
310 presence = purple_buddy_get_presence(buddy);
311 list = g_slist_delete_link(list, list);
313 if (purple_presence_get_login_time(presence) != login_time)
315 purple_presence_set_login_time(presence, login_time);
317 purple_signal_emit(purple_blist_get_handle(), "buddy-got-login-time", buddy);
322 void
323 purple_protocol_got_user_status(PurpleAccount *account, const char *name,
324 const char *status_id, ...)
326 GSList *list, *l;
327 PurpleBuddy *buddy;
328 PurplePresence *presence;
329 PurpleStatus *status;
330 PurpleStatus *old_status;
331 va_list args;
333 g_return_if_fail(account != NULL);
334 g_return_if_fail(name != NULL);
335 g_return_if_fail(status_id != NULL);
336 g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
338 if((list = purple_blist_find_buddies(account, name)) == NULL)
339 return;
341 for(l = list; l != NULL; l = l->next) {
342 buddy = l->data;
344 presence = purple_buddy_get_presence(buddy);
345 status = purple_presence_get_status(presence, status_id);
347 if(NULL == status)
349 * TODO: This should never happen, right? We should call
350 * g_warning() or something.
352 continue;
354 old_status = purple_presence_get_active_status(presence);
356 va_start(args, status_id);
357 purple_status_set_active_with_attrs(status, TRUE, args);
358 va_end(args);
360 purple_buddy_update_status(buddy, old_status);
363 g_slist_free(list);
365 /* The buddy is no longer online, they are therefore by definition not
366 * still typing to us. */
367 if (!purple_status_is_online(status)) {
368 purple_serv_got_typing_stopped(purple_account_get_connection(account), name);
369 purple_protocol_got_media_caps(account, name);
373 void purple_protocol_got_user_status_deactive(PurpleAccount *account, const char *name,
374 const char *status_id)
376 GSList *list, *l;
377 PurpleBuddy *buddy;
378 PurplePresence *presence;
379 PurpleStatus *status;
381 g_return_if_fail(account != NULL);
382 g_return_if_fail(name != NULL);
383 g_return_if_fail(status_id != NULL);
384 g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account));
386 if((list = purple_blist_find_buddies(account, name)) == NULL)
387 return;
389 for(l = list; l != NULL; l = l->next) {
390 buddy = l->data;
392 presence = purple_buddy_get_presence(buddy);
393 status = purple_presence_get_status(presence, status_id);
395 if(NULL == status)
396 continue;
398 if (purple_status_is_active(status)) {
399 purple_status_set_active(status, FALSE);
400 purple_buddy_update_status(buddy, status);
404 g_slist_free(list);
407 static void
408 do_protocol_change_account_status(PurpleAccount *account,
409 PurpleStatus *old_status, PurpleStatus *new_status)
411 PurpleProtocol *protocol;
413 if (purple_status_is_online(new_status) &&
414 purple_account_is_disconnected(account) &&
415 purple_network_is_available())
417 purple_account_connect(account);
418 return;
421 if (!purple_status_is_online(new_status))
423 if (!purple_account_is_disconnected(account))
424 purple_account_disconnect(account);
425 /* Clear out the unsaved password if we switch to offline status */
426 if (!purple_account_get_remember_password(account))
427 purple_account_set_password(account, NULL, NULL, NULL);
429 return;
432 if (purple_account_is_connecting(account))
434 * We don't need to call the set_status protocol function because
435 * the protocol will take care of setting its status during the
436 * connection process.
438 return;
440 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
442 if (protocol == NULL)
443 return;
445 if (!purple_account_is_disconnected(account))
446 purple_protocol_server_iface_set_status(protocol, account, new_status);
449 void
450 purple_protocol_change_account_status(PurpleAccount *account,
451 PurpleStatus *old_status, PurpleStatus *new_status)
453 g_return_if_fail(account != NULL);
454 g_return_if_fail(new_status != NULL);
455 g_return_if_fail(!purple_status_is_exclusive(new_status) || old_status != NULL);
457 purple_signal_emit(purple_accounts_get_handle(), "account-status-changing",
458 account, old_status, new_status);
460 do_protocol_change_account_status(account, old_status, new_status);
462 purple_signal_emit(purple_accounts_get_handle(), "account-status-changed",
463 account, old_status, new_status);
466 GList *
467 purple_protocol_get_statuses(PurpleAccount *account, PurplePresence *presence)
469 GList *statuses = NULL;
470 GList *l;
471 PurpleStatus *status;
473 g_return_val_if_fail(account != NULL, NULL);
474 g_return_val_if_fail(presence != NULL, NULL);
476 for (l = purple_account_get_status_types(account); l != NULL; l = l->next)
478 status = purple_status_new((PurpleStatusType *)l->data, presence);
479 statuses = g_list_prepend(statuses, status);
482 statuses = g_list_reverse(statuses);
484 return statuses;
487 static void
488 purple_protocol_attention(PurpleConversation *conv, const char *who,
489 guint type, PurpleMessageFlags flags, time_t mtime)
491 PurpleAccount *account = purple_conversation_get_account(conv);
492 purple_signal_emit(purple_conversations_get_handle(),
493 flags == PURPLE_MESSAGE_SEND ? "sent-attention" : "got-attention",
494 account, who, conv, type);
497 void
498 purple_protocol_send_attention(PurpleConnection *gc, const char *who, guint type_code)
500 PurpleAttentionType *attn;
501 PurpleProtocol *protocol;
502 PurpleIMConversation *im;
503 PurpleBuddy *buddy;
504 const char *alias;
505 gchar *description;
507 g_return_if_fail(gc != NULL);
508 g_return_if_fail(who != NULL);
510 protocol = purple_protocols_find(purple_account_get_protocol_id(purple_connection_get_account(gc)));
511 g_return_if_fail(PURPLE_PROTOCOL_IMPLEMENTS(protocol, ATTENTION_IFACE, send));
513 attn = purple_get_attention_type_from_code(purple_connection_get_account(gc), type_code);
515 if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who)) != NULL)
516 alias = purple_buddy_get_contact_alias(buddy);
517 else
518 alias = who;
520 if (attn && purple_attention_type_get_outgoing_desc(attn)) {
521 description = g_strdup_printf(purple_attention_type_get_outgoing_desc(attn), alias);
522 } else {
523 description = g_strdup_printf(_("Requesting %s's attention..."), alias);
526 purple_debug_info("server", "serv_send_attention: sending '%s' to %s\n",
527 description, who);
529 if (!purple_protocol_attention_iface_send(protocol, gc, who, type_code))
530 return;
532 im = purple_im_conversation_new(purple_connection_get_account(gc), who);
533 purple_conversation_write_system_message(PURPLE_CONVERSATION(im), description, 0);
534 purple_protocol_attention(PURPLE_CONVERSATION(im), who, type_code, PURPLE_MESSAGE_SEND, time(NULL));
536 g_free(description);
539 static void
540 got_attention(PurpleConnection *gc, int id, const char *who, guint type_code)
542 PurpleMessageFlags flags;
543 PurpleAttentionType *attn;
544 PurpleBuddy *buddy;
545 const char *alias;
546 gchar *description;
547 time_t mtime;
549 mtime = time(NULL);
551 attn = purple_get_attention_type_from_code(purple_connection_get_account(gc), type_code);
553 /* PURPLE_MESSAGE_NOTIFY is for attention messages. */
554 flags = PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_RECV;
556 /* TODO: if (attn->icon_name) is non-null, use it to lookup an emoticon and display
557 * it next to the attention command. And if it is null, display a generic icon. */
559 if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who)) != NULL)
560 alias = purple_buddy_get_contact_alias(buddy);
561 else
562 alias = who;
564 if (attn && purple_attention_type_get_incoming_desc(attn)) {
565 description = g_strdup_printf(purple_attention_type_get_incoming_desc(attn), alias);
566 } else {
567 description = g_strdup_printf(_("%s has requested your attention!"), alias);
570 purple_debug_info("server", "got_attention: got '%s' from %s\n",
571 description, who);
573 if (id == -1)
574 purple_serv_got_im(gc, who, description, flags, mtime);
575 else
576 purple_serv_got_chat_in(gc, id, who, flags, description, mtime);
578 /* TODO: sounds (depending on PurpleAttentionType), shaking, etc. */
580 g_free(description);
583 void
584 purple_protocol_got_attention(PurpleConnection *gc, const char *who, guint type_code)
586 PurpleConversation *conv = NULL;
587 PurpleAccount *account = purple_connection_get_account(gc);
589 got_attention(gc, -1, who, type_code);
590 conv =
591 purple_conversations_find_with_account(who, account);
592 if (conv)
593 purple_protocol_attention(conv, who, type_code, PURPLE_MESSAGE_RECV,
594 time(NULL));
597 void
598 purple_protocol_got_attention_in_chat(PurpleConnection *gc, int id, const char *who, guint type_code)
600 got_attention(gc, id, who, type_code);
603 gboolean
604 purple_protocol_initiate_media(PurpleAccount *account,
605 const char *who,
606 PurpleMediaSessionType type)
608 #ifdef USE_VV
609 PurpleConnection *gc = NULL;
610 PurpleProtocol *protocol = NULL;
612 if (account)
613 gc = purple_account_get_connection(account);
614 if (gc)
615 protocol = purple_connection_get_protocol(gc);
617 if (protocol) {
618 /* should check that the protocol supports this media type here? */
619 return purple_protocol_media_iface_initiate_session(protocol, account, who, type);
620 } else
621 #endif
622 return FALSE;
625 PurpleMediaCaps
626 purple_protocol_get_media_caps(PurpleAccount *account, const char *who)
628 #ifdef USE_VV
629 PurpleConnection *gc = NULL;
630 PurpleProtocol *protocol = NULL;
632 if (account)
633 gc = purple_account_get_connection(account);
634 if (gc)
635 protocol = purple_connection_get_protocol(gc);
637 if (protocol)
638 return purple_protocol_media_iface_get_caps(protocol, account, who);
639 #endif
640 return PURPLE_MEDIA_CAPS_NONE;
643 void
644 purple_protocol_got_media_caps(PurpleAccount *account, const char *name)
646 #ifdef USE_VV
647 GSList *list;
649 g_return_if_fail(account != NULL);
650 g_return_if_fail(name != NULL);
652 if ((list = purple_blist_find_buddies(account, name)) == NULL)
653 return;
655 while (list) {
656 PurpleBuddy *buddy = list->data;
657 PurpleMediaCaps oldcaps = purple_buddy_get_media_caps(buddy);
658 PurpleMediaCaps newcaps = 0;
659 const gchar *bname = purple_buddy_get_name(buddy);
660 list = g_slist_delete_link(list, list);
663 newcaps = purple_protocol_get_media_caps(account, bname);
664 purple_buddy_set_media_caps(buddy, newcaps);
666 if (oldcaps == newcaps)
667 continue;
669 purple_signal_emit(purple_blist_get_handle(),
670 "buddy-caps-changed", buddy,
671 newcaps, oldcaps);
673 #endif
676 gssize
677 purple_protocol_get_max_message_size(PurpleProtocol *protocol)
679 g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), 0);
681 return purple_protocol_client_iface_get_max_message_size(protocol, NULL);
684 /**************************************************************************/
685 /* Protocol Action API */
686 /**************************************************************************/
688 PurpleProtocolAction *
689 purple_protocol_action_new(const char* label,
690 PurpleProtocolActionCallback callback)
692 PurpleProtocolAction *action;
694 g_return_val_if_fail(label != NULL && callback != NULL, NULL);
696 action = g_new0(PurpleProtocolAction, 1);
698 action->label = g_strdup(label);
699 action->callback = callback;
701 return action;
704 void
705 purple_protocol_action_free(PurpleProtocolAction *action)
707 g_return_if_fail(action != NULL);
709 g_free(action->label);
710 g_free(action);
713 /**************************************************************************
714 * GBoxed code for PurpleProtocolAction
715 **************************************************************************/
717 static PurpleProtocolAction *
718 purple_protocol_action_copy(PurpleProtocolAction *action)
720 g_return_val_if_fail(action != NULL, NULL);
722 return purple_protocol_action_new(action->label, action->callback);
725 GType
726 purple_protocol_action_get_type(void)
728 static GType type = 0;
730 if (type == 0) {
731 type = g_boxed_type_register_static("PurpleProtocolAction",
732 (GBoxedCopyFunc)purple_protocol_action_copy,
733 (GBoxedFreeFunc)purple_protocol_action_free);
736 return type;
739 /**************************************************************************
740 * Protocols API
741 **************************************************************************/
743 * Negative if a before b, 0 if equal, positive if a after b.
745 static gint
746 compare_protocol(PurpleProtocol *a, PurpleProtocol *b)
748 const gchar *aname = purple_protocol_get_name(a);
749 const gchar *bname = purple_protocol_get_name(b);
751 if (aname) {
752 if (bname)
753 return strcmp(aname, bname);
754 else
755 return -1;
756 } else {
757 if (bname)
758 return 1;
759 else
760 return 0;
764 PurpleProtocol *
765 purple_protocols_find(const char *id)
767 g_return_val_if_fail(protocols != NULL && id != NULL, NULL);
769 return g_hash_table_lookup(protocols, id);
772 PurpleProtocol *
773 purple_protocols_add(GType protocol_type, GError **error)
775 PurpleProtocol *protocol;
776 PurpleProtocolClass *klass;
778 if (protocol_type == G_TYPE_INVALID) {
779 g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
780 _("Protocol type is not registered"));
781 return NULL;
784 if (!g_type_is_a(protocol_type, PURPLE_TYPE_PROTOCOL)) {
785 g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
786 _("Protocol type does not inherit PurpleProtocol"));
787 return NULL;
790 if (G_TYPE_IS_ABSTRACT(protocol_type)) {
791 g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
792 _("Protocol type is abstract"));
793 return NULL;
796 protocol = g_object_new(protocol_type, NULL);
797 klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
799 if (!protocol) {
800 g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
801 _("Could not create protocol instance"));
802 return NULL;
805 if (!purple_protocol_get_id(protocol)) {
806 g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
807 _("Protocol does not provide an ID"));
809 g_object_unref(protocol);
810 return NULL;
813 if (purple_protocols_find(purple_protocol_get_id(protocol))) {
814 g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
815 _("A protocol with the ID %s is already added."),
816 purple_protocol_get_id(protocol));
818 g_object_unref(protocol);
819 return NULL;
822 /* Make sure the protocol implements the required functions */
823 if (!klass->login || !klass->close ||
824 !klass->status_types || !klass->list_icon )
826 g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
827 _("Protocol %s does not implement all the functions in "
828 "PurpleProtocolClass"), purple_protocol_get_id(protocol));
830 g_object_unref(protocol);
831 return NULL;
834 g_hash_table_insert(protocols, g_strdup(purple_protocol_get_id(protocol)),
835 protocol);
837 purple_debug_info("protocols", "Added protocol %s\n",
838 purple_protocol_get_id(protocol));
840 purple_signal_emit(purple_protocols_get_handle(), "protocol-added",
841 protocol);
842 return protocol;
845 gboolean purple_protocols_remove(PurpleProtocol *protocol, GError **error)
847 g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), FALSE);
848 g_return_val_if_fail(purple_protocol_get_id(protocol) != NULL, FALSE);
850 if (purple_protocols_find(purple_protocol_get_id(protocol)) == NULL) {
851 g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
852 _("Protocol %s is not added."),
853 purple_protocol_get_id(protocol));
855 return FALSE;
858 purple_debug_info("protocols", "Removing protocol %s\n",
859 purple_protocol_get_id(protocol));
861 purple_signal_emit(purple_protocols_get_handle(), "protocol-removed",
862 protocol);
864 g_hash_table_remove(protocols, purple_protocol_get_id(protocol));
865 return TRUE;
868 GList *
869 purple_protocols_get_all(void)
871 GList *ret = NULL;
872 PurpleProtocol *protocol;
873 GHashTableIter iter;
875 g_hash_table_iter_init(&iter, protocols);
876 while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&protocol))
877 ret = g_list_insert_sorted(ret, protocol, (GCompareFunc)compare_protocol);
879 return ret;
882 /**************************************************************************
883 * Protocols Subsystem API
884 **************************************************************************/
885 void
886 purple_protocols_init(void)
888 void *handle = purple_protocols_get_handle();
890 protocols = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
891 (GDestroyNotify)g_object_unref);
893 purple_signal_register(handle, "protocol-added",
894 purple_marshal_VOID__POINTER,
895 G_TYPE_NONE, 1, PURPLE_TYPE_PROTOCOL);
896 purple_signal_register(handle, "protocol-removed",
897 purple_marshal_VOID__POINTER,
898 G_TYPE_NONE, 1, PURPLE_TYPE_PROTOCOL);
901 void *
902 purple_protocols_get_handle(void)
904 static int handle;
906 return &handle;
909 void
910 purple_protocols_uninit(void)
912 g_hash_table_destroy(protocols);