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
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
30 #include "dns_sd_proxy.h"
36 #include "accountopt.h"
42 #include "mdns_common.h"
45 #include "bonjour_ft.h"
47 static char *default_firstname
;
48 static char *default_lastname
;
49 static char *default_hostname
;
52 bonjour_removeallfromlocal(PurpleConnection
*conn
, PurpleGroup
*bonjour_group
)
54 PurpleAccount
*account
= purple_connection_get_account(conn
);
55 PurpleBlistNode
*cnode
, *cnodenext
, *bnode
, *bnodenext
;
58 if (bonjour_group
== NULL
)
61 /* Go through and remove all buddies that belong to this account */
62 for (cnode
= purple_blist_node_get_first_child((PurpleBlistNode
*) bonjour_group
); cnode
; cnode
= cnodenext
) {
63 cnodenext
= purple_blist_node_get_sibling_next(cnode
);
64 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode
))
66 for (bnode
= purple_blist_node_get_first_child(cnode
); bnode
; bnode
= bnodenext
) {
67 bnodenext
= purple_blist_node_get_sibling_next(bnode
);
68 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode
))
70 buddy
= (PurpleBuddy
*) bnode
;
71 if (purple_buddy_get_account(buddy
) != account
)
73 purple_prpl_got_user_status(account
, purple_buddy_get_name(buddy
), "offline", NULL
);
74 purple_account_remove_buddy(account
, buddy
, NULL
);
75 purple_blist_remove_buddy(buddy
);
82 bonjour_login(PurpleAccount
*account
)
84 PurpleConnection
*gc
= purple_account_get_connection(account
);
87 PurplePresence
*presence
;
90 if (!dns_sd_available()) {
91 purple_connection_error_reason(gc
,
92 PURPLE_CONNECTION_ERROR_OTHER_ERROR
,
93 _("The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: "
94 "http://d.pidgin.im/BonjourWindows"
95 " for more information."));
100 gc
->flags
|= PURPLE_CONNECTION_HTML
;
101 gc
->proto_data
= bd
= g_new0(BonjourData
, 1);
103 /* Start waiting for jabber connections (iChat style) */
104 bd
->jabber_data
= g_new0(BonjourJabber
, 1);
105 bd
->jabber_data
->port
= purple_account_get_int(account
, "port", BONJOUR_DEFAULT_PORT
);
106 bd
->jabber_data
->account
= account
;
108 if (bonjour_jabber_start(bd
->jabber_data
) == -1) {
109 /* Send a message about the connection error */
110 purple_connection_error_reason (gc
,
111 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
112 _("Unable to listen for incoming IM connections\n"));
116 /* Connect to the mDNS daemon looking for buddies in the LAN */
117 bd
->dns_sd_data
= bonjour_dns_sd_new();
118 bd
->dns_sd_data
->first
= g_strdup(purple_account_get_string(account
, "first", default_firstname
));
119 bd
->dns_sd_data
->last
= g_strdup(purple_account_get_string(account
, "last", default_lastname
));
120 bd
->dns_sd_data
->port_p2pj
= bd
->jabber_data
->port
;
121 /* Not engaged in AV conference */
122 bd
->dns_sd_data
->vc
= g_strdup("!");
124 status
= purple_account_get_active_status(account
);
125 presence
= purple_account_get_presence(account
);
126 if (purple_presence_is_available(presence
))
127 bd
->dns_sd_data
->status
= g_strdup("avail");
128 else if (purple_presence_is_idle(presence
))
129 bd
->dns_sd_data
->status
= g_strdup("away");
131 bd
->dns_sd_data
->status
= g_strdup("dnd");
132 bd
->dns_sd_data
->msg
= g_strdup(purple_status_get_attr_string(status
, "message"));
134 bd
->dns_sd_data
->account
= account
;
135 if (!bonjour_dns_sd_start(bd
->dns_sd_data
))
137 purple_connection_error_reason (gc
,
138 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
139 _("Unable to establish connection with the local mDNS server. Is it running?"));
143 bonjour_dns_sd_update_buddy_icon(bd
->dns_sd_data
);
145 /* Show the buddy list by telling Purple we have already connected */
146 purple_connection_set_state(gc
, PURPLE_CONNECTED
);
150 bonjour_close(PurpleConnection
*connection
)
152 PurpleGroup
*bonjour_group
;
153 BonjourData
*bd
= connection
->proto_data
;
155 bonjour_group
= purple_find_group(BONJOUR_GROUP_NAME
);
157 /* Remove all the bonjour buddies */
158 bonjour_removeallfromlocal(connection
, bonjour_group
);
160 /* Stop looking for buddies in the LAN */
161 if (bd
!= NULL
&& bd
->dns_sd_data
!= NULL
)
163 bonjour_dns_sd_stop(bd
->dns_sd_data
);
164 bonjour_dns_sd_free(bd
->dns_sd_data
);
167 if (bd
!= NULL
&& bd
->jabber_data
!= NULL
)
169 /* Stop waiting for conversations */
170 bonjour_jabber_stop(bd
->jabber_data
);
171 g_free(bd
->jabber_data
);
174 /* Delete the bonjour group */
175 if (bonjour_group
!= NULL
)
176 purple_blist_remove_group(bonjour_group
);
178 /* Cancel any file transfers */
179 while (bd
!= NULL
&& bd
->xfer_lists
) {
180 purple_xfer_cancel_local(bd
->xfer_lists
->data
);
184 connection
->proto_data
= NULL
;
188 bonjour_list_icon(PurpleAccount
*account
, PurpleBuddy
*buddy
)
190 return BONJOUR_ICON_NAME
;
194 bonjour_send_im(PurpleConnection
*connection
, const char *to
, const char *msg
, PurpleMessageFlags flags
)
199 return bonjour_jabber_send_message(((BonjourData
*)(connection
->proto_data
))->jabber_data
, to
, msg
);
203 bonjour_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
205 PurpleConnection
*gc
;
207 gboolean disconnected
;
208 PurpleStatusType
*type
;
210 PurplePresence
*presence
;
211 const char *message
, *bonjour_status
;
214 gc
= purple_account_get_connection(account
);
216 disconnected
= purple_account_is_disconnected(account
);
217 type
= purple_status_get_type(status
);
218 primitive
= purple_status_type_get_primitive(type
);
219 presence
= purple_account_get_presence(account
);
221 message
= purple_status_get_attr_string(status
, "message");
224 stripped
= purple_markup_strip_html(message
);
227 * The three possible status for Bonjour are
228 * -available ("avail")
231 * Each of them can have an optional message.
233 if (purple_presence_is_available(presence
))
234 bonjour_status
= "avail";
235 else if (purple_presence_is_idle(presence
))
236 bonjour_status
= "away";
238 bonjour_status
= "dnd";
240 bonjour_dns_sd_send_status(bd
->dns_sd_data
, bonjour_status
, stripped
);
245 * The add_buddy callback removes the buddy from the local list.
246 * Bonjour manages buddies for you, and adding someone locally by
247 * hand is stupid. Perhaps we should change libpurple not to allow adding
248 * if there is no add_buddy callback.
251 bonjour_fake_add_buddy(PurpleConnection
*pc
, PurpleBuddy
*buddy
, PurpleGroup
*group
) {
252 purple_debug_error("bonjour", "Buddy '%s' manually added; removing. "
253 "Bonjour buddies must be discovered and not manually added.\n",
254 purple_buddy_get_name(buddy
));
256 /* I suppose we could alert the user here, but it seems unnecessary. */
258 /* If this causes problems, it can be moved to an idle callback */
259 purple_blist_remove_buddy(buddy
);
263 static void bonjour_remove_buddy(PurpleConnection
*pc
, PurpleBuddy
*buddy
, PurpleGroup
*group
) {
264 if (buddy
->proto_data
) {
265 bonjour_buddy_delete(buddy
->proto_data
);
266 buddy
->proto_data
= NULL
;
271 bonjour_status_types(PurpleAccount
*account
)
273 GList
*status_types
= NULL
;
274 PurpleStatusType
*type
;
276 g_return_val_if_fail(account
!= NULL
, NULL
);
278 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE
,
279 BONJOUR_STATUS_ID_AVAILABLE
,
280 NULL
, TRUE
, TRUE
, FALSE
,
281 "message", _("Message"),
282 purple_value_new(PURPLE_TYPE_STRING
), NULL
);
283 status_types
= g_list_append(status_types
, type
);
285 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY
,
286 BONJOUR_STATUS_ID_AWAY
,
287 NULL
, TRUE
, TRUE
, FALSE
,
288 "message", _("Message"),
289 purple_value_new(PURPLE_TYPE_STRING
), NULL
);
290 status_types
= g_list_append(status_types
, type
);
292 type
= purple_status_type_new_full(PURPLE_STATUS_OFFLINE
,
293 BONJOUR_STATUS_ID_OFFLINE
,
294 NULL
, TRUE
, TRUE
, FALSE
);
295 status_types
= g_list_append(status_types
, type
);
301 bonjour_convo_closed(PurpleConnection
*connection
, const char *who
)
303 PurpleBuddy
*buddy
= purple_find_buddy(connection
->account
, who
);
306 if (buddy
== NULL
|| buddy
->proto_data
== NULL
)
309 * This buddy is not in our buddy list, and therefore does not really
310 * exist, so we won't have any data about them.
315 bb
= buddy
->proto_data
;
316 bonjour_jabber_close_conversation(bb
->conversation
);
317 bb
->conversation
= NULL
;
321 void bonjour_set_buddy_icon(PurpleConnection
*conn
, PurpleStoredImage
*img
)
323 BonjourData
*bd
= conn
->proto_data
;
324 bonjour_dns_sd_update_buddy_icon(bd
->dns_sd_data
);
329 bonjour_status_text(PurpleBuddy
*buddy
)
331 const PurplePresence
*presence
;
332 const PurpleStatus
*status
;
336 presence
= purple_buddy_get_presence(buddy
);
337 status
= purple_presence_get_active_status(presence
);
339 message
= purple_status_get_attr_string(status
, "message");
341 if (message
!= NULL
) {
342 ret
= g_markup_escape_text(message
, -1);
343 purple_util_chrreplace(ret
, '\n', ' ');
350 bonjour_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
352 PurplePresence
*presence
;
353 PurpleStatus
*status
;
354 BonjourBuddy
*bb
= buddy
->proto_data
;
355 const char *status_description
;
358 presence
= purple_buddy_get_presence(buddy
);
359 status
= purple_presence_get_active_status(presence
);
360 message
= purple_status_get_attr_string(status
, "message");
362 if (purple_presence_is_available(presence
))
363 status_description
= purple_status_get_name(status
);
364 else if (purple_presence_is_idle(presence
))
365 status_description
= _("Idle");
367 status_description
= purple_status_get_name(status
);
369 purple_notify_user_info_add_pair(user_info
, _("Status"), status_description
);
371 purple_notify_user_info_add_pair(user_info
, _("Message"), message
);
374 purple_debug_error("bonjour", "Got tooltip request for a buddy without protocol data.\n");
378 /* Only show first/last name if there is a nickname set (to avoid duplication) */
379 if (bb
->nick
!= NULL
&& *bb
->nick
!= '\0') {
380 if (bb
->first
!= NULL
&& *bb
->first
!= '\0')
381 purple_notify_user_info_add_pair(user_info
, _("First name"), bb
->first
);
382 if (bb
->last
!= NULL
&& *bb
->last
!= '\0')
383 purple_notify_user_info_add_pair(user_info
, _("Last name"), bb
->last
);
386 if (bb
->email
!= NULL
&& *bb
->email
!= '\0')
387 purple_notify_user_info_add_pair(user_info
, _("Email"), bb
->email
);
389 if (bb
->AIM
!= NULL
&& *bb
->AIM
!= '\0')
390 purple_notify_user_info_add_pair(user_info
, _("AIM Account"), bb
->AIM
);
392 if (bb
->jid
!= NULL
&& *bb
->jid
!= '\0')
393 purple_notify_user_info_add_pair(user_info
, _("XMPP Account"), bb
->jid
);
397 bonjour_group_buddy(PurpleConnection
*connection
, const char *who
, const char *old_group
, const char *new_group
)
399 PurpleBlistNodeFlags oldflags
;
400 PurpleBuddy
*buddy
= purple_find_buddy(connection
->account
, who
);
405 oldflags
= purple_blist_node_get_flags((PurpleBlistNode
*)buddy
);
407 /* If we're moving them out of the bonjour group, make them persistent */
408 if (strcmp(new_group
, BONJOUR_GROUP_NAME
) == 0)
409 purple_blist_node_set_flags((PurpleBlistNode
*)buddy
, oldflags
| PURPLE_BLIST_NODE_FLAG_NO_SAVE
);
411 purple_blist_node_set_flags((PurpleBlistNode
*)buddy
, oldflags
^ PURPLE_BLIST_NODE_FLAG_NO_SAVE
);
416 bonjour_can_receive_file(PurpleConnection
*connection
, const char *who
)
418 PurpleBuddy
*buddy
= purple_find_buddy(connection
->account
, who
);
420 return (buddy
!= NULL
&& buddy
->proto_data
!= NULL
);
425 plugin_unload(PurplePlugin
*plugin
)
427 /* These shouldn't happen here because they are allocated in _init() */
429 g_free(default_firstname
);
430 g_free(default_lastname
);
431 g_free(default_hostname
);
436 static PurplePlugin
*my_protocol
= NULL
;
438 static PurplePluginProtocolInfo prpl_info
=
440 OPT_PROTO_NO_PASSWORD
,
441 NULL
, /* user_splits */
442 NULL
, /* protocol_options */
443 {"png,gif,jpeg", 0, 0, 96, 96, 65535, PURPLE_ICON_SCALE_DISPLAY
}, /* icon_spec */
444 bonjour_list_icon
, /* list_icon */
445 NULL
, /* list_emblem */
446 bonjour_status_text
, /* status_text */
447 bonjour_tooltip_text
, /* tooltip_text */
448 bonjour_status_types
, /* status_types */
449 NULL
, /* blist_node_menu */
450 NULL
, /* chat_info */
451 NULL
, /* chat_info_defaults */
452 bonjour_login
, /* login */
453 bonjour_close
, /* close */
454 bonjour_send_im
, /* send_im */
456 NULL
, /* send_typing */
458 bonjour_set_status
, /* set_status */
460 NULL
, /* change_passwd */
461 bonjour_fake_add_buddy
, /* add_buddy */
462 NULL
, /* add_buddies */
463 bonjour_remove_buddy
, /* remove_buddy */
464 NULL
, /* remove_buddies */
465 NULL
, /* add_permit */
467 NULL
, /* rem_permit */
469 NULL
, /* set_permit_deny */
470 NULL
, /* join_chat */
471 NULL
, /* reject_chat */
472 NULL
, /* get_chat_name */
473 NULL
, /* chat_invite */
474 NULL
, /* chat_leave */
475 NULL
, /* chat_whisper */
476 NULL
, /* chat_send */
477 NULL
, /* keepalive */
478 NULL
, /* register_user */
479 NULL
, /* get_cb_info */
480 NULL
, /* get_cb_away */
481 NULL
, /* alias_buddy */
482 bonjour_group_buddy
, /* group_buddy */
483 NULL
, /* rename_group */
484 NULL
, /* buddy_free */
485 bonjour_convo_closed
, /* convo_closed */
486 NULL
, /* normalize */
487 bonjour_set_buddy_icon
, /* set_buddy_icon */
488 NULL
, /* remove_group */
489 NULL
, /* get_cb_real_name */
490 NULL
, /* set_chat_topic */
491 NULL
, /* find_blist_chat */
492 NULL
, /* roomlist_get_list */
493 NULL
, /* roomlist_cancel */
494 NULL
, /* roomlist_expand_category */
495 bonjour_can_receive_file
, /* can_receive_file */
496 bonjour_send_file
, /* send_file */
497 bonjour_new_xfer
, /* new_xfer */
498 NULL
, /* offline_message */
499 NULL
, /* whiteboard_prpl_ops */
501 NULL
, /* roomlist_room_serialize */
507 sizeof(PurplePluginProtocolInfo
), /* struct_size */
511 static PurplePluginInfo info
=
514 PURPLE_MAJOR_VERSION
,
515 PURPLE_MINOR_VERSION
,
516 PURPLE_PLUGIN_PROTOCOL
, /**< type */
517 NULL
, /**< ui_requirement */
519 NULL
, /**< dependencies */
520 PURPLE_PRIORITY_DEFAULT
, /**< priority */
522 "prpl-bonjour", /**< id */
523 "Bonjour", /**< name */
524 DISPLAY_VERSION
, /**< version */
526 N_("Bonjour Protocol Plugin"),
528 N_("Bonjour Protocol Plugin"),
530 PURPLE_WEBSITE
, /**< homepage */
533 plugin_unload
, /**< unload */
534 NULL
, /**< destroy */
536 NULL
, /**< ui_info */
537 &prpl_info
, /**< extra_info */
538 NULL
, /**< prefs_info */
549 static gboolean
_set_default_name_cb(gpointer data
) {
550 gchar
*fullname
= data
;
551 const char *splitpoint
;
552 GList
*tmp
= prpl_info
.protocol_options
;
553 PurpleAccountOption
*option
;
556 purple_debug_info("bonjour", "Unable to look up First and Last name or Username from system; using defaults.\n");
560 g_free(default_firstname
);
561 g_free(default_lastname
);
563 /* Split the real name into a first and last name */
564 splitpoint
= strchr(fullname
, ' ');
565 if (splitpoint
!= NULL
) {
566 default_firstname
= g_strndup(fullname
, splitpoint
- fullname
);
567 default_lastname
= g_strdup(&splitpoint
[1]);
569 default_firstname
= g_strdup(fullname
);
570 default_lastname
= g_strdup("");
575 for(; tmp
!= NULL
; tmp
= tmp
->next
) {
577 if (strcmp("first", purple_account_option_get_setting(option
)) == 0)
578 purple_account_option_set_default_string(option
, default_firstname
);
579 else if (strcmp("last", purple_account_option_get_setting(option
)) == 0)
580 purple_account_option_set_default_string(option
, default_lastname
);
586 static gpointer
_win32_name_lookup_thread(gpointer data
) {
587 gchar
*fullname
= NULL
;
588 wchar_t username
[UNLEN
+ 1];
589 DWORD dwLenUsername
= UNLEN
+ 1;
591 GetUserNameW((LPWSTR
) &username
, &dwLenUsername
);
593 if (username
!= NULL
&& *username
!= '\0') {
594 LPBYTE servername
= NULL
;
597 NetGetDCName(NULL
, NULL
, &servername
);
599 /* purple_debug_info("bonjour", "Looking up the full name from the %s.\n", (servername ? "domain controller" : "local machine")); */
601 if (NetUserGetInfo((LPCWSTR
) servername
, username
, 10, &info
) == NERR_Success
602 && info
!= NULL
&& ((LPUSER_INFO_10
) info
)->usri10_full_name
!= NULL
603 && *(((LPUSER_INFO_10
) info
)->usri10_full_name
) != '\0') {
604 fullname
= g_utf16_to_utf8(
605 ((LPUSER_INFO_10
) info
)->usri10_full_name
,
606 -1, NULL
, NULL
, NULL
);
608 /* Fall back to the local machine if we didn't get the full name from the domain controller */
609 else if (servername
!= NULL
) {
610 /* purple_debug_info("bonjour", "Looking up the full name from the local machine"); */
612 if (info
!= NULL
) NetApiBufferFree(info
);
615 if (NetUserGetInfo(NULL
, username
, 10, &info
) == NERR_Success
616 && info
!= NULL
&& ((LPUSER_INFO_10
) info
)->usri10_full_name
!= NULL
617 && *(((LPUSER_INFO_10
) info
)->usri10_full_name
) != '\0') {
618 fullname
= g_utf16_to_utf8(
619 ((LPUSER_INFO_10
) info
)->usri10_full_name
,
620 -1, NULL
, NULL
, NULL
);
624 if (info
!= NULL
) NetApiBufferFree(info
);
625 if (servername
!= NULL
) NetApiBufferFree(servername
);
628 fullname
= g_utf16_to_utf8(username
, -1, NULL
, NULL
, NULL
);
631 purple_timeout_add(0, _set_default_name_cb
, fullname
);
638 initialize_default_account_values(void)
643 const char *fullname
= NULL
, *splitpoint
, *tmp
;
647 /* Try to figure out the user's real name */
648 info
= getpwuid(getuid());
649 if ((info
!= NULL
) && (info
->pw_gecos
!= NULL
) && (info
->pw_gecos
[0] != '\0'))
650 fullname
= info
->pw_gecos
;
651 else if ((info
!= NULL
) && (info
->pw_name
!= NULL
) && (info
->pw_name
[0] != '\0'))
652 fullname
= info
->pw_name
;
653 else if (((fullname
= getlogin()) != NULL
) && (fullname
[0] == '\0'))
656 /* The Win32 username lookup functions are synchronous so we do it in a thread */
657 g_thread_create(_win32_name_lookup_thread
, NULL
, FALSE
, NULL
);
660 /* Make sure fullname is valid UTF-8. If not, try to convert it. */
661 if (fullname
!= NULL
&& !g_utf8_validate(fullname
, -1, NULL
)) {
662 fullname
= conv
= g_locale_to_utf8(fullname
, -1, NULL
, NULL
, NULL
);
663 if (conv
== NULL
|| *conv
== '\0')
667 if (fullname
== NULL
)
668 fullname
= _("Purple Person");
670 /* Split the real name into a first and last name */
671 splitpoint
= strchr(fullname
, ' ');
672 if (splitpoint
!= NULL
) {
673 default_firstname
= g_strndup(fullname
, splitpoint
- fullname
);
674 tmp
= &splitpoint
[1];
676 /* The last name may be followed by a comma and additional data.
677 * Only use the last name itself.
679 splitpoint
= strchr(tmp
, ',');
680 if (splitpoint
!= NULL
)
681 default_lastname
= g_strndup(tmp
, splitpoint
- tmp
);
683 default_lastname
= g_strdup(tmp
);
685 default_firstname
= g_strdup(fullname
);
686 default_lastname
= g_strdup("");
691 /* Try to figure out a good host name to use */
692 /* TODO: Avoid 'localhost,' if possible */
693 default_hostname
= g_strdup(purple_get_host_name());
697 init_plugin(PurplePlugin
*plugin
)
699 PurpleAccountUserSplit
*split
;
700 PurpleAccountOption
*option
;
702 initialize_default_account_values();
704 /* Creating the user splits */
705 split
= purple_account_user_split_new(_("Hostname"), default_hostname
, '@');
706 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
708 /* Creating the options for the protocol */
709 option
= purple_account_option_int_new(_("Local Port"), "port", BONJOUR_DEFAULT_PORT
);
710 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
712 option
= purple_account_option_string_new(_("First name"), "first", default_firstname
);
713 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
715 option
= purple_account_option_string_new(_("Last name"), "last", default_lastname
);
716 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
718 option
= purple_account_option_string_new(_("Email"), "email", "");
719 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
721 option
= purple_account_option_string_new(_("AIM Account"), "AIM", "");
722 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
724 option
= purple_account_option_string_new(_("XMPP Account"), "jid", "");
725 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
727 my_protocol
= plugin
;
730 PURPLE_INIT_PLUGIN(bonjour
, init_plugin
, info
);