Mercurial > pidgin.yaz
changeset 18854:2516cf81b763
merge of '1aba81cae8b963708281123ed6c438ba5861113d'
and 'a44fe9cfe3fd6d287a438835c65977f4570da41e'
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Thu, 09 Aug 2007 18:34:01 +0000 |
parents | fab096e7b804 (current diff) 2283d5bfc24b (diff) |
children | 28a7c9dbbc2b |
files | |
diffstat | 10 files changed, 411 insertions(+), 98 deletions(-) [+] |
line wrap: on
line diff
--- a/libpurple/protocols/bonjour/bonjour.c Thu Aug 09 02:42:49 2007 +0000 +++ b/libpurple/protocols/bonjour/bonjour.c Thu Aug 09 18:34:01 2007 +0000 @@ -138,6 +138,8 @@ return; } + bonjour_dns_sd_update_buddy_icon(bd->dns_sd_data); + /* Create a group for bonjour buddies */ bonjour_group = purple_group_new(BONJOUR_GROUP_NAME); purple_blist_add_group(bonjour_group, NULL); @@ -283,6 +285,14 @@ bb->conversation = NULL; } +static +void bonjour_set_buddy_icon(PurpleConnection *conn, PurpleStoredImage *img) +{ + BonjourData *bd = conn->proto_data; + bonjour_dns_sd_update_buddy_icon(bd->dns_sd_data); +} + + static char * bonjour_status_text(PurpleBuddy *buddy) { @@ -339,8 +349,7 @@ OPT_PROTO_NO_PASSWORD, NULL, /* user_splits */ NULL, /* protocol_options */ - /* {"png", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, */ /* icon_spec */ - NO_BUDDY_ICONS, /* not yet */ /* icon_spec */ + {"png,gif,jpeg", 0, 0, 96, 96, 65535, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ bonjour_list_icon, /* list_icon */ NULL, /* list_emblem */ bonjour_status_text, /* status_text */ @@ -384,7 +393,7 @@ NULL, /* buddy_free */ bonjour_convo_closed, /* convo_closed */ NULL, /* normalize */ - NULL, /* set_buddy_icon */ + bonjour_set_buddy_icon, /* set_buddy_icon */ NULL, /* remove_group */ NULL, /* get_cb_real_name */ NULL, /* set_chat_topic */ @@ -533,7 +542,7 @@ { default_firstname = g_strndup(fullname, splitpoint - fullname); tmp = &splitpoint[1]; - + /* The last name may be followed by a comma and additional data. * Only use the last name itself. */
--- a/libpurple/protocols/bonjour/buddy.c Thu Aug 09 02:42:49 2007 +0000 +++ b/libpurple/protocols/bonjour/buddy.c Thu Aug 09 18:34:01 2007 +0000 @@ -18,6 +18,7 @@ #include <stdlib.h> #include "internal.h" +#include "cipher.h" #include "buddy.h" #include "account.h" #include "blist.h" @@ -106,11 +107,10 @@ PurpleBuddy *buddy; PurpleGroup *group; PurpleAccount *account = bonjour_buddy->account; - const char *status_id, *first, *last, *old_hash, *new_hash; - gchar *alias = NULL; + const char *status_id, *old_hash, *new_hash; /* Translate between the Bonjour status and the Purple status */ - if (g_ascii_strcasecmp("dnd", bonjour_buddy->status) == 0) + if (bonjour_buddy->status != NULL && g_ascii_strcasecmp("dnd", bonjour_buddy->status) == 0) status_id = BONJOUR_STATUS_ID_AWAY; else status_id = BONJOUR_STATUS_ID_AVAILABLE; @@ -138,15 +138,21 @@ } /* Create the alias for the buddy using the first and the last name */ - first = bonjour_buddy->first; - last = bonjour_buddy->last; - if ((first && *first) || (last && *last)) - alias = g_strdup_printf("%s%s%s", - (first && *first ? first : ""), - (first && *first && last && *last ? " " : ""), - (last && *last ? last : "")); - serv_got_alias(purple_account_get_connection(account), buddy->name, alias); - g_free(alias); + if (bonjour_buddy->nick) + serv_got_alias(purple_account_get_connection(account), buddy->name, bonjour_buddy->nick); + else { + gchar *alias = NULL; + const char *first, *last; + first = bonjour_buddy->first; + last = bonjour_buddy->last; + if ((first && *first) || (last && *last)) + alias = g_strdup_printf("%s%s%s", + (first && *first ? first : ""), + (first && *first && last && *last ? " " : ""), + (last && *last ? last : "")); + serv_got_alias(purple_account_get_connection(account), buddy->name, alias); + g_free(alias); + } /* Set the user's status */ if (bonjour_buddy->msg != NULL) @@ -166,12 +172,46 @@ new_hash = (bonjour_buddy->phsh && *(bonjour_buddy->phsh)) ? bonjour_buddy->phsh : NULL; if (new_hash && (!old_hash || strcmp(old_hash, new_hash) != 0)) { /* Look up the new icon data */ + /* TODO: Make sure the hash assigned to the retrieved buddy icon is the same + * as what we looked up. */ bonjour_dns_sd_retrieve_buddy_icon(bonjour_buddy); - } else + } else if (!new_hash) purple_buddy_icons_set_for_user(account, buddy->name, NULL, 0, NULL); } /** + * We got the buddy icon data; deal with it + */ +void bonjour_buddy_got_buddy_icon(BonjourBuddy *buddy, gconstpointer data, gsize len) { + /* Recalculate the hash instead of using the current phsh to make sure it is accurate for the icon. */ + int i; + gchar *enc; + char *p, hash[41]; + unsigned char hashval[20]; + + if (data == NULL || len == 0) + return; + + enc = purple_base64_encode(data, len); + + purple_cipher_digest_region("sha1", data, + len, sizeof(hashval), + hashval, NULL); + + p = hash; + for(i=0; i<20; i++, p+=2) + snprintf(p, 3, "%02x", hashval[i]); + + purple_debug_info("bonjour", "Got buddy icon for %s icon hash='%s' phsh='%s'.\n", buddy->name, + hash, buddy->phsh ? buddy->phsh : "(null)"); + + purple_buddy_icons_set_for_user(buddy->account, buddy->name, + g_memdup(data, len), len, hash); + + g_free(enc); +} + +/** * Deletes a buddy from memory. */ void @@ -179,8 +219,6 @@ { g_free(buddy->name); g_free(buddy->ip); - g_free(buddy->full_service_name); - g_free(buddy->first); g_free(buddy->phsh); g_free(buddy->status);
--- a/libpurple/protocols/bonjour/buddy.h Thu Aug 09 02:42:49 2007 +0000 +++ b/libpurple/protocols/bonjour/buddy.h Thu Aug 09 18:34:01 2007 +0000 @@ -29,7 +29,6 @@ gchar *name; /* TODO: Remove and just use the hostname */ gchar *ip; - gchar *full_service_name; gint port_p2pj; gchar *first; @@ -91,6 +90,11 @@ void bonjour_buddy_add_to_purple(BonjourBuddy *buddy); /** + * We got the buddy icon data; deal with it + */ +void bonjour_buddy_got_buddy_icon(BonjourBuddy *buddy, gconstpointer data, gsize len); + +/** * Deletes a buddy from memory. */ void bonjour_buddy_delete(BonjourBuddy *buddy);
--- a/libpurple/protocols/bonjour/issues.txt Thu Aug 09 02:42:49 2007 +0000 +++ b/libpurple/protocols/bonjour/issues.txt Thu Aug 09 18:34:01 2007 +0000 @@ -3,6 +3,5 @@ ========================================== * Status changes don't work -* Avatars * File transfers * Typing notifications
--- a/libpurple/protocols/bonjour/mdns_avahi.c Thu Aug 09 02:42:49 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_avahi.c Thu Aug 09 18:34:01 2007 +0000 @@ -19,6 +19,7 @@ #include "mdns_interface.h" #include "debug.h" #include "buddy.h" +#include "bonjour.h" #include <avahi-client/client.h> #include <avahi-client/lookup.h> @@ -32,14 +33,25 @@ #include <avahi-glib/glib-malloc.h> #include <avahi-glib/glib-watch.h> +/* For some reason, this is missing from the Avahi type defines */ +#ifndef AVAHI_DNS_TYPE_NULL +#define AVAHI_DNS_TYPE_NULL 0x0A +#endif + /* data used by avahi bonjour implementation */ typedef struct _avahi_session_impl_data { AvahiClient *client; AvahiGLibPoll *glib_poll; AvahiServiceBrowser *sb; AvahiEntryGroup *group; + AvahiEntryGroup *buddy_icon_group; } AvahiSessionImplData; +typedef struct _avahi_buddy_impl_data { + AvahiServiceResolver *resolver; + AvahiRecordBrowser *buddy_icon_rec_browser; +} AvahiBuddyImplData; + static void _resolver_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, @@ -59,11 +71,14 @@ case AVAHI_RESOLVER_FAILURE: purple_debug_error("bonjour", "_resolve_callback - Failure: %s\n", avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))); + avahi_service_resolver_free(r); break; case AVAHI_RESOLVER_FOUND: /* create a buddy record */ buddy = bonjour_buddy_new(name, account); + ((AvahiBuddyImplData *)buddy->mdns_impl_data)->resolver = r; + /* Get the ip as a string */ buddy->ip = g_malloc(AVAHI_ADDRESS_STR_MAX); avahi_address_snprint(buddy->ip, AVAHI_ADDRESS_STR_MAX, a); @@ -95,7 +110,6 @@ purple_debug_info("bonjour", "Unrecognized Service Resolver event: %d.\n", event); } - avahi_service_resolver_free(r); } static void @@ -145,6 +159,30 @@ } static void +_buddy_icon_group_cb(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { + BonjourDnsSd *data = userdata; + AvahiSessionImplData *idata = data->mdns_impl_data; + + g_return_if_fail(g == idata->buddy_icon_group || idata->buddy_icon_group == NULL); + + switch(state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED: + purple_debug_info("bonjour", "Successfully registered buddy icon data.\n"); + case AVAHI_ENTRY_GROUP_COLLISION: + purple_debug_error("bonjour", "Collision registering buddy icon data.\n"); + break; + case AVAHI_ENTRY_GROUP_FAILURE: + purple_debug_error("bonjour", "Error registering buddy icon data: %s\n.", + avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g)))); + break; + case AVAHI_ENTRY_GROUP_UNCOMMITED: + case AVAHI_ENTRY_GROUP_REGISTERING: + break; + } + +} + +static void _entry_group_cb(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { AvahiSessionImplData *idata = userdata; @@ -170,6 +208,31 @@ } +static void +_buddy_icon_record_cb(AvahiRecordBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, + AvahiBrowserEvent event, const char *name, uint16_t clazz, uint16_t type, + const void *rdata, size_t size, AvahiLookupResultFlags flags, void *userdata) { + BonjourBuddy *buddy = userdata; + AvahiBuddyImplData *idata = buddy->mdns_impl_data; + + switch (event) { + case AVAHI_BROWSER_NEW: + bonjour_buddy_got_buddy_icon(buddy, rdata, size); + break; + case AVAHI_BROWSER_REMOVE: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_FAILURE: + purple_debug_error("bonjour", "Error rerieving buddy icon record: %s\n", + avahi_strerror(avahi_client_errno(avahi_record_browser_get_client(b)))); + break; + } + + /* Stop listening */ + avahi_record_browser_free(idata->buddy_icon_rec_browser); + idata->buddy_icon_rec_browser = NULL; +} + /**************************** * mdns_interface functions * ****************************/ @@ -290,7 +353,8 @@ return FALSE; } - if ((publish_result = avahi_entry_group_commit(idata->group)) < 0) { + if (type == PUBLISH_START + && (publish_result = avahi_entry_group_commit(idata->group)) < 0) { purple_debug_error("bonjour", "Failed to commit " ICHAT_SERVICE " service. Error: %s\n", avahi_strerror(publish_result)); @@ -317,9 +381,71 @@ return TRUE; } -/* This is done differently than with Howl/Apple Bonjour */ -guint _mdns_register_to_mainloop(BonjourDnsSd *data) { - return 0; +gboolean _mdns_set_buddy_icon_data(BonjourDnsSd *data, gconstpointer avatar_data, gsize avatar_len) { + AvahiSessionImplData *idata = data->mdns_impl_data; + + if (idata == NULL || idata->client == NULL) + return FALSE; + + if (avatar_data != NULL) { + gboolean new_group = FALSE; + gchar *svc_name; + int ret; + AvahiPublishFlags flags = 0; + + if (idata->buddy_icon_group == NULL) { + purple_debug_info("bonjour", "Setting new buddy icon.\n"); + new_group = TRUE; + + idata->buddy_icon_group = avahi_entry_group_new(idata->client, + _buddy_icon_group_cb, data); + } else { + purple_debug_info("bonjour", "Updating existing buddy icon.\n"); + flags |= AVAHI_PUBLISH_UPDATE; + } + + if (idata->buddy_icon_group == NULL) { + purple_debug_error("bonjour", + "Unable to initialize the buddy icon group (%s).\n", + avahi_strerror(avahi_client_errno(idata->client))); + return FALSE; + } + + svc_name = g_strdup_printf("%s." ICHAT_SERVICE "local", + purple_account_get_username(data->account)); + + ret = avahi_entry_group_add_record(idata->buddy_icon_group, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, flags, svc_name, + AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 120, avatar_data, avatar_len); + + g_free(svc_name); + + if (ret < 0) { + purple_debug_error("bonjour", + "Failed to register buddy icon. Error: %s\n", avahi_strerror(ret)); + if (new_group) { + avahi_entry_group_free(idata->buddy_icon_group); + idata->buddy_icon_group = NULL; + } + return FALSE; + } + + if (new_group && (ret = avahi_entry_group_commit(idata->buddy_icon_group)) < 0) { + purple_debug_error("bonjour", + "Failed to commit buddy icon group. Error: %s\n", avahi_strerror(ret)); + if (new_group) { + avahi_entry_group_free(idata->buddy_icon_group); + idata->buddy_icon_group = NULL; + } + return FALSE; + } + } else if (idata->buddy_icon_group != NULL) { + purple_debug_info("bonjour", "Removing existing buddy icon.\n"); + avahi_entry_group_free(idata->buddy_icon_group); + idata->buddy_icon_group = NULL; + } + + return TRUE; } void _mdns_stop(BonjourDnsSd *data) { @@ -340,10 +466,50 @@ } void _mdns_init_buddy(BonjourBuddy *buddy) { + buddy->mdns_impl_data = g_new0(AvahiBuddyImplData, 1); } void _mdns_delete_buddy(BonjourBuddy *buddy) { + AvahiBuddyImplData *idata = buddy->mdns_impl_data; + + g_return_if_fail(idata != NULL); + + if (idata->buddy_icon_rec_browser != NULL) + avahi_record_browser_free(idata->buddy_icon_rec_browser); + + if (idata->resolver != NULL) + avahi_service_resolver_free(idata->resolver); + + g_free(idata); + + buddy->mdns_impl_data = NULL; } -void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy) { +void _mdns_retrieve_buddy_icon(BonjourBuddy* buddy) { + PurpleConnection *conn = purple_account_get_connection(buddy->account); + BonjourData *bd = conn->proto_data; + AvahiSessionImplData *session_idata = bd->dns_sd_data->mdns_impl_data; + AvahiBuddyImplData *idata = buddy->mdns_impl_data; + gchar *name; + + g_return_if_fail(idata != NULL); + + if (idata->buddy_icon_rec_browser != NULL) + avahi_record_browser_free(idata->buddy_icon_rec_browser); + + purple_debug_info("bonjour", "Retrieving buddy icon for '%s'.\n", buddy->name); + + name = g_strdup_printf("%s." ICHAT_SERVICE "local", buddy->name); + idata->buddy_icon_rec_browser = avahi_record_browser_new(session_idata->client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0, + _buddy_icon_record_cb, buddy); + g_free(name); + + if (!idata->buddy_icon_rec_browser) { + purple_debug_error("bonjour", + "Unable to initialize buddy icon record browser. Error: %s\n.", + avahi_strerror(avahi_client_errno(session_idata->client))); + } + } +
--- a/libpurple/protocols/bonjour/mdns_common.c Thu Aug 09 02:42:49 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_common.c Thu Aug 09 18:34:01 2007 +0000 @@ -17,11 +17,13 @@ #include <string.h> #include "internal.h" +#include "cipher.h" +#include "debug.h" + #include "mdns_common.h" #include "mdns_interface.h" #include "bonjour.h" #include "buddy.h" -#include "debug.h" /** @@ -54,8 +56,7 @@ * Send a new dns-sd packet updating our status. */ void -bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message) -{ +bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message) { g_free(data->status); g_free(data->msg); @@ -67,6 +68,64 @@ } /** + * Retrieve the buddy icon blob + */ +void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy) { + _mdns_retrieve_buddy_icon(buddy); +} + +void +bonjour_dns_sd_update_buddy_icon(BonjourDnsSd *data) { + PurpleStoredImage *img; + + if ((img = purple_buddy_icons_find_account_icon(data->account))) { + gconstpointer avatar_data; + gsize avatar_len; + + avatar_data = purple_imgstore_get_data(img); + avatar_len = purple_imgstore_get_size(img); + + if (_mdns_set_buddy_icon_data(data, avatar_data, avatar_len)) { + int i; + gchar *enc; + char *p, hash[41]; + unsigned char hashval[20]; + + enc = purple_base64_encode(avatar_data, avatar_len); + + purple_cipher_digest_region("sha1", avatar_data, + avatar_len, sizeof(hashval), + hashval, NULL); + + p = hash; + for(i=0; i<20; i++, p+=2) + snprintf(p, 3, "%02x", hashval[i]); + + g_free(data->phsh); + data->phsh = g_strdup(hash); + + g_free(enc); + + /* Update our TXT record */ + _mdns_publish(data, PUBLISH_UPDATE); + } + + purple_imgstore_unref(img); + } else { + /* We need to do this regardless of whether data->phsh is set so that we + * cancel any icons that are currently in the process of being set */ + _mdns_set_buddy_icon_data(data, NULL, 0); + if (data->phsh != NULL) { + /* Clear the buddy icon */ + g_free(data->phsh); + data->phsh = NULL; + /* Update our TXT record */ + _mdns_publish(data, PUBLISH_UPDATE); + } + } +} + +/** * Advertise our presence within the dns-sd daemon and start browsing * for other bonjour peers. */ @@ -91,11 +150,6 @@ return FALSE; } - - /* Get the socket that communicates with the mDNS daemon and bind it to a */ - /* callback that will handle the dns_sd packets */ - gc->inpa = _mdns_register_to_mainloop(data); - return TRUE; } @@ -104,13 +158,6 @@ */ void -bonjour_dns_sd_stop(BonjourDnsSd *data) -{ - PurpleConnection *gc; - +bonjour_dns_sd_stop(BonjourDnsSd *data) { _mdns_stop(data); - - gc = purple_account_get_connection(data->account); - if (gc->inpa > 0) - purple_input_remove(gc->inpa); }
--- a/libpurple/protocols/bonjour/mdns_common.h Thu Aug 09 02:42:49 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_common.h Thu Aug 09 18:34:01 2007 +0000 @@ -42,6 +42,11 @@ void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy); /** + * Deal with a buddy icon update + */ +void bonjour_dns_sd_update_buddy_icon(BonjourDnsSd *data); + +/** * Advertise our presence within the dns-sd daemon and start * browsing for other bonjour peers. */
--- a/libpurple/protocols/bonjour/mdns_howl.c Thu Aug 09 02:42:49 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_howl.c Thu Aug 09 18:34:01 2007 +0000 @@ -26,6 +26,7 @@ typedef struct _howl_impl_data { sw_discovery session; sw_discovery_oid session_id; + guint session_handler; } HowlSessionImplData; static sw_result HOWL_API @@ -276,17 +277,18 @@ g_return_val_if_fail(idata != NULL, FALSE); - return (sw_discovery_browse(idata->session, 0, ICHAT_SERVICE, NULL, _browser_reply, - data->account, &session_id) == SW_OKAY); + if (sw_discovery_browse(idata->session, 0, ICHAT_SERVICE, NULL, _browser_reply, + data->account, &session_id) == SW_OKAY) { + idata->session_handler = purple_input_add(sw_discovery_socket(idata->session), + PURPLE_INPUT_READ, _mdns_handle_event, idata->session); + return TRUE; + } + + return FALSE; } -guint _mdns_register_to_mainloop(BonjourDnsSd *data) { - HowlSessionImplData *idata = data->mdns_impl_data; - - g_return_val_if_fail(idata != NULL, 0); - - return purple_input_add(sw_discovery_socket(idata->session), - PURPLE_INPUT_READ, _mdns_handle_event, idata->session); +gboolean _mdns_set_buddy_icon_data(BonjourDnsSd *data, gconstpointer avatar_data, gsize avatar_len) { + return FALSE; } void _mdns_stop(BonjourDnsSd *data) { @@ -297,6 +299,8 @@ sw_discovery_cancel(idata->session, idata->session_id); + purple_input_remove(idata->session_handler); + /* TODO: should this really be g_free()'d ??? */ g_free(idata->session); @@ -311,5 +315,7 @@ void _mdns_delete_buddy(BonjourBuddy *buddy) { } -void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy) { +void _mdns_retrieve_buddy_icon(BonjourBuddy* buddy) { } + +
--- a/libpurple/protocols/bonjour/mdns_interface.h Thu Aug 09 02:42:49 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_interface.h Thu Aug 09 18:34:01 2007 +0000 @@ -26,15 +26,14 @@ gboolean _mdns_browse(BonjourDnsSd *data); -guint _mdns_register_to_mainloop(BonjourDnsSd *data); +void _mdns_stop(BonjourDnsSd *data); -void _mdns_stop(BonjourDnsSd *data); +gboolean _mdns_set_buddy_icon_data(BonjourDnsSd *data, gconstpointer avatar_data, gsize avatar_len); void _mdns_init_buddy(BonjourBuddy *buddy); void _mdns_delete_buddy(BonjourBuddy *buddy); -/* This doesn't quite belong here, but there really isn't any shared functionality */ -void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy); +void _mdns_retrieve_buddy_icon(BonjourBuddy* buddy); #endif
--- a/libpurple/protocols/bonjour/mdns_win32.c Thu Aug 09 02:42:49 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_win32.c Thu Aug 09 18:34:01 2007 +0000 @@ -21,12 +21,14 @@ #include "mdns_interface.h" #include "dns_sd_proxy.h" #include "dnsquery.h" +#include "mdns_common.h" /* data structure for the resolve callback */ typedef struct _ResolveCallbackArgs { DNSServiceRef resolver; guint resolver_handler; + gchar *full_service_name; PurpleDnsQueryData *query; @@ -35,10 +37,12 @@ /* data used by win32 bonjour implementation */ typedef struct _win32_session_impl_data { - DNSServiceRef advertisement; - DNSServiceRef browser; + DNSServiceRef presence_svc; + DNSServiceRef browser_svc; + DNSRecordRef buddy_icon_rec; - guint advertisement_handler; /* hack... windows bonjour is broken, so we have to have this */ + guint presence_handler; + guint browser_handler; } Win32SessionImplData; typedef struct _win32_buddy_impl_data { @@ -90,8 +94,7 @@ g_return_if_fail(idata != NULL); - purple_buddy_icons_set_for_user(buddy->account, buddy->name, - g_memdup(rdata, rdlen), rdlen, buddy->phsh); + bonjour_buddy_got_buddy_icon(buddy, rdata, rdlen); /* We've got what we need; stop listening */ purple_input_remove(idata->null_query_handler); @@ -120,14 +123,16 @@ /* finally, set up the continuous txt record watcher, and add the buddy to purple */ - if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->txt_query, 0, 0, buddy->full_service_name, - kDNSServiceType_TXT, kDNSServiceClass_IN, _mdns_text_record_query_callback, buddy)) { - int fd = DNSServiceRefSockFD(idata->txt_query); - idata->txt_query_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, idata->txt_query); + if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->txt_query, kDNSServiceFlagsLongLivedQuery, + kDNSServiceInterfaceIndexAny, args->full_service_name, kDNSServiceType_TXT, + kDNSServiceClass_IN, _mdns_text_record_query_callback, buddy)) { + + purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", buddy->name, buddy->ip, buddy->port_p2pj); + + idata->txt_query_handler = purple_input_add(DNSServiceRefSockFD(idata->txt_query), + PURPLE_INPUT_READ, _mdns_handle_event, idata->txt_query); bonjour_buddy_add_to_purple(buddy); - - purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", buddy->name, buddy->ip, buddy->port_p2pj); } else bonjour_buddy_delete(buddy); @@ -138,6 +143,7 @@ /* free the remaining args memory */ purple_dnsquery_destroy(args->query); + g_free(args->full_service_name); g_free(args); } @@ -165,13 +171,14 @@ _mdns_parse_text_record(args->buddy, txtRecord, txtLen); /* set more arguments, and start the host resolver */ - args->buddy->full_service_name = g_strdup(fullname); + args->full_service_name = g_strdup(fullname); if (!(args->query = purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args))) { purple_debug_error("bonjour", "service resolver - host resolution failed.\n"); bonjour_buddy_delete(args->buddy); + g_free(args->full_service_name); g_free(args); } } @@ -180,11 +187,11 @@ static void DNSSD_API _mdns_service_register_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, - const char *name, const char *regtype, const char *domain, void *context) -{ - /* we don't actually care about anything said in this callback - this is only here because Bonjour for windows is broken */ + const char *name, const char *regtype, const char *domain, void *context) { + + /* TODO: deal with collision */ if (kDNSServiceErr_NoError != errorCode) - purple_debug_error("bonjour", "service advertisement - callback error.\n"); + purple_debug_error("bonjour", "service advertisement - callback error (%d).\n", errorCode); else purple_debug_info("bonjour", "service advertisement - callback.\n"); } @@ -301,14 +308,15 @@ switch (type) { case PUBLISH_START: - purple_debug_info("bonjour", "Registering service on port %d\n", data->port_p2pj); - err = DNSServiceRegister(&idata->advertisement, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE, + purple_debug_info("bonjour", "Registering presence on port %d\n", data->port_p2pj); + err = DNSServiceRegister(&idata->presence_svc, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL, NULL, htons(data->port_p2pj), TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), _mdns_service_register_callback, NULL); break; case PUBLISH_UPDATE: - err = DNSServiceUpdateRecord(idata->advertisement, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0); + purple_debug_info("bonjour", "Updating presence.\n"); + err = DNSServiceUpdateRecord(idata->presence_svc, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0); break; } @@ -316,9 +324,12 @@ purple_debug_error("bonjour", "Failed to publish presence service.\n"); ret = FALSE; } else if (type == PUBLISH_START) { - /* hack: Bonjour on windows is broken. We don't care about the callback but we have to listen anyway */ - gint fd = DNSServiceRefSockFD(idata->advertisement); - idata->advertisement_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, idata->advertisement); + /* We need to do this because according to the Apple docs: + * "the client is responsible for ensuring that DNSServiceProcessResult() is called + * whenever there is a reply from the daemon - the daemon may terminate its connection + * with a client that does not process the daemon's responses */ + idata->presence_handler = purple_input_add(DNSServiceRefSockFD(idata->presence_svc), + PURPLE_INPUT_READ, _mdns_handle_event, idata->presence_svc); } } @@ -332,37 +343,64 @@ g_return_val_if_fail(idata != NULL, FALSE); - return (DNSServiceBrowse(&idata->browser, 0, 0, ICHAT_SERVICE, NULL, + if (DNSServiceBrowse(&idata->browser_svc, 0, 0, ICHAT_SERVICE, NULL, _mdns_service_browse_callback, data->account) - == kDNSServiceErr_NoError); -} + == kDNSServiceErr_NoError) { + idata->browser_handler = purple_input_add(DNSServiceRefSockFD(idata->browser_svc), + PURPLE_INPUT_READ, _mdns_handle_event, idata->browser_svc); + return TRUE; + } -guint _mdns_register_to_mainloop(BonjourDnsSd *data) { - Win32SessionImplData *idata = data->mdns_impl_data; - - g_return_val_if_fail(idata != NULL, 0); - - return purple_input_add(DNSServiceRefSockFD(idata->browser), - PURPLE_INPUT_READ, _mdns_handle_event, idata->browser); + return FALSE; } void _mdns_stop(BonjourDnsSd *data) { Win32SessionImplData *idata = data->mdns_impl_data; - if (idata == NULL || idata->advertisement == NULL || idata->browser == NULL) + if (idata == NULL) return; - /* hack: for win32, we need to stop listening to the advertisement pipe too */ - purple_input_remove(idata->advertisement_handler); + if (idata->presence_svc != NULL) { + purple_input_remove(idata->presence_handler); + DNSServiceRefDeallocate(idata->presence_svc); + } - DNSServiceRefDeallocate(idata->advertisement); - DNSServiceRefDeallocate(idata->browser); + if (idata->browser_svc != NULL) { + purple_input_remove(idata->browser_handler); + DNSServiceRefDeallocate(idata->browser_svc); + } g_free(idata); data->mdns_impl_data = NULL; } +gboolean _mdns_set_buddy_icon_data(BonjourDnsSd *data, gconstpointer avatar_data, gsize avatar_len) { + Win32SessionImplData *idata = data->mdns_impl_data; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + g_return_val_if_fail(idata != NULL, FALSE); + + if (avatar_data != NULL && idata->buddy_icon_rec == NULL) { + purple_debug_info("bonjour", "Setting new buddy icon.\n"); + err = DNSServiceAddRecord(idata->presence_svc, &idata->buddy_icon_rec, + 0, kDNSServiceType_NULL, avatar_len, avatar_data, 0); + } else if (avatar_data != NULL) { + purple_debug_info("bonjour", "Updating existing buddy icon.\n"); + err = DNSServiceUpdateRecord(idata->presence_svc, idata->buddy_icon_rec, + 0, avatar_len, avatar_data, 0); + } else if (idata->buddy_icon_rec != NULL) { + purple_debug_info("bonjour", "Removing existing buddy icon.\n"); + DNSServiceRemoveRecord(idata->presence_svc, idata->buddy_icon_rec, 0); + idata->buddy_icon_rec = NULL; + } + + if (err != kDNSServiceErr_NoError) + purple_debug_error("bonjour", "Error (%d) setting buddy icon record.\n", err); + + return (err == kDNSServiceErr_NoError); +} + void _mdns_init_buddy(BonjourBuddy *buddy) { buddy->mdns_impl_data = g_new0(Win32BuddyImplData, 1); } @@ -387,8 +425,9 @@ buddy->mdns_impl_data = NULL; } -void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy) { +void _mdns_retrieve_buddy_icon(BonjourBuddy* buddy) { Win32BuddyImplData *idata = buddy->mdns_impl_data; + char svc_name[kDNSServiceMaxDomainName]; g_return_if_fail(idata != NULL); @@ -400,10 +439,11 @@ idata->null_query = NULL; } - if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->null_query, 0, 0, buddy->full_service_name, + DNSServiceConstructFullName(svc_name, buddy->name, ICHAT_SERVICE, "local"); + if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->null_query, 0, kDNSServiceInterfaceIndexAny, svc_name, kDNSServiceType_NULL, kDNSServiceClass_IN, _mdns_text_record_query_callback, buddy)) { - int fd = DNSServiceRefSockFD(idata->null_query); - idata->null_query_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query); + idata->null_query_handler = purple_input_add(DNSServiceRefSockFD(idata->null_query), + PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query); } }