Use GSList functions instead of manual iterations
[pidgin-git.git] / libpurple / protocols / bonjour / bonjour.c
blob1e4240cc45ddb32db11a078faca2eafa0614a7d4
1 /*
2 * purple - Bonjour Protocol Plugin
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
22 #include <glib.h>
23 #ifndef _WIN32
24 #include <pwd.h>
25 #else
26 #define UNICODE
27 #include <winsock2.h>
28 #include <windows.h>
29 #include <lm.h>
30 #include "dns_sd_proxy.h"
31 #endif
33 #include "internal.h"
34 #include <purple.h>
36 #include "bonjour.h"
37 #include "mdns_common.h"
38 #include "jabber.h"
39 #include "buddy.h"
40 #include "bonjour_ft.h"
42 static PurpleProtocol *my_protocol = NULL;
44 static char *default_firstname;
45 static char *default_lastname;
47 const char *
48 bonjour_get_jid(PurpleAccount *account)
50 PurpleConnection *conn = purple_account_get_connection(account);
51 BonjourData *bd = purple_connection_get_protocol_data(conn);
52 return bd->jid;
55 static void
56 bonjour_removeallfromlocal(PurpleConnection *conn, PurpleGroup *bonjour_group)
58 PurpleAccount *account = purple_connection_get_account(conn);
59 PurpleBlistNode *cnode, *cnodenext, *bnode, *bnodenext;
60 PurpleBuddy *buddy;
62 if (bonjour_group == NULL)
63 return;
65 /* Go through and remove all buddies that belong to this account */
66 for (cnode = purple_blist_node_get_first_child((PurpleBlistNode *) bonjour_group); cnode; cnode = cnodenext) {
67 cnodenext = purple_blist_node_get_sibling_next(cnode);
68 if (!PURPLE_IS_CONTACT(cnode))
69 continue;
70 for (bnode = purple_blist_node_get_first_child(cnode); bnode; bnode = bnodenext) {
71 bnodenext = purple_blist_node_get_sibling_next(bnode);
72 if (!PURPLE_IS_BUDDY(bnode))
73 continue;
74 buddy = (PurpleBuddy *) bnode;
75 if (purple_buddy_get_account(buddy) != account)
76 continue;
77 purple_account_remove_buddy(account, buddy, NULL);
78 purple_blist_remove_buddy(buddy);
84 static void
85 bonjour_login(PurpleAccount *account)
87 PurpleConnection *gc = purple_account_get_connection(account);
88 BonjourData *bd;
89 PurpleStatus *status;
90 PurplePresence *presence;
92 #ifdef _WIN32
93 if (!dns_sd_available()) {
94 purple_connection_error(gc,
95 PURPLE_CONNECTION_ERROR_OTHER_ERROR,
96 _("Unable to find Apple's \"Bonjour for Windows\" toolkit, see "
97 "https://developer.pidgin.im/BonjourWindows for more information."));
98 return;
100 #endif /* _WIN32 */
102 purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_HTML |
103 PURPLE_CONNECTION_FLAG_NO_IMAGES);
104 bd = g_new0(BonjourData, 1);
105 purple_connection_set_protocol_data(gc, bd);
107 /* Start waiting for jabber connections (iChat style) */
108 bd->jabber_data = g_new0(BonjourJabber, 1);
109 bd->jabber_data->port = purple_account_get_int(account, "port", BONJOUR_DEFAULT_PORT);
110 bd->jabber_data->account = account;
112 if (bonjour_jabber_start(bd->jabber_data) == -1) {
113 /* Send a message about the connection error */
114 purple_connection_error (gc,
115 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
116 _("Unable to listen for incoming IM connections"));
117 return;
120 /* Connect to the mDNS daemon looking for buddies in the LAN */
121 bd->dns_sd_data = bonjour_dns_sd_new();
122 bd->dns_sd_data->first = g_strdup(purple_account_get_string(account, "first", default_firstname));
123 bd->dns_sd_data->last = g_strdup(purple_account_get_string(account, "last", default_lastname));
124 bd->dns_sd_data->port_p2pj = bd->jabber_data->port;
125 /* Not engaged in AV conference */
126 bd->dns_sd_data->vc = g_strdup("!");
128 status = purple_account_get_active_status(account);
129 presence = purple_account_get_presence(account);
130 if (purple_presence_is_available(presence))
131 bd->dns_sd_data->status = g_strdup("avail");
132 else if (purple_presence_is_idle(presence))
133 bd->dns_sd_data->status = g_strdup("away");
134 else
135 bd->dns_sd_data->status = g_strdup("dnd");
136 bd->dns_sd_data->msg = g_strdup(purple_status_get_attr_string(status, "message"));
138 bd->dns_sd_data->account = account;
139 if (!bonjour_dns_sd_start(bd->dns_sd_data))
141 purple_connection_error (gc,
142 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
143 _("Unable to establish connection with the local mDNS server. Is it running?"));
144 return;
147 bonjour_dns_sd_update_buddy_icon(bd->dns_sd_data);
149 /* Show the buddy list by telling Purple we have already connected */
150 purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
153 static void
154 bonjour_close(PurpleConnection *connection)
156 PurpleGroup *bonjour_group;
157 BonjourData *bd = purple_connection_get_protocol_data(connection);
159 bonjour_group = purple_blist_find_group(BONJOUR_GROUP_NAME);
161 /* Remove all the bonjour buddies */
162 bonjour_removeallfromlocal(connection, bonjour_group);
164 /* Stop looking for buddies in the LAN */
165 if (bd != NULL && bd->dns_sd_data != NULL)
167 bonjour_dns_sd_stop(bd->dns_sd_data);
168 bonjour_dns_sd_free(bd->dns_sd_data);
171 if (bd != NULL && bd->jabber_data != NULL)
173 /* Stop waiting for conversations */
174 bonjour_jabber_stop(bd->jabber_data);
175 g_free(bd->jabber_data);
178 /* Delete the bonjour group
179 * (purple_blist_remove_group will bail out if the group isn't empty)
181 if (bonjour_group != NULL)
182 purple_blist_remove_group(bonjour_group);
184 /* Cancel any file transfers */
185 while (bd != NULL && bd->xfer_lists) {
186 purple_xfer_cancel_local(bd->xfer_lists->data);
189 if (bd != NULL)
190 g_free(bd->jid);
191 g_free(bd);
192 purple_connection_set_protocol_data(connection, NULL);
195 static const char *
196 bonjour_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
198 return BONJOUR_ICON_NAME;
201 static int
202 bonjour_send_im(PurpleConnection *connection, PurpleMessage *msg)
204 BonjourData *bd = purple_connection_get_protocol_data(connection);
206 if (purple_message_is_empty(msg) || !purple_message_get_recipient(msg))
207 return 0;
209 return bonjour_jabber_send_message(bd->jabber_data,
210 purple_message_get_recipient(msg),
211 purple_message_get_contents(msg));
214 static void
215 bonjour_set_status(PurpleAccount *account, PurpleStatus *status)
217 PurpleConnection *gc;
218 BonjourData *bd;
219 PurplePresence *presence;
220 const char *message, *bonjour_status;
221 gchar *stripped;
223 gc = purple_account_get_connection(account);
224 bd = purple_connection_get_protocol_data(gc);
225 presence = purple_account_get_presence(account);
227 message = purple_status_get_attr_string(status, "message");
228 if (message == NULL)
229 message = "";
230 stripped = purple_markup_strip_html(message);
233 * The three possible status for Bonjour are
234 * -available ("avail")
235 * -idle ("away")
236 * -away ("dnd")
237 * Each of them can have an optional message.
239 if (purple_presence_is_available(presence))
240 bonjour_status = "avail";
241 else if (purple_presence_is_idle(presence))
242 bonjour_status = "away";
243 else
244 bonjour_status = "dnd";
246 bonjour_dns_sd_send_status(bd->dns_sd_data, bonjour_status, stripped);
247 g_free(stripped);
251 * The add_buddy callback removes the buddy from the local list.
252 * Bonjour manages buddies for you, and adding someone locally by
253 * hand is stupid. Perhaps we should change libpurple not to allow adding
254 * if there is no add_buddy callback.
256 static void
257 bonjour_fake_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char *message) {
258 purple_debug_error("bonjour", "Buddy '%s' manually added; removing. "
259 "Bonjour buddies must be discovered and not manually added.\n",
260 purple_buddy_get_name(buddy));
262 /* I suppose we could alert the user here, but it seems unnecessary. */
264 /* If this causes problems, it can be moved to an idle callback */
265 purple_blist_remove_buddy(buddy);
269 static void bonjour_remove_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) {
270 BonjourBuddy *bb = purple_buddy_get_protocol_data(buddy);
271 if (bb) {
272 bonjour_buddy_delete(bb);
273 purple_buddy_set_protocol_data(buddy, NULL);
277 static GList *
278 bonjour_status_types(PurpleAccount *account)
280 GList *status_types = NULL;
281 PurpleStatusType *type;
283 g_return_val_if_fail(account != NULL, NULL);
285 type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
286 BONJOUR_STATUS_ID_AVAILABLE,
287 NULL, TRUE, TRUE, FALSE,
288 "message", _("Message"),
289 purple_value_new(G_TYPE_STRING), NULL);
290 status_types = g_list_append(status_types, type);
292 type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
293 BONJOUR_STATUS_ID_AWAY,
294 NULL, TRUE, TRUE, FALSE,
295 "message", _("Message"),
296 purple_value_new(G_TYPE_STRING), NULL);
297 status_types = g_list_append(status_types, type);
299 type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
300 BONJOUR_STATUS_ID_OFFLINE,
301 NULL, TRUE, TRUE, FALSE);
302 status_types = g_list_append(status_types, type);
304 return status_types;
307 static void
308 bonjour_convo_closed(PurpleConnection *connection, const char *who)
310 PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(connection), who);
311 BonjourBuddy *bb;
313 if (buddy == NULL || (bb = purple_buddy_get_protocol_data(buddy)) == NULL)
316 * This buddy is not in our buddy list, and therefore does not really
317 * exist, so we won't have any data about them.
319 return;
322 bonjour_jabber_close_conversation(bb->conversation);
323 bb->conversation = NULL;
326 static void
327 bonjour_set_buddy_icon(PurpleConnection *conn, PurpleImage *img)
329 BonjourData *bd = purple_connection_get_protocol_data(conn);
330 bonjour_dns_sd_update_buddy_icon(bd->dns_sd_data);
334 static char *
335 bonjour_status_text(PurpleBuddy *buddy)
337 PurplePresence *presence;
338 PurpleStatus *status;
339 const char *message;
340 gchar *ret = NULL;
342 presence = purple_buddy_get_presence(buddy);
343 status = purple_presence_get_active_status(presence);
345 message = purple_status_get_attr_string(status, "message");
347 if (message != NULL) {
348 ret = g_markup_escape_text(message, -1);
349 purple_util_chrreplace(ret, '\n', ' ');
352 return ret;
355 static void
356 bonjour_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full)
358 PurplePresence *presence;
359 PurpleStatus *status;
360 BonjourBuddy *bb = purple_buddy_get_protocol_data(buddy);
361 const char *status_description;
362 const char *message;
364 presence = purple_buddy_get_presence(buddy);
365 status = purple_presence_get_active_status(presence);
366 message = purple_status_get_attr_string(status, "message");
368 if (purple_presence_is_available(presence))
369 status_description = purple_status_get_name(status);
370 else if (purple_presence_is_idle(presence))
371 status_description = _("Idle");
372 else
373 status_description = purple_status_get_name(status);
375 purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status_description);
376 if (message != NULL) {
377 /* TODO: Check whether it's correct to call add_pair_html,
378 or if we should be using add_pair_plaintext */
379 purple_notify_user_info_add_pair_html(user_info, _("Message"), message);
382 if (bb == NULL) {
383 purple_debug_error("bonjour", "Got tooltip request for a buddy without protocol data.\n");
384 return;
387 /* Only show first/last name if there is a nickname set (to avoid duplication) */
388 if (bb->nick != NULL && *bb->nick != '\0') {
389 if (bb->first != NULL && *bb->first != '\0') {
390 /* TODO: Check whether it's correct to call add_pair_html,
391 or if we should be using add_pair_plaintext */
392 purple_notify_user_info_add_pair_html(user_info, _("First name"), bb->first);
394 if (bb->last != NULL && *bb->last != '\0') {
395 /* TODO: Check whether it's correct to call add_pair_html,
396 or if we should be using add_pair_plaintext */
397 purple_notify_user_info_add_pair_html(user_info, _("Last name"), bb->last);
401 if (bb->email != NULL && *bb->email != '\0') {
402 /* TODO: Check whether it's correct to call add_pair_html,
403 or if we should be using add_pair_plaintext */
404 purple_notify_user_info_add_pair_html(user_info, _("Email"), bb->email);
407 if (bb->AIM != NULL && *bb->AIM != '\0') {
408 /* TODO: Check whether it's correct to call add_pair_html,
409 or if we should be using add_pair_plaintext */
410 purple_notify_user_info_add_pair_html(user_info, _("AIM Account"), bb->AIM);
413 if (bb->jid != NULL && *bb->jid != '\0') {
414 /* TODO: Check whether it's correct to call add_pair_html,
415 or if we should be using add_pair_plaintext */
416 purple_notify_user_info_add_pair_html(user_info, _("XMPP Account"), bb->jid);
420 static void
421 bonjour_do_group_change(PurpleBuddy *buddy, const char *new_group)
423 if (buddy == NULL)
424 return;
426 /* If we're moving them out of the bonjour group, make them persistent */
427 if (purple_strequal(new_group, BONJOUR_GROUP_NAME))
428 purple_blist_node_set_transient(PURPLE_BLIST_NODE(buddy), TRUE);
429 else
430 purple_blist_node_set_transient(PURPLE_BLIST_NODE(buddy),
431 !purple_blist_node_is_transient(PURPLE_BLIST_NODE(buddy)));
435 static void
436 bonjour_group_buddy(PurpleConnection *connection, const char *who, const char *old_group, const char *new_group)
438 PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(connection), who);
440 bonjour_do_group_change(buddy, new_group);
444 static void
445 bonjour_rename_group(PurpleConnection *connection, const char *old_name, PurpleGroup *group, GList *moved_buddies)
447 const char *new_group;
449 new_group = purple_group_get_name(group);
451 g_list_foreach(moved_buddies, (GFunc)bonjour_do_group_change, new_group);
454 static gboolean
455 bonjour_can_receive_file(PurpleProtocolXfer *prplxfer, PurpleConnection *connection, const char *who)
457 PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(connection), who);
459 return (buddy != NULL && purple_buddy_get_protocol_data(buddy) != NULL);
462 static gssize
463 bonjour_get_max_message_size(PurpleConversation *conv)
465 return -1; /* 5MB successfully tested. */
468 #ifdef WIN32
469 static gboolean
470 _set_default_name_cb(gpointer data) {
471 gchar *fullname = data;
472 const char *splitpoint;
473 GList *tmp = my_protocol->account_options;
474 PurpleAccountOption *option;
476 if (!fullname) {
477 purple_debug_info("bonjour", "Unable to look up First and Last name or Username from system; using defaults.\n");
478 return FALSE;
481 g_free(default_firstname);
482 g_free(default_lastname);
484 /* Split the real name into a first and last name */
485 splitpoint = strchr(fullname, ' ');
486 if (splitpoint != NULL) {
487 default_firstname = g_strndup(fullname, splitpoint - fullname);
488 default_lastname = g_strdup(&splitpoint[1]);
489 } else {
490 default_firstname = g_strdup(fullname);
491 default_lastname = g_strdup("");
493 g_free(fullname);
496 for(; tmp != NULL; tmp = tmp->next) {
497 option = tmp->data;
498 if (purple_strequal("first", purple_account_option_get_setting(option)))
499 purple_account_option_set_default_string(option, default_firstname);
500 else if (purple_strequal("last", purple_account_option_get_setting(option)))
501 purple_account_option_set_default_string(option, default_lastname);
504 return FALSE;
507 static gpointer
508 _win32_name_lookup_thread(gpointer data) {
509 gchar *fullname = NULL;
510 wchar_t username[UNLEN + 1];
511 DWORD dwLenUsername = UNLEN + 1;
513 GetUserNameW((LPWSTR) &username, &dwLenUsername);
515 if (username != NULL && *username != '\0') {
516 LPBYTE servername = NULL;
517 LPBYTE info = NULL;
519 NetGetDCName(NULL, NULL, &servername);
521 /* purple_debug_info("bonjour", "Looking up the full name from the %s.\n", (servername ? "domain controller" : "local machine")); */
523 if (NetUserGetInfo((LPCWSTR) servername, username, 10, &info) == NERR_Success
524 && info != NULL && ((LPUSER_INFO_10) info)->usri10_full_name != NULL
525 && *(((LPUSER_INFO_10) info)->usri10_full_name) != '\0') {
526 fullname = g_utf16_to_utf8(
527 ((LPUSER_INFO_10) info)->usri10_full_name,
528 -1, NULL, NULL, NULL);
530 /* Fall back to the local machine if we didn't get the full name from the domain controller */
531 else if (servername != NULL) {
532 /* purple_debug_info("bonjour", "Looking up the full name from the local machine"); */
534 if (info != NULL) NetApiBufferFree(info);
535 info = NULL;
537 if (NetUserGetInfo(NULL, username, 10, &info) == NERR_Success
538 && info != NULL && ((LPUSER_INFO_10) info)->usri10_full_name != NULL
539 && *(((LPUSER_INFO_10) info)->usri10_full_name) != '\0') {
540 fullname = g_utf16_to_utf8(
541 ((LPUSER_INFO_10) info)->usri10_full_name,
542 -1, NULL, NULL, NULL);
546 if (info != NULL) NetApiBufferFree(info);
547 if (servername != NULL) NetApiBufferFree(servername);
549 if (!fullname)
550 fullname = g_utf16_to_utf8(username, -1, NULL, NULL, NULL);
553 g_timeout_add(0, _set_default_name_cb, fullname);
555 return NULL;
557 #endif
559 static void
560 initialize_default_account_values(void)
562 #ifndef _WIN32
563 struct passwd *info;
564 #else
565 GThread *lookup_thread;
566 #endif
567 const char *fullname = NULL, *splitpoint, *tmp;
568 gchar *conv = NULL;
570 #ifndef _WIN32
571 /* Try to figure out the user's real name */
572 info = getpwuid(getuid());
573 if ((info != NULL) && (info->pw_gecos != NULL) && (info->pw_gecos[0] != '\0'))
574 fullname = info->pw_gecos;
575 else if ((info != NULL) && (info->pw_name != NULL) && (info->pw_name[0] != '\0'))
576 fullname = info->pw_name;
577 else if (((fullname = getlogin()) != NULL) && (fullname[0] == '\0'))
578 fullname = NULL;
579 #else
580 /* The Win32 username lookup functions are synchronous so we do it in a thread */
581 lookup_thread = g_thread_try_new("bonjour dns thread", _win32_name_lookup_thread, NULL, NULL);
582 if (lookup_thread)
583 g_thread_unref(lookup_thread);
584 else
585 purple_debug_fatal("bonjour", "failed to create lookup thread\n");
586 #endif
588 /* Make sure fullname is valid UTF-8. If not, try to convert it. */
589 if (fullname != NULL && !g_utf8_validate(fullname, -1, NULL)) {
590 fullname = conv = g_locale_to_utf8(fullname, -1, NULL, NULL, NULL);
591 if (conv == NULL || *conv == '\0')
592 fullname = NULL;
595 if (fullname == NULL)
596 fullname = _("Purple Person");
598 /* Split the real name into a first and last name */
599 splitpoint = strchr(fullname, ' ');
600 if (splitpoint != NULL) {
601 default_firstname = g_strndup(fullname, splitpoint - fullname);
602 tmp = &splitpoint[1];
604 /* The last name may be followed by a comma and additional data.
605 * Only use the last name itself.
607 splitpoint = strchr(tmp, ',');
608 if (splitpoint != NULL)
609 default_lastname = g_strndup(tmp, splitpoint - tmp);
610 else
611 default_lastname = g_strdup(tmp);
612 } else {
613 default_firstname = g_strdup(fullname);
614 default_lastname = g_strdup("");
617 g_free(conv);
620 static void
621 bonjour_protocol_init(BonjourProtocol *self)
623 PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
624 PurpleAccountOption *option;
626 protocol->id = "prpl-bonjour";
627 protocol->name = "Bonjour";
628 protocol->options = OPT_PROTO_NO_PASSWORD;
629 protocol->icon_spec = purple_buddy_icon_spec_new("png,gif,jpeg",
630 0, 0, 96, 96, 65535,
631 PURPLE_ICON_SCALE_DISPLAY);
633 /* Creating the options for the protocol */
634 option = purple_account_option_int_new(_("Local Port"), "port", BONJOUR_DEFAULT_PORT);
635 protocol->account_options = g_list_append(protocol->account_options, option);
637 option = purple_account_option_string_new(_("First name"), "first", default_firstname);
638 protocol->account_options = g_list_append(protocol->account_options, option);
640 option = purple_account_option_string_new(_("Last name"), "last", default_lastname);
641 protocol->account_options = g_list_append(protocol->account_options, option);
643 option = purple_account_option_string_new(_("Email"), "email", "");
644 protocol->account_options = g_list_append(protocol->account_options, option);
646 option = purple_account_option_string_new(_("AIM Account"), "AIM", "");
647 protocol->account_options = g_list_append(protocol->account_options, option);
649 option = purple_account_option_string_new(_("XMPP Account"), "jid", "");
650 protocol->account_options = g_list_append(protocol->account_options, option);
653 static void
654 bonjour_protocol_class_init(BonjourProtocolClass *klass)
656 PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
658 protocol_class->login = bonjour_login;
659 protocol_class->close = bonjour_close;
660 protocol_class->status_types = bonjour_status_types;
661 protocol_class->list_icon = bonjour_list_icon;
664 static void
665 bonjour_protocol_class_finalize(G_GNUC_UNUSED BonjourProtocolClass *klass)
669 static void
670 bonjour_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
672 client_iface->status_text = bonjour_status_text;
673 client_iface->tooltip_text = bonjour_tooltip_text;
674 client_iface->convo_closed = bonjour_convo_closed;
675 client_iface->get_max_message_size = bonjour_get_max_message_size;
678 static void
679 bonjour_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
681 server_iface->add_buddy = bonjour_fake_add_buddy;
682 server_iface->remove_buddy = bonjour_remove_buddy;
683 server_iface->group_buddy = bonjour_group_buddy;
684 server_iface->rename_group = bonjour_rename_group;
685 server_iface->set_buddy_icon = bonjour_set_buddy_icon;
686 server_iface->set_status = bonjour_set_status;
689 static void
690 bonjour_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
692 im_iface->send = bonjour_send_im;
695 static void
696 bonjour_protocol_xfer_iface_init(PurpleProtocolXferInterface *xfer_iface)
698 xfer_iface->can_receive = bonjour_can_receive_file;
699 xfer_iface->send_file = bonjour_send_file;
700 xfer_iface->new_xfer = bonjour_new_xfer;
703 G_DEFINE_DYNAMIC_TYPE_EXTENDED(
704 BonjourProtocol, bonjour_protocol, PURPLE_TYPE_PROTOCOL, 0,
706 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
707 bonjour_protocol_client_iface_init)
709 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
710 bonjour_protocol_server_iface_init)
712 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
713 bonjour_protocol_im_iface_init)
715 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
716 bonjour_protocol_xfer_iface_init));
718 static PurplePluginInfo *
719 plugin_query(GError **error)
721 return purple_plugin_info_new(
722 "id", "prpl-bonjour",
723 "name", "Bonjour Protocol",
724 "version", DISPLAY_VERSION,
725 "category", N_("Protocol"),
726 "summary", N_("Bonjour Protocol Plugin"),
727 "description", N_("Bonjour Protocol Plugin"),
728 "website", PURPLE_WEBSITE,
729 "abi-version", PURPLE_ABI_VERSION,
730 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
731 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
732 NULL
736 static gboolean
737 plugin_load(PurplePlugin *plugin, GError **error)
739 bonjour_protocol_register_type(G_TYPE_MODULE(plugin));
741 xep_xfer_register(G_TYPE_MODULE(plugin));
743 my_protocol = purple_protocols_add(BONJOUR_TYPE_PROTOCOL, error);
744 if (!my_protocol)
745 return FALSE;
747 initialize_default_account_values();
749 return TRUE;
752 static gboolean
753 plugin_unload(PurplePlugin *plugin, GError **error)
755 if (!purple_protocols_remove(my_protocol, error))
756 return FALSE;
758 g_free(default_firstname);
759 g_free(default_lastname);
761 return TRUE;
764 PURPLE_PLUGIN_INIT(bonjour, plugin_query, plugin_load, plugin_unload);