diff options
author | Olli Salli <olli.salli@collabora.co.uk> | 2012-03-26 18:37:06 +0300 |
---|---|---|
committer | Olli Salli <olli.salli@collabora.co.uk> | 2012-03-26 18:38:19 +0300 |
commit | 8f19f6a49cc6affb7b100f6a711f41af0c90eb40 (patch) | |
tree | 44bf06338f3f993262a0c28fea75cbf32ec641ec | |
parent | a7e8bc4c56eeab229147630b06b9ce395018bae3 (diff) | |
parent | 79b2f7bed48e64569291464dbcb7d1b42a67e709 (diff) |
Merge remote-tracking branch 'siraj/fix_review'
Reviewed-by: Olli Salli <olli.salli@collabora.co.uk>
Reviewed-by: Jonny Lamb <jonny.lamb@collabora.co.uk>
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/bonjour-contact-manager.c | 242 | ||||
-rw-r--r-- | src/bonjour-contact.c | 736 | ||||
-rw-r--r-- | src/bonjour-contact.h | 75 | ||||
-rw-r--r-- | src/bonjour-discovery-client.c | 100 | ||||
-rw-r--r-- | src/bonjour-discovery-client.h | 8 | ||||
-rw-r--r-- | src/bonjour-self.c | 41 |
7 files changed, 1051 insertions, 155 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 7968c434..8a985abd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,7 +17,6 @@ noinst_PROGRAMS = write-mgr-file CORE_SOURCES = \ symbol-hacks.c \ symbol-hacks.h \ - capability-set.c \ $(top_srcdir)/salut/capabilities.h \ $(top_srcdir)/salut/capability-set.h \ gabble_namespaces.h \ @@ -101,6 +100,8 @@ AVAHI_BACKEND_SOURCES = \ BONJOUR_BACKEND_SOURCES = \ bonjour-self.h \ bonjour-self.c \ + bonjour-contact.h \ + bonjour-contact.c \ bonjour-contact-manager.h \ bonjour-contact-manager.c \ bonjour-discovery-client.h \ @@ -141,6 +142,7 @@ libsalut_plugins_android_la_LDFLAGS = \ libsalut_plugins_la_LIBADD = \ @TELEPATHY_GLIB_LIBS@ \ + @GLIB_LIBS@ \ @WOCKY_LIBS@ libsalut_plugins_la_SOURCES = \ diff --git a/src/bonjour-contact-manager.c b/src/bonjour-contact-manager.c index 97bedf88..569f6dd8 100644 --- a/src/bonjour-contact-manager.c +++ b/src/bonjour-contact-manager.c @@ -22,10 +22,16 @@ #include <stdlib.h> #include <string.h> +#include <dns_sd.h> +#undef interface + #define DEBUG_FLAG DEBUG_MUC #include "debug.h" +#include "bonjour-contact.h" #include "bonjour-contact-manager.h" +#include "plugin-connection.h" +#include "connection.h" G_DEFINE_TYPE (SalutBonjourContactManager, salut_bonjour_contact_manager, SALUT_TYPE_CONTACT_MANAGER); @@ -43,8 +49,9 @@ typedef struct _SalutBonjourContactManagerPrivate SalutBonjourContactManagerPriv struct _SalutBonjourContactManagerPrivate { SalutBonjourDiscoveryClient *discovery_client; - gpointer *presence_browser; + DNSServiceRef presence_browser; + gboolean all_for_now; gboolean dispose_has_run; }; @@ -55,9 +62,9 @@ struct _SalutBonjourContactManagerPrivate static void salut_bonjour_contact_manager_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) + guint property_id, + GValue *value, + GParamSpec *pspec) { SalutBonjourContactManager *chan = SALUT_BONJOUR_CONTACT_MANAGER (object); SalutBonjourContactManagerPrivate *priv = @@ -75,9 +82,9 @@ salut_bonjour_contact_manager_get_property (GObject *object, static void salut_bonjour_contact_manager_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) + guint property_id, + const GValue *value, + GParamSpec *pspec) { SalutBonjourContactManager *chan = SALUT_BONJOUR_CONTACT_MANAGER (object); SalutBonjourContactManagerPrivate *priv = @@ -102,154 +109,153 @@ salut_bonjour_contact_manager_init (SalutBonjourContactManager *self) self->priv = priv; + priv->all_for_now = FALSE; priv->discovery_client = NULL; } static SalutContact * salut_bonjour_contact_manager_create_contact (SalutContactManager *mgr, - const gchar *name) + const gchar *name) { - /* SalutBonjourContactManager *self = SALUT_BONJOUR_CONTACT_MANAGER (mgr); SalutBonjourContactManagerPrivate *priv = SALUT_BONJOUR_CONTACT_MANAGER_GET_PRIVATE (self); - SALUT_CONTACT (salut_bonjour_contact_new (mgr->connection, - name, priv->discovery_client)); - */ - return NULL; + return SALUT_CONTACT (salut_bonjour_contact_new (mgr->connection, + name, priv->discovery_client)); } -/* -static void -browser_found (GaServiceBrowser *browser, - BonjourIfIndex interface, - BonjourProtocol protocol, - const char *name, - const char *type, - const char *domain, - GaLookupResultFlags flags, - SalutBonjourContactManager *self) + +static void DNSSD_API +_salut_bonjour_service_browse_cb (DNSServiceRef service, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType error_type, + const char *name, + const char *regtype, + const char *domain, + void *context) { + SalutBonjourContactManager *self = SALUT_BONJOUR_CONTACT_MANAGER (context); SalutContactManager *mgr = SALUT_CONTACT_MANAGER (self); + SalutBonjourContactManagerPrivate *priv = + SALUT_BONJOUR_CONTACT_MANAGER_GET_PRIVATE (self); SalutContact *contact; - const char *contact_name = name; + const char *self_contact_name; - if (flags & BONJOUR_LOOKUP_RESULT_OUR_OWN) - return; - - contact = g_hash_table_lookup (mgr->contacts, contact_name); - if (contact == NULL) - { - contact = salut_bonjour_contact_manager_create_contact (mgr, contact_name); - salut_contact_manager_contact_created (mgr, contact); - } - else if (!salut_bonjour_contact_has_services (SALUT_BONJOUR_CONTACT (contact))) + if (error_type != kDNSServiceErr_NoError) { - g_object_ref (contact); + DEBUG ("Browser Failed with : (%d)", error_type); + salut_bonjour_discovery_client_drop_svc_ref (priv->discovery_client, + priv->presence_browser); + return; } - if (!salut_bonjour_contact_add_service (SALUT_BONJOUR_CONTACT (contact), - interface, protocol, name, type, domain)) - { - if (!salut_bonjour_contact_has_services (SALUT_BONJOUR_CONTACT (contact))) - g_object_unref (contact); - } - else - { - WockyContactFactory *contact_factory; - - contact_factory = wocky_session_get_contact_factory ( - mgr->connection->session); - - wocky_contact_factory_add_ll_contact (contact_factory, - WOCKY_LL_CONTACT (contact)); - } -} - -static void -browser_removed (GaServiceBrowser *browser, - BonjourIfIndex interface, - BonjourProtocol protocol, - const char *name, - const char *type, - const char *domain, - GaLookupResultFlags flags, - SalutBonjourContactManager *self) -{ - SalutContactManager *mgr = SALUT_CONTACT_MANAGER (self); - SalutContact *contact; - const char *contact_name = name; + self_contact_name = salut_connection_get_name (SALUT_PLUGIN_CONNECTION ( + mgr->connection)); - DEBUG ("Browser removed for %s", name); + if (g_ascii_strcasecmp (name, self_contact_name) == 0) + return; - contact = g_hash_table_lookup (mgr->contacts, contact_name); - if (contact != NULL) + if (flags & kDNSServiceFlagsAdd) { - salut_bonjour_contact_remove_service (SALUT_BONJOUR_CONTACT (contact), - interface, protocol, name, type, domain); - if (!salut_bonjour_contact_has_services (SALUT_BONJOUR_CONTACT (contact))) - g_object_unref (contact); - + DEBUG ("New Service : %s, on iface : %u, with domain %s of type %s", + name, interfaceIndex, domain, regtype); + + contact = g_hash_table_lookup (mgr->contacts, name); + + if (contact == NULL) + { + contact = salut_bonjour_contact_manager_create_contact (mgr, name); + salut_contact_manager_contact_created (mgr, contact); + } + else if (!salut_bonjour_contact_has_services (SALUT_BONJOUR_CONTACT + (contact))) + { + g_object_ref (contact); + } + + if (!salut_bonjour_contact_add_service (SALUT_BONJOUR_CONTACT (contact), + interfaceIndex, name, regtype, domain)) + { + /* If we couldn't add the server check the refcounting */ + if (!salut_bonjour_contact_has_services (SALUT_BONJOUR_CONTACT (contact))) + g_object_unref (contact); + } + else + { + WockyContactFactory *contact_factory; + + contact_factory = wocky_session_get_contact_factory ( + mgr->connection->session); + wocky_contact_factory_add_ll_contact (contact_factory, + WOCKY_LL_CONTACT (contact)); + } } else { - DEBUG ("Unknown contact removed from service browser"); + DEBUG ("Contact Removed : %s", name); + contact = g_hash_table_lookup (mgr->contacts, name); + + if (contact != NULL) + { + salut_bonjour_contact_remove_service (SALUT_BONJOUR_CONTACT (contact), + interfaceIndex, name, regtype, domain); + if (!salut_bonjour_contact_has_services + (SALUT_BONJOUR_CONTACT (contact))) + { + g_object_unref (contact); + } + } + else + { + DEBUG ("Unknown Contact Removed from Service Browser"); + } } -} -static void -browser_failed (GaServiceBrowser *browser, - GError *error, - SalutBonjourContactManager *self) -{ - g_warning ("browser failed -> %s", error->message); + if (!priv->all_for_now && !(flags & kDNSServiceFlagsMoreComing)) + { + g_signal_emit_by_name (self, "all-for-now"); + priv->all_for_now = TRUE; + } } - -static void -browser_all_for_now (GaServiceBrowser *browser, - SalutBonjourContactManager *self) -{ - g_signal_emit_by_name (self, "all-for-now"); -} -*/ static gboolean salut_bonjour_contact_manager_start (SalutContactManager *mgr, - GError **error) + GError **error) { -/* SalutBonjourContactManager *self = SALUT_BONJOUR_CONTACT_MANAGER (mgr); SalutBonjourContactManagerPrivate *priv = SALUT_BONJOUR_CONTACT_MANAGER_GET_PRIVATE (self); - g_signal_connect (priv->presence_browser, "new-service", - G_CALLBACK (browser_found), mgr); - g_signal_connect (priv->presence_browser, "removed-service", - G_CALLBACK (browser_removed), mgr); - g_signal_connect (priv->presence_browser, "failure", - G_CALLBACK (browser_failed), mgr); - g_signal_connect (priv->presence_browser, "all-for-now", - G_CALLBACK (browser_all_for_now), mgr); - - if (!ga_service_browser_attach (priv->presence_browser, - priv->discovery_client->bonjour_client, error)) + DNSServiceErrorType error_type = kDNSServiceErr_NoError; + const gchar *dnssd_name = + salut_bonjour_discovery_client_get_dnssd_name (priv->discovery_client); + + error_type = DNSServiceBrowse (&priv->presence_browser, 0, + kDNSServiceInterfaceIndexAny, dnssd_name, NULL, + _salut_bonjour_service_browse_cb, self); + + if (error_type != kDNSServiceErr_NoError) { - DEBUG ("browser attach failed"); + *error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE, + "Service Browse Failed with : (%d)", error_type); return FALSE; } -*/ + + salut_bonjour_discovery_client_watch_svc_ref (priv->discovery_client, + priv->presence_browser); + return TRUE; } static void salut_bonjour_contact_manager_dispose_contact (SalutContactManager *mgr, - SalutContact *contact) + SalutContact *contact) { - /* if (salut_bonjour_contact_has_services (SALUT_BONJOUR_CONTACT (contact))) { + /* We reffed this contact as it has services */ g_object_unref (contact); } - */ } static void @@ -259,11 +265,8 @@ salut_bonjour_contact_manager_close_all (SalutContactManager *mgr) SalutBonjourContactManagerPrivate *priv = SALUT_BONJOUR_CONTACT_MANAGER_GET_PRIVATE (self); - if (priv->presence_browser != NULL) - { - g_object_unref (priv->presence_browser); - priv->presence_browser = NULL; - } + salut_bonjour_discovery_client_drop_svc_ref (priv->discovery_client, + priv->presence_browser); if (priv->discovery_client != NULL) { @@ -275,17 +278,6 @@ salut_bonjour_contact_manager_close_all (SalutContactManager *mgr) static void salut_bonjour_contact_manager_constructed (GObject *object) { - SalutBonjourContactManager *self = SALUT_BONJOUR_CONTACT_MANAGER (object); - SalutBonjourContactManagerPrivate *priv = - SALUT_BONJOUR_CONTACT_MANAGER_GET_PRIVATE (self); - /* - const gchar *dnssd_name = salut_bonjour_discovery_client_get_dnssd_name ( - priv->discovery_client); - - ga_service_browser_new ((gchar *) dnssd_name); - */ - priv->presence_browser = NULL; - if (G_OBJECT_CLASS (salut_bonjour_contact_manager_parent_class)->constructed) G_OBJECT_CLASS (salut_bonjour_contact_manager_parent_class)->constructed (object); } @@ -325,7 +317,7 @@ salut_bonjour_contact_manager_class_init ( SalutBonjourContactManager * salut_bonjour_contact_manager_new (SalutConnection *connection, - SalutBonjourDiscoveryClient *discovery_client) + SalutBonjourDiscoveryClient *discovery_client) { return g_object_new (SALUT_TYPE_BONJOUR_CONTACT_MANAGER, "connection", connection, diff --git a/src/bonjour-contact.c b/src/bonjour-contact.c new file mode 100644 index 00000000..96285a76 --- /dev/null +++ b/src/bonjour-contact.c @@ -0,0 +1,736 @@ +/* + * bonjour-contact.c - Source for SalutBonjourContact + * Copyright (C) 2012 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <dbus/dbus-glib.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#undef interface +#include "bonjour-contact.h" + +#include <telepathy-glib/channel-factory-iface.h> +#include <telepathy-glib/interfaces.h> + +#define DEBUG_FLAG DEBUG_MUC +#include "debug.h" + +G_DEFINE_TYPE (SalutBonjourContact, salut_bonjour_contact, + SALUT_TYPE_CONTACT); + +/* properties */ +enum { + PROP_CLIENT = 1, + LAST_PROP +}; + +/* private structure */ +typedef struct _SalutBonjourContactPrivate SalutBonjourContactPrivate; + +struct _SalutBonjourContactPrivate +{ + SalutBonjourDiscoveryClient *discovery_client; + GSList *resolvers; + + char *full_name; + + gboolean dispose_has_run; +}; + +struct resolverInfo { + uint32_t interface; + const char *name; + const char *type; + const char *domain; +}; + +typedef struct _SalutBonjourResolveCtx +{ + DNSServiceRef resolve_ref; + DNSServiceRef address_ref; + + SalutBonjourContact *contact; + + char *name; + char *type; + char *domain; + uint32_t interface; + struct sockaddr *address; + uint16_t port; + + uint16_t txt_length; + char *txt_record; +} SalutBonjourResolveCtx; + +static void +salut_bonjour_contact_init (SalutBonjourContact *self) +{ + SalutBonjourContactPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + SALUT_TYPE_BONJOUR_CONTACT, SalutBonjourContactPrivate); + + self->priv = priv; + + priv->full_name = NULL; + priv->resolvers = NULL; +} + +static void +_salut_bonjour_resolve_ctx_free (SalutBonjourContact *self, + SalutBonjourResolveCtx *ctx) +{ + SalutBonjourContactPrivate *priv = self->priv; + + if (ctx->address_ref != NULL) + { + salut_bonjour_discovery_client_drop_svc_ref (priv->discovery_client, + ctx->address_ref); + } + + if (ctx->txt_record) + { + g_free (ctx->txt_record); + } + + if (ctx->address) + g_free (ctx->address); + + if (ctx->name) + g_free (ctx->name); + + if (ctx->type) + g_free (ctx->type); + + if (ctx->domain) + g_free (ctx->domain); + + g_slice_free (SalutBonjourResolveCtx, ctx); +} + +static void +salut_bonjour_contact_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + SalutBonjourContact *self = SALUT_BONJOUR_CONTACT (object); + SalutBonjourContactPrivate *priv = self->priv; + + switch (property_id) + { + case PROP_CLIENT: + g_value_set_object (value, priv->discovery_client); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +salut_bonjour_contact_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + SalutBonjourContact *self = SALUT_BONJOUR_CONTACT (object); + SalutBonjourContactPrivate *priv = self->priv; + + switch (property_id) + { + case PROP_CLIENT: + priv->discovery_client = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gchar * +salut_bonjour_contact_dup_jid (WockyContact *contact) +{ + SalutContact *self = SALUT_CONTACT (contact); + + return g_strdup (self->name); +} + +static GList * +salut_bonjour_contact_ll_get_addresses (WockyLLContact *contact) +{ + SalutBonjourContact *self = SALUT_BONJOUR_CONTACT (contact); + SalutBonjourContactPrivate *priv = self->priv; + /* omg, GQueue! */ + GQueue queue = G_QUEUE_INIT; + GSList *l; + + for (l = priv->resolvers; l != NULL; l = l->next) + { + SalutBonjourResolveCtx *ctx = l->data; + uint16_t port; + + if (ctx->address) + { + GInetAddress *addr; + GSocketAddress *socket_address; + + if (ctx->address->sa_family == AF_INET) + { + struct sockaddr_in *address = (struct sockaddr_in *) ctx->address; + + port = ctx->port; + addr = g_inet_address_new_from_bytes ( + (guint8 *) &(address->sin_addr), G_SOCKET_FAMILY_IPV4); + } + else if (ctx->address->sa_family == AF_INET6) + { + struct sockaddr_in6 *address = (struct sockaddr_in6 *) ctx->address; + port = ctx->port; + addr = g_inet_address_new_from_bytes ( + (uint8_t *) &(address->sin6_addr), G_SOCKET_FAMILY_IPV6); + } + else + g_assert_not_reached (); + + socket_address = g_inet_socket_address_new (addr, port); + g_object_unref (addr); + + g_queue_push_tail (&queue, socket_address); + } + } + + return queue.head; +} + +static void +_bonjour_add_address (SalutBonjourResolveCtx *ctx, + GArray *addresses) +{ + salut_contact_address_t s_address; + const struct sockaddr *address = ctx->address; + + if (ctx->address) + { + if (address->sa_family == AF_INET) + { + struct sockaddr_in *_addr4 = + (struct sockaddr_in *) &s_address.address; + struct sockaddr_in *address4 = (struct sockaddr_in *) ctx->address; + + _addr4->sin_family = AF_INET; + _addr4->sin_port = address4->sin_port; + _addr4->sin_addr.s_addr = address4->sin_addr.s_addr; + } + else if (address->sa_family == AF_INET6) + { + struct sockaddr_in6 *_addr6 = + (struct sockaddr_in6 *) &s_address.address; + struct sockaddr_in6 *address6 = (struct sockaddr_in6 *) ctx->address; + + _addr6->sin6_family = AF_INET6; + _addr6->sin6_port = address6->sin6_port; + _addr6->sin6_flowinfo = 0; + _addr6->sin6_scope_id = address6->sin6_scope_id; + memcpy (_addr6->sin6_addr.s6_addr, address6->sin6_addr.s6_addr, 16); + } + else + g_assert_not_reached (); + + g_array_append_val (addresses, s_address); + } +} + +static GArray * +salut_bonjour_contact_get_addresses (SalutContact *self) +{ + SalutBonjourContact *_self = SALUT_BONJOUR_CONTACT (self); + SalutBonjourContactPrivate *priv = _self->priv; + GArray *addresses; + + addresses = + g_array_sized_new (TRUE, TRUE, sizeof (salut_contact_address_t), + g_slist_length (priv->resolvers)); + g_slist_foreach (priv->resolvers, (GFunc) _bonjour_add_address, addresses); + + return addresses; +} + +static gint +_compare_sockaddr (SalutBonjourResolveCtx *ctx, + struct sockaddr *b) +{ + const struct sockaddr *a = ctx->address; + + if (a->sa_family != b->sa_family) + return -1; + + if (a->sa_family == AF_INET) + { + struct sockaddr_in *a4 = (struct sockaddr_in *) a; + struct sockaddr_in *b4 = (struct sockaddr_in *) b; + + return a4->sin_addr.s_addr - b4->sin_addr.s_addr; + } + else if (a->sa_family == AF_INET6) + { + struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) a; + struct sockaddr_in6 *b6 = (struct sockaddr_in6 *) b; + + return memcmp (a6->sin6_addr.s6_addr, b6->sin6_addr.s6_addr, 16); + } + else + g_assert_not_reached (); + + return 0; +} + +static gint +compare_resolver (SalutBonjourResolveCtx *a, + struct resolverInfo *b) +{ + gint result; + + if (a->interface == b->interface + && !tp_strdiff (a->name, b->name) + && !tp_strdiff (a->type, b->type) + && !tp_strdiff (a->domain, b->domain)) + { + result = 0; + } + else + { + result = 1; + } + + return result; +} + +static SalutBonjourResolveCtx * +find_resolver (SalutBonjourContact *contact, + uint32_t interface, + const char *name, + const char *type, + const char *domain) +{ + SalutBonjourContactPrivate *priv = contact->priv; + struct resolverInfo info; + GSList *ret; + info.interface = interface; + info.name = name; + info.type = type; + info.domain = domain; + + ret = g_slist_find_custom (priv->resolvers, &info, + (GCompareFunc) compare_resolver); + + return ret ? (SalutBonjourResolveCtx *) ret->data : NULL; +} + +static gboolean +salut_bonjour_contact_has_address (SalutContact *contact, + struct sockaddr *address, + guint size) +{ + SalutBonjourContact *_self = SALUT_BONJOUR_CONTACT (contact); + SalutBonjourContactPrivate *priv = _self->priv; + + return (g_slist_find_custom (priv->resolvers, address, + (GCompareFunc) _compare_sockaddr) != NULL); +} + +static void salut_bonjour_contact_dispose (GObject *object); + +static void +salut_bonjour_contact_class_init ( + SalutBonjourContactClass *salut_bonjour_contact_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (salut_bonjour_contact_class); + SalutContactClass *contact_class = SALUT_CONTACT_CLASS ( + salut_bonjour_contact_class); + WockyContactClass *w_contact_class = WOCKY_CONTACT_CLASS ( + salut_bonjour_contact_class); + WockyLLContactClass *ll_contact_class = WOCKY_LL_CONTACT_CLASS ( + salut_bonjour_contact_class); + GParamSpec *param_spec; + + g_type_class_add_private (salut_bonjour_contact_class, + sizeof (SalutBonjourContactPrivate)); + + object_class->get_property = salut_bonjour_contact_get_property; + object_class->set_property = salut_bonjour_contact_set_property; + + object_class->dispose = salut_bonjour_contact_dispose; + + contact_class->get_addresses = salut_bonjour_contact_get_addresses; + contact_class->has_address = salut_bonjour_contact_has_address; + contact_class->retrieve_avatar = NULL; + + w_contact_class->dup_jid = salut_bonjour_contact_dup_jid; + ll_contact_class->get_addresses = salut_bonjour_contact_ll_get_addresses; + + param_spec = g_param_spec_object ( + "discovery-client", + "SalutBonjourDiscoveryClient object", + "The Salut Bonjour Discovery client associated with this muc manager", + SALUT_TYPE_BONJOUR_DISCOVERY_CLIENT, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_CLIENT, + param_spec); +} + +void +salut_bonjour_contact_dispose (GObject *object) +{ + SalutBonjourContact *self = SALUT_BONJOUR_CONTACT (object); + SalutBonjourContactPrivate *priv = self->priv; + GSList *l; + + if (priv->dispose_has_run) + return; + + priv->dispose_has_run = TRUE; + + for (l = priv->resolvers; l != NULL; l = l->next) + { + SalutBonjourResolveCtx *ctx = l->data; + + salut_bonjour_discovery_client_drop_svc_ref (priv->discovery_client, + ctx->resolve_ref); + _salut_bonjour_resolve_ctx_free (self, ctx); + } + + g_slist_free (priv->resolvers); + priv->resolvers = NULL; + + if (priv->discovery_client != NULL) + { + g_object_unref (priv->discovery_client); + priv->discovery_client = NULL; + } + + if (priv->full_name) + { + g_free (priv->full_name); + priv->full_name = NULL; + } + + if (G_OBJECT_CLASS (salut_bonjour_contact_parent_class)->dispose) + G_OBJECT_CLASS (salut_bonjour_contact_parent_class)->dispose (object); +} + +/* public functions */ +SalutBonjourContact * +salut_bonjour_contact_new (SalutConnection *connection, + const gchar *name, + SalutBonjourDiscoveryClient *discovery_client) +{ + g_assert (connection != NULL); + g_assert (name != NULL); + g_assert (discovery_client != NULL); + + return g_object_new (SALUT_TYPE_BONJOUR_CONTACT, + "connection", connection, + "name", name, + "discovery-client", discovery_client, + NULL); +} + +static void +update_alias (SalutBonjourContact *self, + const gchar *nick) +{ + SalutContact *contact = SALUT_CONTACT (self); + + if (!tp_str_empty (nick)) + { + salut_contact_change_alias (contact, nick); + return; + } + + if (!tp_str_empty (contact->full_name)) + { + salut_contact_change_alias (contact, contact->full_name); + return; + } + + salut_contact_change_alias (contact, NULL); +} + +static void DNSSD_API +_bonjour_getaddr_cb (DNSServiceRef service_ref, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType error_type, + const char *host_name, + const struct sockaddr *address, + uint32_t ttl, + void *context) +{ + SalutBonjourResolveCtx *ctx = (SalutBonjourResolveCtx *) context; + SalutBonjourContact *self = SALUT_BONJOUR_CONTACT (ctx->contact); + SalutBonjourContactPrivate *priv = self->priv; + SalutContact *contact = SALUT_CONTACT (self); + const char *txt_record = ctx->txt_record; + uint32_t txt_length = ctx->txt_length; + char *status, *status_message, *nick, *first, *last; + char *node, *hash, *ver; + char *email, *jid; + char *tmp; + uint8_t txt_len; + + if (error_type != kDNSServiceErr_NoError) + { + DEBUG ("Resolver failed with : (%d)", error_type); + salut_bonjour_discovery_client_drop_svc_ref (priv->discovery_client, + ctx->address_ref); + ctx->address_ref = NULL; + g_free (ctx->txt_record); + ctx->txt_record = NULL; + return; + } + + if (ctx->address) + { + g_free (ctx->address); + ctx->address = NULL; + } + + if (address->sa_family == AF_INET) + ctx->address = g_memdup (address, sizeof (struct sockaddr_in)); + else if (address->sa_family == AF_INET6) + ctx->address = g_memdup (address, sizeof (struct sockaddr_in6)); + else + g_assert_not_reached (); + + salut_bonjour_discovery_client_drop_svc_ref (priv->discovery_client, + ctx->address_ref); + ctx->address_ref = NULL; + + salut_contact_freeze (contact); + + /* status */ + tmp = (char *) TXTRecordGetValuePtr + (txt_length, txt_record, "status", &txt_len); + status = g_strndup (tmp, txt_len); + + if (status != NULL) + { + for (int i = 0; i < SALUT_PRESENCE_NR_PRESENCES; i++) + { + if (tp_strdiff (status, salut_presence_status_txt_names[i])) + { + salut_contact_change_status (contact, i); + break; + } + } + free (status); + } + + /* status message */ + tmp = (char *) TXTRecordGetValuePtr (txt_length, txt_record, + "msg", &txt_len); + status_message = g_strndup (tmp, txt_len); + salut_contact_change_status_message (contact, status_message); + free (status_message); + + /* real name and nick */ + tmp = (char *) TXTRecordGetValuePtr (txt_length, txt_record, + "nick", &txt_len); + nick = g_strndup (tmp, txt_len); + tmp = (char *) TXTRecordGetValuePtr (txt_length, txt_record, + "1st", &txt_len); + first = g_strndup (tmp, txt_len); + tmp = (char *) TXTRecordGetValuePtr (txt_length, txt_record, + "last", &txt_len); + last = g_strndup (tmp, txt_len); + + salut_contact_change_real_name (contact, first, last); + update_alias (self, nick); + + free (nick); + free (first); + free (last); + + /* capabilities */ + tmp = (char *) TXTRecordGetValuePtr (txt_length, txt_record, + "hash", &txt_len); + hash = g_strndup (tmp, txt_len); + + tmp = (char *) TXTRecordGetValuePtr (txt_length, txt_record, + "node", &txt_len); + node = g_strndup (tmp, txt_len); + + tmp = (char *) TXTRecordGetValuePtr (txt_length, txt_record, + "ver", &txt_len); + ver = g_strndup (tmp, txt_len); + + salut_contact_change_capabilities (contact, hash, node, ver); + + free (hash); + free (node); + free (ver); + + /* email */ + tmp = (char *) TXTRecordGetValuePtr (txt_length, txt_record, + "email", &txt_len); + email = g_strndup (tmp, txt_len); + tmp = (char *) TXTRecordGetValuePtr (txt_length, txt_record, + "jid", &txt_len); + jid = g_strndup (tmp, txt_len); + + salut_contact_change_email (contact, email); + salut_contact_change_jid (contact, jid); + + free (email); + free (jid); + + DEBUG ("Announce Contact Found"); + salut_contact_found (contact); + salut_contact_thaw (contact); + + g_free (ctx->txt_record); + ctx->txt_length = 0; + ctx->txt_record = NULL; +} + +static void DNSSD_API +_bonjour_service_resolve_cb (DNSServiceRef service_ref, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType error_type, + const char *full_name, + const char *host_target, + uint16_t port, + uint16_t txt_length, + const unsigned char *txt_record, + void *context) +{ + SalutBonjourResolveCtx *ctx = (SalutBonjourResolveCtx *) context; + SalutBonjourContact *self = SALUT_BONJOUR_CONTACT (ctx->contact); + SalutBonjourContactPrivate *priv = self->priv; + DNSServiceErrorType _error_type = kDNSServiceErr_NoError; + + if (ctx->address_ref != NULL) + { + salut_bonjour_discovery_client_drop_svc_ref (priv->discovery_client, + ctx->address_ref); + ctx->address_ref = NULL; + } + + if (ctx->txt_record != NULL) + { + g_free (ctx->txt_record); + ctx->txt_record = NULL; + } + + ctx->txt_record = g_strndup ((const gchar *) txt_record, (guint) txt_length); + ctx->txt_length = txt_length; + ctx->port = ntohs (port); + + _error_type = DNSServiceGetAddrInfo (&ctx->address_ref, 0, + interfaceIndex, kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, + host_target, _bonjour_getaddr_cb, ctx); + + if (_error_type != kDNSServiceErr_NoError) + { + DEBUG ("Address resolving failed with : (%d)", _error_type); + return; + } + + salut_bonjour_discovery_client_watch_svc_ref (priv->discovery_client, + ctx->address_ref); +} + +void +salut_bonjour_contact_remove_service (SalutBonjourContact *self, + uint32_t interface, + const char *name, + const char *type, + const char *domain) +{ + SalutBonjourContactPrivate *priv = self->priv; + SalutBonjourResolveCtx *ctx = NULL; + + ctx = find_resolver (self, interface, name, type, domain); + + if (ctx == NULL) + return; + + salut_bonjour_discovery_client_drop_svc_ref (priv->discovery_client, + ctx->resolve_ref); + + priv->resolvers = g_slist_remove (priv->resolvers, ctx); + + _salut_bonjour_resolve_ctx_free (self, ctx); + + if (priv->resolvers == NULL) + salut_contact_lost (SALUT_CONTACT (self)); +} + +gboolean +salut_bonjour_contact_add_service (SalutBonjourContact *self, + uint32_t interface, + const char *name, + const char *type, + const char *domain) +{ + SalutBonjourContactPrivate *priv = self->priv; + DNSServiceErrorType error_type = kDNSServiceErr_NoError; + SalutBonjourResolveCtx *ctx = NULL; + + ctx = find_resolver (self, interface, name, type, domain); + if (ctx != NULL) + return TRUE; + + ctx = g_slice_new0 (SalutBonjourResolveCtx); + ctx->interface = interface; + ctx->name = g_strdup (name); + ctx->type = g_strdup (type); + ctx->domain = g_strdup (domain); + ctx->contact = self; + ctx->address = NULL; + ctx->txt_length = 0; + ctx->txt_record = NULL; + ctx->address_ref = NULL; + + error_type = DNSServiceResolve (&ctx->resolve_ref, + 0, interface, name, type, domain, _bonjour_service_resolve_cb, ctx); + + if (error_type != kDNSServiceErr_NoError) + { + DEBUG ("ServiceResolve failed with : (%d)", error_type); + _salut_bonjour_resolve_ctx_free (self, ctx); + return FALSE; + } + + salut_bonjour_discovery_client_watch_svc_ref (priv->discovery_client, + ctx->resolve_ref); + + priv->resolvers = g_slist_prepend (priv->resolvers, ctx); + + return TRUE; +} + +gboolean +salut_bonjour_contact_has_services (SalutBonjourContact *self) +{ + SalutBonjourContactPrivate *priv = self->priv; + + return priv->resolvers != NULL; +} diff --git a/src/bonjour-contact.h b/src/bonjour-contact.h new file mode 100644 index 00000000..a60c0aa8 --- /dev/null +++ b/src/bonjour-contact.h @@ -0,0 +1,75 @@ +/* + * bonjour-contact.h - Header for SalutBonjourContact + * Copyright (C) 2012 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SALUT_BONJOUR_CONTACT_H__ +#define __SALUT_BONJOUR_CONTACT_H__ + +#include <glib-object.h> + +#include "contact.h" +#include "bonjour-discovery-client.h" + +G_BEGIN_DECLS + +typedef struct _SalutBonjourContact SalutBonjourContact; +typedef struct _SalutBonjourContactClass SalutBonjourContactClass; + +struct _SalutBonjourContactClass { + SalutContactClass parent_class; +}; + +struct _SalutBonjourContact { + SalutContact parent; + + gpointer priv; +}; + +GType salut_bonjour_contact_get_type (void); + +/* TYPE MACROS */ +#define SALUT_TYPE_BONJOUR_CONTACT \ + (salut_bonjour_contact_get_type ()) +#define SALUT_BONJOUR_CONTACT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), SALUT_TYPE_BONJOUR_CONTACT, SalutBonjourContact)) +#define SALUT_BONJOUR_CONTACT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), SALUT_TYPE_BONJOUR_CONTACT, SalutBonjourContactClass)) +#define SALUT_IS_BONJOUR_CONTACT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), SALUT_TYPE_BONJOUR_CONTACT)) +#define SALUT_IS_BONJOUR_CONTACT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), SALUT_TYPE_BONJOUR_CONTACT)) +#define SALUT_BONJOUR_CONTACT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), SALUT_TYPE_BONJOUR_CONTACT, SalutBonjourContactClass)) + +SalutBonjourContact * +salut_bonjour_contact_new (SalutConnection *connection, const gchar *name, + SalutBonjourDiscoveryClient *discovery_client); + +gboolean salut_bonjour_contact_add_service (SalutBonjourContact *contact, + uint32_t interface, const char *name, + const char *type, const char *domain); + +void salut_bonjour_contact_remove_service (SalutBonjourContact *contact, + uint32_t interface, const char *name, + const char *type, const char *domain); + +gboolean salut_bonjour_contact_has_services (SalutBonjourContact *contact); + +G_END_DECLS + +#endif /* #ifndef __SALUT_BONJOUR_CONTACT_H__*/ diff --git a/src/bonjour-discovery-client.c b/src/bonjour-discovery-client.c index fb69e0cd..fc9beaeb 100644 --- a/src/bonjour-discovery-client.c +++ b/src/bonjour-discovery-client.c @@ -66,6 +66,9 @@ struct _SalutBonjourDiscoveryClientPrivate gchar *dnssd_name; + GHashTable *svc_ref_table; + GHashTable *svc_source_table; + gboolean dispose_has_run; }; @@ -73,6 +76,10 @@ struct _SalutBonjourDiscoveryClientPrivate ((SalutBonjourDiscoveryClientPrivate *) \ ((SalutBonjourDiscoveryClient *) obj)->priv) +static void _destroy_source_id (gpointer ptr); +static void _destroy_channel (gpointer ptr); +static void _destroy_service (gpointer ptr); + static void salut_bonjour_discovery_client_init (SalutBonjourDiscoveryClient *self) { @@ -81,6 +88,11 @@ salut_bonjour_discovery_client_init (SalutBonjourDiscoveryClient *self) self->priv = priv; priv->dispose_has_run = FALSE; + priv->svc_ref_table = g_hash_table_new_full (g_direct_hash, + g_direct_equal, _destroy_service, _destroy_channel); + + priv->svc_source_table = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, _destroy_source_id); priv->state = SALUT_DISCOVERY_CLIENT_STATE_DISCONNECTED; } @@ -109,6 +121,12 @@ salut_bonjour_discovery_client_dispose (GObject *object) if (priv->dispose_has_run) return; + g_hash_table_remove_all (priv->svc_source_table); + g_hash_table_remove_all (priv->svc_ref_table); + + g_hash_table_unref (priv->svc_source_table); + g_hash_table_unref (priv->svc_ref_table); + priv->dispose_has_run = TRUE; tp_clear_pointer (&priv->dnssd_name, g_free); @@ -254,6 +272,88 @@ salut_bonjour_discovery_client_get_host_name_fqdn (SalutDiscoveryClient *clt) return NULL; } +static gboolean +_bonjour_socket_process_cb (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + DNSServiceRef service_ref = data; + DNSServiceErrorType error_type = kDNSServiceErr_NoError; + + error_type = DNSServiceProcessResult (service_ref); + + if (error_type != kDNSServiceErr_NoError) + { + g_warning ("Socket Processing Failed with : (%d)", error_type); + return FALSE; + } + + return TRUE; +} + +static void +_destroy_service (gpointer service_ptr) +{ + DNSServiceRef service = service_ptr; + + DNSServiceRefDeallocate (service); +} + +static void +_destroy_channel (gpointer channel_ptr) +{ + g_io_channel_unref (channel_ptr); +} + +static void +_destroy_source_id (gpointer source_id) +{ + if (!g_source_remove (GPOINTER_TO_UINT (source_id))) + { + g_warning ("Error removing source"); + } +} + +void +salut_bonjour_discovery_client_watch_svc_ref (SalutBonjourDiscoveryClient *self, + DNSServiceRef service) +{ + SalutBonjourDiscoveryClientPrivate *priv = + SALUT_BONJOUR_DISCOVERY_CLIENT_GET_PRIVATE (self); + GIOChannel *channel = NULL; + guint source_id; + + + channel = g_io_channel_win32_new_socket ( + DNSServiceRefSockFD ((service))); + + source_id = g_io_add_watch (channel, G_IO_IN, + _bonjour_socket_process_cb, service); + + g_hash_table_insert (priv->svc_ref_table, service, channel); + g_hash_table_insert (priv->svc_source_table, channel, + GUINT_TO_POINTER (source_id)); +} + +void +salut_bonjour_discovery_client_drop_svc_ref (SalutBonjourDiscoveryClient *self, + DNSServiceRef service) +{ + SalutBonjourDiscoveryClientPrivate *priv = + SALUT_BONJOUR_DISCOVERY_CLIENT_GET_PRIVATE (self); + gpointer channel = NULL; + + if (!g_hash_table_lookup_extended ( + priv->svc_ref_table, service, NULL, &channel)) + return; + + if (!channel) + return; + + g_hash_table_remove (priv->svc_source_table, channel); + g_hash_table_remove (priv->svc_ref_table, service); +} + const gchar * salut_bonjour_discovery_client_get_dnssd_name (SalutBonjourDiscoveryClient *clt) { diff --git a/src/bonjour-discovery-client.h b/src/bonjour-discovery-client.h index afe3dd12..40798674 100644 --- a/src/bonjour-discovery-client.h +++ b/src/bonjour-discovery-client.h @@ -48,6 +48,14 @@ GType salut_bonjour_discovery_client_get_type (void); const gchar * salut_bonjour_discovery_client_get_dnssd_name ( SalutBonjourDiscoveryClient *self); +void salut_bonjour_discovery_client_watch_svc_ref ( + SalutBonjourDiscoveryClient *self, + DNSServiceRef service); + +void salut_bonjour_discovery_client_drop_svc_ref ( + SalutBonjourDiscoveryClient *self, + DNSServiceRef service); + /* TYPE MACROS */ #define SALUT_TYPE_BONJOUR_DISCOVERY_CLIENT \ (salut_bonjour_discovery_client_get_type ()) diff --git a/src/bonjour-self.c b/src/bonjour-self.c index 856af022..518db17b 100644 --- a/src/bonjour-self.c +++ b/src/bonjour-self.c @@ -267,25 +267,6 @@ _bonjour_self_init_txt_record_presence (SalutBonjourSelf *self, return kDNSServiceErr_NoError; } -static gboolean -_bonjour_socket_process_cb (GIOChannel *source, - GIOCondition condition, - gpointer data) -{ - DNSServiceRef service_ref = data; - DNSServiceErrorType error_type = kDNSServiceErr_NoError; - - error_type = DNSServiceProcessResult (service_ref); - - if (error_type != kDNSServiceErr_NoError) - { - DEBUG ("Socket processing failed with : (%d)", error_type); - return FALSE; - } - - return TRUE; -} - static void DNSSD_API _bonjour_service_register_cb (DNSServiceRef service_ref, DNSServiceFlags service_flags, @@ -304,6 +285,8 @@ _bonjour_service_register_cb (DNSServiceRef service_ref, if (!self || error_type != kDNSServiceErr_NoError) { DEBUG ("Service Registration Failed with : (%d)", error_type); + salut_bonjour_discovery_client_drop_svc_ref (priv->discovery_client, + priv->bonjour_service); } else { @@ -324,9 +307,6 @@ _bonjour_service_register_cb (DNSServiceRef service_ref, return; } - _self->name = g_strdup_printf ("%s@%s", _self->published_name, - g_get_host_name ()); - salut_self_established (_self); } } @@ -339,7 +319,6 @@ salut_bonjour_self_announce (SalutSelf *_self, SalutBonjourSelf *self = SALUT_BONJOUR_SELF (_self); SalutBonjourSelfPrivate *priv = self->priv; DNSServiceErrorType error_type = kDNSServiceErr_NoError; - GIOChannel *bonjour_service_channel = NULL; const gchar *dnssd_name; dnssd_name = @@ -351,17 +330,17 @@ salut_bonjour_self_announce (SalutSelf *_self, RETURN_ERROR_IF_FAIL (error_type, error); + _self->name = g_strdup_printf ("%s@%s", _self->published_name, + g_get_host_name ()); + error_type = DNSServiceRegister (&priv->bonjour_service, - kDNSServiceInterfaceIndexAny, 0, NULL, + kDNSServiceInterfaceIndexAny, 0, _self->name, dnssd_name, NULL, NULL, htons (port), 0, NULL, _bonjour_service_register_cb, self); RETURN_ERROR_IF_FAIL (error_type, error); - bonjour_service_channel = g_io_channel_win32_new_socket ( - DNSServiceRefSockFD (priv->bonjour_service)); - - g_io_add_watch (bonjour_service_channel, G_IO_IN, _bonjour_socket_process_cb, + salut_bonjour_discovery_client_watch_svc_ref (priv->discovery_client, priv->bonjour_service); return TRUE; @@ -386,7 +365,8 @@ salut_bonjour_self_set_presence (SalutSelf *self, error_type = TXTRecordSetValue (&priv->txt_record_presence, "msg", strlen (self->status_message), self->status_message); } - else + else if ((TXTRecordContainsKey (TXTRecordGetLength (&priv->txt_record_presence), + &priv->txt_record_presence, "msg")) == TRUE) { error_type = TXTRecordRemoveValue (&priv->txt_record_presence, "msg"); } @@ -565,6 +545,9 @@ salut_bonjour_self_dispose (GObject *object) priv->dispose_has_run = TRUE; + salut_bonjour_discovery_client_drop_svc_ref (priv->discovery_client, + priv->bonjour_service); + if (priv->discovery_client != NULL) { g_object_unref (priv->discovery_client); |