/* libobby - Network text editing library
* Copyright (C) 2005-2006 0x539 dev group
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdexcept>
#include <sstream>
#include <iostream>
#include <assert.h>
#include <avahi-client/client.h>
#include <avahi-client/lookup.h>
#include <avahi-client/publish.h>
#include <avahi-common/simple-watch.h>
#include <avahi-common/error.h>
#include "zeroconf_avahi.hpp"
#include "common.hpp"
namespace obby
{
zeroconf_avahi::zeroconf_avahi()
: m_client(NULL), m_simple_poll(NULL), m_sb(NULL), m_group(NULL)
{
int error;
m_simple_poll = avahi_simple_poll_new();
m_client = avahi_client_new(avahi_simple_poll_get(m_simple_poll),
static_cast<AvahiClientFlags>(0),
&zeroconf_avahi::avahi_client_callback, this, &error);
if(!m_client)
{
std::stringstream stream;
stream << "Failed to create client: " << avahi_strerror(error);
throw std::runtime_error(stream.str() );
}
}
zeroconf_avahi::zeroconf_avahi(const AvahiPoll* poll):
m_client(NULL), m_simple_poll(NULL), m_sb(NULL), m_group(NULL)
{
int error;
m_client = avahi_client_new(poll, static_cast<AvahiClientFlags>(0),
&zeroconf_avahi::avahi_client_callback, this, &error);
if(!m_client)
{
std::stringstream stream;
stream << "Failed to create client: " << avahi_strerror(error);
throw std::runtime_error(stream.str() );
}
}
zeroconf_avahi::~zeroconf_avahi()
{
if(m_group)
avahi_entry_group_free(m_group);
if(m_sb)
avahi_service_browser_free(m_sb);
if(m_client)
avahi_client_free(m_client);
if(m_simple_poll)
avahi_simple_poll_free(m_simple_poll);
}
void zeroconf_avahi::publish(const std::string& name, unsigned int port)
{
if(m_group == NULL)
{
m_group = avahi_entry_group_new(
m_client,
&zeroconf_avahi::avahi_entry_group_callback,
this
);
}
std::string version = "version=";
version += obby_version();
avahi_entry_group_add_service(
m_group,
AVAHI_IF_UNSPEC,
AVAHI_PROTO_INET,
static_cast<AvahiPublishFlags>(0),
name.c_str(),
"_lobby._tcp",
NULL,
NULL,
port,
version.c_str(),
NULL
);
avahi_entry_group_commit(m_group);
}
void zeroconf_avahi::unpublish(const std::string& name)
{
throw std::runtime_error("Not yet implemented");
}
void zeroconf_avahi::unpublish_all()
{
if(m_group != NULL)
{
avahi_entry_group_free(m_group);
m_group = NULL;
}
}
void zeroconf_avahi::discover()
{
/* If there is already a service browser present we need to
* replace it to receive all already emitted servers.
*/
if(m_sb)
{
avahi_service_browser_free(m_sb);
m_sb = NULL;
}
m_sb = avahi_service_browser_new(
m_client,
AVAHI_IF_UNSPEC,
AVAHI_PROTO_INET,
"_lobby._tcp",
NULL,
static_cast<AvahiLookupFlags>(0),
&zeroconf_avahi::avahi_browse_callback,
this
);
if(!m_sb)
throw std::runtime_error(avahi_strerror(avahi_client_errno(m_client)));
}
void zeroconf_avahi::select()
{
assert(m_simple_poll != NULL);
avahi_simple_poll_loop(m_simple_poll);
}
void zeroconf_avahi::select(unsigned int msecs)
{
assert(m_simple_poll != NULL);
/* Work around Avahi bug */
if(msecs == 0) msecs = 1;
avahi_simple_poll_iterate(m_simple_poll, msecs);
}
void zeroconf_avahi::avahi_client_callback(AvahiClient* client,
AvahiClientState state, void* userdata)
{
switch(state)
{
case AVAHI_CLIENT_FAILURE:
std::cerr << "[Avahi] CLIENT-FAILURE: " << avahi_strerror(
avahi_client_errno(client)) << std::endl;
}
}
void zeroconf_avahi::avahi_browse_callback(AvahiServiceBrowser* sb,
AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event,
const char* name, const char* type, const char* domain,
AvahiLookupResultFlags flags, void* userdata)
{
zeroconf_avahi* obj = static_cast<zeroconf_avahi*>(userdata);
AvahiClient* cl = obj->m_client;
switch(event)
{
case AVAHI_BROWSER_FAILURE:
std::cerr << "[Avahi] BROWSER-FAILURE: " << avahi_strerror(
avahi_client_errno(avahi_service_browser_get_client(sb))
) << std::endl;
return;
case AVAHI_BROWSER_NEW:
if(!avahi_service_resolver_new(cl, interface, protocol, name,
type, domain, AVAHI_PROTO_INET,
static_cast<AvahiLookupFlags>(0),
&zeroconf_avahi::avahi_resolve_callback,
userdata))
{
std::cerr << "[Avahi] RESOLVE-FAILURE: " << name << "': "
<< avahi_strerror(avahi_client_errno(cl));
}
break;
case AVAHI_BROWSER_REMOVE:
obj->leave_event().emit(name);
break;
case AVAHI_BROWSER_ALL_FOR_NOW:
case AVAHI_BROWSER_CACHE_EXHAUSTED:
break;
default:
std::cerr << "[Avahi] uncaught event: " << event << std::endl;
}
}
void zeroconf_avahi::avahi_resolve_callback(AvahiServiceResolver* r,
AvahiIfIndex interface, AvahiProtocol protocol,
AvahiResolverEvent event, const char* name, const char* type,
const char* domain, const char* hostname, const AvahiAddress* addr,
uint16_t port, AvahiStringList* txt, AvahiLookupResultFlags flags,
void* userdata)
{
switch(event)
{
case AVAHI_RESOLVER_FOUND:
static_cast<zeroconf_avahi*>(userdata)->discover_event().emit(
name, net6::ipv4_address::create_from_address(
addr->data.ipv4.address, port));
}
/* Clean up */
avahi_service_resolver_free(r);
}
void zeroconf_avahi::avahi_entry_group_callback(AvahiEntryGroup* g,
AvahiEntryGroupState state, void* userdata)
{
}
}
syntax highlighted by Code2HTML, v. 0.9.1