mark PurpleImageClass as private
[pidgin-git.git] / libpurple / protocols / bonjour / bonjour.c
bloba6a19cd886eff8805bd7a9a8b54ebcc41fe967fd
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"
35 #include "account.h"
36 #include "accountopt.h"
37 #include "debug.h"
38 #include "plugins.h"
39 #include "util.h"
40 #include "version.h"
42 #include "bonjour.h"
43 #include "mdns_common.h"
44 #include "jabber.h"
45 #include "buddy.h"
46 #include "bonjour_ft.h"
48 static PurpleProtocol *my_protocol = NULL;
50 static char *default_firstname;
51 static char *default_lastname;
53 const char *
54 bonjour_get_jid(PurpleAccount *account)
56 PurpleConnection *conn = purple_account_get_connection(account);
57 BonjourData *bd = purple_connection_get_protocol_data(conn);
58 return bd->jid;
61 static void
62 bonjour_removeallfromlocal(PurpleConnection *conn, PurpleGroup *bonjour_group)
64 PurpleAccount *account = purple_connection_get_account(conn);
65 PurpleBlistNode *cnode, *cnodenext, *bnode, *bnodenext;
66 PurpleBuddy *buddy;
68 if (bonjour_group == NULL)
69 return;
71 /* Go through and remove all buddies that belong to this account */
72 for (cnode = purple_blist_node_get_first_child((PurpleBlistNode *) bonjour_group); cnode; cnode = cnodenext) {
73 cnodenext = purple_blist_node_get_sibling_next(cnode);
74 if (!PURPLE_IS_CONTACT(cnode))
75 continue;
76 for (bnode = purple_blist_node_get_first_child(cnode); bnode; bnode = bnodenext) {
77 bnodenext = purple_blist_node_get_sibling_next(bnode);
78 if (!PURPLE_IS_BUDDY(bnode))
79 continue;
80 buddy = (PurpleBuddy *) bnode;
81 if (purple_buddy_get_account(buddy) != account)
82 continue;
83 purple_account_remove_buddy(account, buddy, NULL);
84 purple_blist_remove_buddy(buddy);
90 static void
91 bonjour_login(PurpleAccount *account)
93 PurpleConnection *gc = purple_account_get_connection(account);
94 BonjourData *bd;
95 PurpleStatus *status;
96 PurplePresence *presence;
98 #ifdef _WIN32
99 if (!dns_sd_available()) {
100 purple_connection_error(gc,
101 PURPLE_CONNECTION_ERROR_OTHER_ERROR,
102 _("Unable to find Apple's \"Bonjour for Windows\" toolkit, see "
103 "https://developer.pidgin.im/BonjourWindows for more information."));
104 return;
106 #endif /* _WIN32 */
108 purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_HTML |
109 PURPLE_CONNECTION_FLAG_NO_IMAGES);
110 bd = g_new0(BonjourData, 1);
111 purple_connection_set_protocol_data(gc, bd);
113 /* Start waiting for jabber connections (iChat style) */
114 bd->jabber_data = g_new0(BonjourJabber, 1);
115 bd->jabber_data->port = purple_account_get_int(account, "port", BONJOUR_DEFAULT_PORT);
116 bd->jabber_data->account = account;
118 if (bonjour_jabber_start(bd->jabber_data) == -1) {
119 /* Send a message about the connection error */
120 purple_connection_error (gc,
121 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
122 _("Unable to listen for incoming IM connections"));
123 return;
126 /* Connect to the mDNS daemon looking for buddies in the LAN */
127 bd->dns_sd_data = bonjour_dns_sd_new();
128 bd->dns_sd_data->first = g_strdup(purple_account_get_string(account, "first", default_firstname));
129 bd->dns_sd_data->last = g_strdup(purple_account_get_string(account, "last", default_lastname));
130 bd->dns_sd_data->port_p2pj = bd->jabber_data->port;
131 /* Not engaged in AV conference */
132 bd->dns_sd_data->vc = g_strdup("!");
134 status = purple_account_get_active_status(account);
135 presence = purple_account_get_presence(account);
136 if (purple_presence_is_available(presence))
137 bd->dns_sd_data->status = g_strdup("avail");
138 else if (purple_presence_is_idle(presence))
139 bd->dns_sd_data->status = g_strdup("away");
140 else
141 bd->dns_sd_data->status = g_strdup("dnd");
142 bd->dns_sd_data->msg = g_strdup(purple_status_get_attr_string(status, "message"));
144 bd->dns_sd_data->account = account;
145 if (!bonjour_dns_sd_start(bd->dns_sd_data))
147 purple_connection_error (gc,
148 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
149 _("Unable to establish connection with the local mDNS server. Is it running?"));
150 return;
153 bonjour_dns_sd_update_buddy_icon(bd->dns_sd_data);
155 /* Show the buddy list by telling Purple we have already connected */
156 purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
159 static void
160 bonjour_close(PurpleConnection *connection)
162 PurpleGroup *bonjour_group;
163 BonjourData *bd = purple_connection_get_protocol_data(connection);
165 bonjour_group = purple_blist_find_group(BONJOUR_GROUP_NAME);
167 /* Remove all the bonjour buddies */
168 bonjour_removeallfromlocal(connection, bonjour_group);
170 /* Stop looking for buddies in the LAN */
171 if (bd != NULL && bd->dns_sd_data != NULL)
173 bonjour_dns_sd_stop(bd->dns_sd_data);
174 bonjour_dns_sd_free(bd->dns_sd_data);
177 if (bd != NULL && bd->jabber_data != NULL)
179 /* Stop waiting for conversations */
180 bonjour_jabber_stop(bd->jabber_data);
181 g_free(bd->jabber_data);
184 /* Delete the bonjour group
185 * (purple_blist_remove_group will bail out if the group isn't empty)
187 if (bonjour_group != NULL)
188 purple_blist_remove_group(bonjour_group);
190 /* Cancel any file transfers */
191 while (bd != NULL && bd->xfer_lists) {
192 purple_xfer_cancel_local(bd->xfer_lists->data);
195 if (bd != NULL)
196 g_free(bd->jid);
197 g_free(bd);
198 purple_connection_set_protocol_data(connection, NULL);
201 static const char *
202 bonjour_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
204 return BONJOUR_ICON_NAME;
207 static int
208 bonjour_send_im(PurpleConnection *connection, PurpleMessage *msg)
210 BonjourData *bd = purple_connection_get_protocol_data(connection);
212 if (purple_message_is_empty(msg) || !purple_message_get_recipient(msg))
213 return 0;
215 return bonjour_jabber_send_message(bd->jabber_data,
216 purple_message_get_recipient(msg),
217 purple_message_get_contents(msg));
220 static void
221 bonjour_set_status(PurpleAccount *account, PurpleStatus *status)
223 PurpleConnection *gc;
224 BonjourData *bd;
225 PurplePresence *presence;
226 const char *message, *bonjour_status;
227 gchar *stripped;
229 gc = purple_account_get_connection(account);
230 bd = purple_connection_get_protocol_data(gc);
231 presence = purple_account_get_presence(account);
233 message = purple_status_get_attr_string(status, "message");
234 if (message == NULL)
235 message = "";
236 stripped = purple_markup_strip_html(message);
239 * The three possible status for Bonjour are
240 * -available ("avail")
241 * -idle ("away")
242 * -away ("dnd")
243 * Each of them can have an optional message.
245 if (purple_presence_is_available(presence))
246 bonjour_status = "avail";
247 else if (purple_presence_is_idle(presence))
248 bonjour_status = "away";
249 else
250 bonjour_status = "dnd";
252 bonjour_dns_sd_send_status(bd->dns_sd_data, bonjour_status, stripped);
253 g_free(stripped);
257 * The add_buddy callback removes the buddy from the local list.
258 * Bonjour manages buddies for you, and adding someone locally by
259 * hand is stupid. Perhaps we should change libpurple not to allow adding
260 * if there is no add_buddy callback.
262 static void
263 bonjour_fake_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char *message) {
264 purple_debug_error("bonjour", "Buddy '%s' manually added; removing. "
265 "Bonjour buddies must be discovered and not manually added.\n",
266 purple_buddy_get_name(buddy));
268 /* I suppose we could alert the user here, but it seems unnecessary. */
270 /* If this causes problems, it can be moved to an idle callback */
271 purple_blist_remove_buddy(buddy);
275 static void bonjour_remove_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) {
276 BonjourBuddy *bb = purple_buddy_get_protocol_data(buddy);
277 if (bb) {
278 bonjour_buddy_delete(bb);
279 purple_buddy_set_protocol_data(buddy, NULL);
283 static GList *
284 bonjour_status_types(PurpleAccount *account)
286 GList *status_types = NULL;
287 PurpleStatusType *type;
289 g_return_val_if_fail(account != NULL, NULL);
291 type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
292 BONJOUR_STATUS_ID_AVAILABLE,
293 NULL, TRUE, TRUE, FALSE,
294 "message", _("Message"),
295 purple_value_new(G_TYPE_STRING), NULL);
296 status_types = g_list_append(status_types, type);
298 type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
299 BONJOUR_STATUS_ID_AWAY,
300 NULL, TRUE, TRUE, FALSE,
301 "message", _("Message"),
302 purple_value_new(G_TYPE_STRING), NULL);
303 status_types = g_list_append(status_types, type);
305 type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
306 BONJOUR_STATUS_ID_OFFLINE,
307 NULL, TRUE, TRUE, FALSE);
308 status_types = g_list_append(status_types, type);
310 return status_types;
313 static void
314 bonjour_convo_closed(PurpleConnection *connection, const char *who)
316 PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(connection), who);
317 BonjourBuddy *bb;
319 if (buddy == NULL || (bb = purple_buddy_get_protocol_data(buddy)) == NULL)
322 * This buddy is not in our buddy list, and therefore does not really
323 * exist, so we won't have any data about them.
325 return;
328 bonjour_jabber_close_conversation(bb->conversation);
329 bb->conversation = NULL;
332 static void
333 bonjour_set_buddy_icon(PurpleConnection *conn, PurpleImage *img)
335 BonjourData *bd = purple_connection_get_protocol_data(conn);
336 bonjour_dns_sd_update_buddy_icon(bd->dns_sd_data);
340 static char *
341 bonjour_status_text(PurpleBuddy *buddy)
343 PurplePresence *presence;
344 PurpleStatus *status;
345 const char *message;
346 gchar *ret = NULL;
348 presence = purple_buddy_get_presence(buddy);
349 status = purple_presence_get_active_status(presence);
351 message = purple_status_get_attr_string(status, "message");
353 if (message != NULL) {
354 ret = g_markup_escape_text(message, -1);
355 purple_util_chrreplace(ret, '\n', ' ');
358 return ret;
361 static void
362 bonjour_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full)
364 PurplePresence *presence;
365 PurpleStatus *status;
366 BonjourBuddy *bb = purple_buddy_get_protocol_data(buddy);
367 const char *status_description;
368 const char *message;
370 presence = purple_buddy_get_presence(buddy);
371 status = purple_presence_get_active_status(presence);
372 message = purple_status_get_attr_string(status, "message");
374 if (purple_presence_is_available(presence))
375 status_description = purple_status_get_name(status);
376 else if (purple_presence_is_idle(presence))
377 status_description = _("Idle");
378 else
379 status_description = purple_status_get_name(status);
381 purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status_description);
382 if (message != NULL) {
383 /* TODO: Check whether it's correct to call add_pair_html,
384 or if we should be using add_pair_plaintext */
385 purple_notify_user_info_add_pair_html(user_info, _("Message"), message);
388 if (bb == NULL) {
389 purple_debug_error("bonjour", "Got tooltip request for a buddy without protocol data.\n");
390 return;
393 /* Only show first/last name if there is a nickname set (to avoid duplication) */
394 if (bb->nick != NULL && *bb->nick != '\0') {
395 if (bb->first != NULL && *bb->first != '\0') {
396 /* TODO: Check whether it's correct to call add_pair_html,
397 or if we should be using add_pair_plaintext */
398 purple_notify_user_info_add_pair_html(user_info, _("First name"), bb->first);
400 if (bb->last != NULL && *bb->last != '\0') {
401 /* TODO: Check whether it's correct to call add_pair_html,
402 or if we should be using add_pair_plaintext */
403 purple_notify_user_info_add_pair_html(user_info, _("Last name"), bb->last);
407 if (bb->email != NULL && *bb->email != '\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, _("Email"), bb->email);
413 if (bb->AIM != NULL && *bb->AIM != '\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, _("AIM Account"), bb->AIM);
419 if (bb->jid != NULL && *bb->jid != '\0') {
420 /* TODO: Check whether it's correct to call add_pair_html,
421 or if we should be using add_pair_plaintext */
422 purple_notify_user_info_add_pair_html(user_info, _("XMPP Account"), bb->jid);
426 static void
427 bonjour_do_group_change(PurpleBuddy *buddy, const char *new_group)
429 if (buddy == NULL)
430 return;
432 /* If we're moving them out of the bonjour group, make them persistent */
433 if (purple_strequal(new_group, BONJOUR_GROUP_NAME))
434 purple_blist_node_set_transient(PURPLE_BLIST_NODE(buddy), TRUE);
435 else
436 purple_blist_node_set_transient(PURPLE_BLIST_NODE(buddy),
437 !purple_blist_node_is_transient(PURPLE_BLIST_NODE(buddy)));
441 static void
442 bonjour_group_buddy(PurpleConnection *connection, const char *who, const char *old_group, const char *new_group)
444 PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(connection), who);
446 bonjour_do_group_change(buddy, new_group);
450 static void
451 bonjour_rename_group(PurpleConnection *connection, const char *old_name, PurpleGroup *group, GList *moved_buddies)
453 GList *cur;
454 const char *new_group;
455 PurpleBuddy *buddy;
457 new_group = purple_group_get_name(group);
459 for (cur = moved_buddies; cur; cur = cur->next) {
460 buddy = cur->data;
461 bonjour_do_group_change(buddy, new_group);
466 static gboolean
467 bonjour_can_receive_file(PurpleProtocolXfer *prplxfer, PurpleConnection *connection, const char *who)
469 PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(connection), who);
471 return (buddy != NULL && purple_buddy_get_protocol_data(buddy) != NULL);
474 static gssize
475 bonjour_get_max_message_size(PurpleConversation *conv)
477 return -1; /* 5MB successfully tested. */
480 #ifdef WIN32
481 static gboolean
482 _set_default_name_cb(gpointer data) {
483 gchar *fullname = data;
484 const char *splitpoint;
485 GList *tmp = my_protocol->account_options;
486 PurpleAccountOption *option;
488 if (!fullname) {
489 purple_debug_info("bonjour", "Unable to look up First and Last name or Username from system; using defaults.\n");
490 return FALSE;
493 g_free(default_firstname);
494 g_free(default_lastname);
496 /* Split the real name into a first and last name */
497 splitpoint = strchr(fullname, ' ');
498 if (splitpoint != NULL) {
499 default_firstname = g_strndup(fullname, splitpoint - fullname);
500 default_lastname = g_strdup(&splitpoint[1]);
501 } else {
502 default_firstname = g_strdup(fullname);
503 default_lastname = g_strdup("");
505 g_free(fullname);
508 for(; tmp != NULL; tmp = tmp->next) {
509 option = tmp->data;
510 if (purple_strequal("first", purple_account_option_get_setting(option)))
511 purple_account_option_set_default_string(option, default_firstname);
512 else if (purple_strequal("last", purple_account_option_get_setting(option)))
513 purple_account_option_set_default_string(option, default_lastname);
516 return FALSE;
519 static gpointer
520 _win32_name_lookup_thread(gpointer data) {
521 gchar *fullname = NULL;
522 wchar_t username[UNLEN + 1];
523 DWORD dwLenUsername = UNLEN + 1;
525 GetUserNameW((LPWSTR) &username, &dwLenUsername);
527 if (username != NULL && *username != '\0') {
528 LPBYTE servername = NULL;
529 LPBYTE info = NULL;
531 NetGetDCName(NULL, NULL, &servername);
533 /* purple_debug_info("bonjour", "Looking up the full name from the %s.\n", (servername ? "domain controller" : "local machine")); */
535 if (NetUserGetInfo((LPCWSTR) servername, username, 10, &info) == NERR_Success
536 && info != NULL && ((LPUSER_INFO_10) info)->usri10_full_name != NULL
537 && *(((LPUSER_INFO_10) info)->usri10_full_name) != '\0') {
538 fullname = g_utf16_to_utf8(
539 ((LPUSER_INFO_10) info)->usri10_full_name,
540 -1, NULL, NULL, NULL);
542 /* Fall back to the local machine if we didn't get the full name from the domain controller */
543 else if (servername != NULL) {
544 /* purple_debug_info("bonjour", "Looking up the full name from the local machine"); */
546 if (info != NULL) NetApiBufferFree(info);
547 info = NULL;
549 if (NetUserGetInfo(NULL, username, 10, &info) == NERR_Success
550 && info != NULL && ((LPUSER_INFO_10) info)->usri10_full_name != NULL
551 && *(((LPUSER_INFO_10) info)->usri10_full_name) != '\0') {
552 fullname = g_utf16_to_utf8(
553 ((LPUSER_INFO_10) info)->usri10_full_name,
554 -1, NULL, NULL, NULL);
558 if (info != NULL) NetApiBufferFree(info);
559 if (servername != NULL) NetApiBufferFree(servername);
561 if (!fullname)
562 fullname = g_utf16_to_utf8(username, -1, NULL, NULL, NULL);
565 g_timeout_add(0, _set_default_name_cb, fullname);
567 return NULL;
569 #endif
571 static void
572 initialize_default_account_values(void)
574 #ifndef _WIN32
575 struct passwd *info;
576 #else
577 GThread *lookup_thread;
578 #endif
579 const char *fullname = NULL, *splitpoint, *tmp;
580 gchar *conv = NULL;
582 #ifndef _WIN32
583 /* Try to figure out the user's real name */
584 info = getpwuid(getuid());
585 if ((info != NULL) && (info->pw_gecos != NULL) && (info->pw_gecos[0] != '\0'))
586 fullname = info->pw_gecos;
587 else if ((info != NULL) && (info->pw_name != NULL) && (info->pw_name[0] != '\0'))
588 fullname = info->pw_name;
589 else if (((fullname = getlogin()) != NULL) && (fullname[0] == '\0'))
590 fullname = NULL;
591 #else
592 /* The Win32 username lookup functions are synchronous so we do it in a thread */
593 lookup_thread = g_thread_try_new("bonjour dns thread", _win32_name_lookup_thread, NULL, NULL);
594 if (lookup_thread)
595 g_thread_unref(lookup_thread);
596 else
597 purple_debug_fatal("bonjour", "failed to create lookup thread\n");
598 #endif
600 /* Make sure fullname is valid UTF-8. If not, try to convert it. */
601 if (fullname != NULL && !g_utf8_validate(fullname, -1, NULL)) {
602 fullname = conv = g_locale_to_utf8(fullname, -1, NULL, NULL, NULL);
603 if (conv == NULL || *conv == '\0')
604 fullname = NULL;
607 if (fullname == NULL)
608 fullname = _("Purple Person");
610 /* Split the real name into a first and last name */
611 splitpoint = strchr(fullname, ' ');
612 if (splitpoint != NULL) {
613 default_firstname = g_strndup(fullname, splitpoint - fullname);
614 tmp = &splitpoint[1];
616 /* The last name may be followed by a comma and additional data.
617 * Only use the last name itself.
619 splitpoint = strchr(tmp, ',');
620 if (splitpoint != NULL)
621 default_lastname = g_strndup(tmp, splitpoint - tmp);
622 else
623 default_lastname = g_strdup(tmp);
624 } else {
625 default_firstname = g_strdup(fullname);
626 default_lastname = g_strdup("");
629 g_free(conv);
632 static void
633 bonjour_protocol_init(PurpleProtocol *protocol)
635 PurpleAccountOption *option;
637 protocol->id = "prpl-bonjour";
638 protocol->name = "Bonjour";
639 protocol->options = OPT_PROTO_NO_PASSWORD;
640 protocol->icon_spec = purple_buddy_icon_spec_new("png,gif,jpeg",
641 0, 0, 96, 96, 65535,
642 PURPLE_ICON_SCALE_DISPLAY);
644 /* Creating the options for the protocol */
645 option = purple_account_option_int_new(_("Local Port"), "port", BONJOUR_DEFAULT_PORT);
646 protocol->account_options = g_list_append(protocol->account_options, option);
648 option = purple_account_option_string_new(_("First name"), "first", default_firstname);
649 protocol->account_options = g_list_append(protocol->account_options, option);
651 option = purple_account_option_string_new(_("Last name"), "last", default_lastname);
652 protocol->account_options = g_list_append(protocol->account_options, option);
654 option = purple_account_option_string_new(_("Email"), "email", "");
655 protocol->account_options = g_list_append(protocol->account_options, option);
657 option = purple_account_option_string_new(_("AIM Account"), "AIM", "");
658 protocol->account_options = g_list_append(protocol->account_options, option);
660 option = purple_account_option_string_new(_("XMPP Account"), "jid", "");
661 protocol->account_options = g_list_append(protocol->account_options, option);
664 static void
665 bonjour_protocol_class_init(PurpleProtocolClass *klass)
667 klass->login = bonjour_login;
668 klass->close = bonjour_close;
669 klass->status_types = bonjour_status_types;
670 klass->list_icon = bonjour_list_icon;
673 static void
674 bonjour_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
676 client_iface->status_text = bonjour_status_text;
677 client_iface->tooltip_text = bonjour_tooltip_text;
678 client_iface->convo_closed = bonjour_convo_closed;
679 client_iface->get_max_message_size = bonjour_get_max_message_size;
682 static void
683 bonjour_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
685 server_iface->add_buddy = bonjour_fake_add_buddy;
686 server_iface->remove_buddy = bonjour_remove_buddy;
687 server_iface->group_buddy = bonjour_group_buddy;
688 server_iface->rename_group = bonjour_rename_group;
689 server_iface->set_buddy_icon = bonjour_set_buddy_icon;
690 server_iface->set_status = bonjour_set_status;
693 static void
694 bonjour_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
696 im_iface->send = bonjour_send_im;
699 static void
700 bonjour_protocol_xfer_iface_init(PurpleProtocolXferInterface *xfer_iface)
702 xfer_iface->can_receive = bonjour_can_receive_file;
703 xfer_iface->send_file = bonjour_send_file;
704 xfer_iface->new_xfer = bonjour_new_xfer;
707 PURPLE_DEFINE_TYPE_EXTENDED(
708 BonjourProtocol, bonjour_protocol, PURPLE_TYPE_PROTOCOL, 0,
710 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT,
711 bonjour_protocol_client_iface_init)
713 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER,
714 bonjour_protocol_server_iface_init)
716 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM,
717 bonjour_protocol_im_iface_init)
719 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER,
720 bonjour_protocol_xfer_iface_init)
723 static PurplePluginInfo *
724 plugin_query(GError **error)
726 return purple_plugin_info_new(
727 "id", "prpl-bonjour",
728 "name", "Bonjour Protocol",
729 "version", DISPLAY_VERSION,
730 "category", N_("Protocol"),
731 "summary", N_("Bonjour Protocol Plugin"),
732 "description", N_("Bonjour Protocol Plugin"),
733 "website", PURPLE_WEBSITE,
734 "abi-version", PURPLE_ABI_VERSION,
735 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
736 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
737 NULL
741 static gboolean
742 plugin_load(PurplePlugin *plugin, GError **error)
744 bonjour_protocol_register_type(plugin);
746 xep_xfer_register(G_TYPE_MODULE(plugin));
748 my_protocol = purple_protocols_add(BONJOUR_TYPE_PROTOCOL, error);
749 if (!my_protocol)
750 return FALSE;
752 initialize_default_account_values();
754 return TRUE;
757 static gboolean
758 plugin_unload(PurplePlugin *plugin, GError **error)
760 if (!purple_protocols_remove(my_protocol, error))
761 return FALSE;
763 g_free(default_firstname);
764 g_free(default_lastname);
766 return TRUE;
769 PURPLE_PLUGIN_INIT(bonjour, plugin_query, plugin_load, plugin_unload);