Browse Source

Initial commit

master
Stefan Bühler 9 years ago
commit
1ac6367f25

+ 8
- 0
.gitignore View File

@@ -0,0 +1,8 @@
*.pro.user
Makefile
moc_*
*.o
*.so.*
ui_*
*.so
qcp/qcp

+ 16
- 0
libqcp/libqcp.pro View File

@@ -0,0 +1,16 @@
# -------------------------------------------------
# Project created by QtCreator 2010-07-21T19:59:16
# -------------------------------------------------
QT += network

# QT -= gui
TARGET = qcp
TEMPLATE = lib
DEFINES += LIBQCP_LIBRARY
SOURCES += qcp.cpp \
qcpmodel.cpp
HEADERS += qcp.h \
libqcp_global.h \
qcpmodel.h
TARGETDEPS += ../libqtavahi/libqtavahi.so
LIBS += ../libqtavahi/libqtavahi.so

+ 12
- 0
libqcp/libqcp_global.h View File

@@ -0,0 +1,12 @@
#ifndef LIBQCP_GLOBAL_H
#define LIBQCP_GLOBAL_H

#include <QtCore/qglobal.h>

#if defined(LIBQCP_LIBRARY)
# define LIBQCPSHARED_EXPORT Q_DECL_EXPORT
#else
# define LIBQCPSHARED_EXPORT Q_DECL_IMPORT
#endif

#endif // LIBQCP_GLOBAL_H

+ 61
- 0
libqcp/qcp.cpp View File

@@ -0,0 +1,61 @@
#include "qcp.h"

#define SERVICETYPE "_qcp._tcp"

namespace QCP {
Browse::Browse(QObject *parent)
: QObject(parent), m_av_browse(new Avahi::Browse(SERVICETYPE, this)) {
connect(m_av_browse, SIGNAL(found(Avahi::Service)), this, SLOT(foundService(Avahi::Service)));
connect(m_av_browse, SIGNAL(lost(Avahi::Service)), this, SLOT(lostService(Avahi::Service)));
connect(m_av_browse, SIGNAL(started()), this, SIGNAL(started()));
connect(m_av_browse, SIGNAL(stopped()), this, SIGNAL(stopped()));
}

WatchShare *Browse::watch(const Avahi::Service &service, QObject *parent) {
ShareData *sd = shareData(service);
WatchShare *ws;
if (!sd) return 0;

sd->watcherList.append(ws = new WatchShare(parent, Share(this, service)));
ws->m_list_it = sd->watcherList.end()-1;

return ws;
}

void Browse::removeWatch(WatcherList::iterator it, const Avahi::Service &service) {
ShareData *sd = shareData(service);
if (!sd || WatcherList::iterator() == it) return;

sd->watcherList.erase(it);
}

void Browse::foundService(Avahi::Service s) {
ShareData *sd = new ShareData();
s.setData(sd);
emit found(Share(this, s));
}

void Browse::lostService(Avahi::Service s) {
emit lost(Share(this, s));
ShareData *sd = shareData(s);
if (0 != sd) {
while (!sd->watcherList.empty()) {
WatchShare *ws = sd->watcherList.first();
emit ws->lost();
if (ws == sd->watcherList.first()) {
ws->m_list_it = WatcherList::iterator();
sd->watcherList.pop_front();
}
}
delete sd;
}
sd->data = 0;
}


WatchShare *Share::watch(QObject *parent) {
return m_browse->watch(m_service, parent);
}

}


+ 107
- 0
libqcp/qcp.h View File

@@ -0,0 +1,107 @@
#ifndef QCP_H
#define QCP_H

#include "libqcp_global.h"

#include <QObject>
#include <QLinkedList>

#include "../libqtavahi/qtavahi.h"

namespace QCP {
class LIBQCPSHARED_EXPORT Share;
class LIBQCPSHARED_EXPORT WatchShare;
class LIBQCPSHARED_EXPORT Browse;
}

namespace QCP {
/* The private data of the Avahi::Service instances belongs to QCP ! :) */

class LIBQCPSHARED_EXPORT Browse : public QObject {
Q_OBJECT
private:
friend class QCP::Share;
friend class QCP::WatchShare;

typedef QLinkedList<WatchShare*> WatcherList;
struct ShareData {
WatcherList watcherList;
void *data;
};

public:
Browse(QObject *parent = 0);

Avahi::Browse* avahiBrowse() { return m_av_browse; }

inline bool start() { return m_av_browse->start(); }
inline void stop() { m_av_browse->stop(); }

signals:
void found(QCP::Share s);
void lost(QCP::Share s);

void started();
void stopped();

private slots:
void foundService(Avahi::Service s);
void lostService(Avahi::Service s);

private:
WatchShare *watch(const Avahi::Service &service, QObject *parent);
void removeWatch(WatcherList::iterator it, const Avahi::Service &service);

static inline ShareData* shareData(const Avahi::Service &service) {
return static_cast<ShareData*>(service.data());
}

Avahi::Browse *m_av_browse;
};

class LIBQCPSHARED_EXPORT Share {
private:
friend class QCP::Browse;
friend class QCP::WatchShare;

public:
inline Share(QCP::Browse *browse, Avahi::Service service) : m_browse(browse), m_service(service) { }

public:
Avahi::Service avahiService() const { return m_service; }

WatchShare *watch(QObject *parent = 0);

void* data() const { Browse::ShareData *sd = Browse::shareData(m_service); return sd ? sd->data : 0; }
void setData(void *data) const { Browse::ShareData *sd = Browse::shareData(m_service); if (sd) sd->data = data; }

private:
QCP::Browse *m_browse;
Avahi::Service m_service;
};

class LIBQCPSHARED_EXPORT WatchShare : public QObject {
Q_OBJECT

friend class QCP::Share;
friend class QCP::Browse;
private:
inline WatchShare(QObject *parent, Share share) : QObject(parent), m_share(share) { }

public:
virtual ~WatchShare() {
m_share.m_browse->removeWatch(m_list_it, m_share.m_service);
}

inline Share share() { return m_share; }

signals:
void lost();

private:
Share m_share;
Browse::WatcherList::iterator m_list_it;
};
}

#endif // QCP_H

+ 91
- 0
libqcp/qcpmodel.cpp View File

@@ -0,0 +1,91 @@
#include "qcpmodel.h"

namespace QCP {

namespace model {
Entry::Entry(Row *parent, const Avahi::Service &s, const QCP::Share &share)
: Avahi::model::Entry(parent, s), m_share(share) {
}

QVariant Entry::data(int col, int role) {
switch (role) {
case Qt::DisplayRole:
switch (col) {
case 0: return service.hostname();
case 1: return QString("%1:%2").arg(service.address().toString()).arg(service.port());
case 2: return "Hello World!";
}
// case Qt::UserRole
default:
return QVariant();
}
return QVariant();
}

EntryList::EntryList(Row *parent, const Avahi::ServiceKey &k)
: Avahi::model::EntryList(parent, k) { }

int EntryList::columnCount() {
return 3;
}

QVariant EntryList::data(int col, int role) {
switch (role) {
case Qt::DisplayRole:
switch (col) {
case 0: return key.name;
}
// case Qt::UserRole
default:
return QVariant();
}
return QVariant();
}

Avahi::model::Entry* EntryList::newEntry(const Avahi::Service &s, void *priv) {
QCP::Share *share = static_cast< QCP::Share* >(priv);
return new Entry(this, s, *share);
}


int Root::columnCount() {
return 3;
}

EntryList* Root::newList(const Avahi::ServiceKey &k) {
return new EntryList(this, k);
}

}


BrowseModel::BrowseModel(QObject *parent)
: Avahi::BrowseModel(new QCP::model::Root(), parent), m_browse(new QCP::Browse(this)) {
connect(m_browse, SIGNAL(found(QCP::Share)), this, SLOT(foundShare(QCP::Share)));
connect(m_browse, SIGNAL(lost(QCP::Share)), this, SLOT(lostShare(QCP::Share)));

m_browse->start();
}

QVariant BrowseModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation != Qt::Horizontal) return QVariant();
switch (role) {
case Qt::DisplayRole:
switch (section) {
case 0: return "Name";
case 1: return "Address";
case 2: return "Version";
}
break;
}
return QVariant();
}

void BrowseModel::foundShare(QCP::Share s) {
insert(s.avahiService(), &s);
}

void BrowseModel::lostShare(QCP::Share s) {
remove(s.avahiService());
}
}

+ 59
- 0
libqcp/qcpmodel.h View File

@@ -0,0 +1,59 @@
#ifndef QCPMODEL_H
#define QCPMODEL_H

#include "qcp.h"
#include "../libqtavahi/qtavahimodel.h"

namespace QCP {
namespace model {
class LIBQCPSHARED_EXPORT Entry : public Avahi::model::Entry {
public:
Entry(Row *parent, const Avahi::Service &s, const QCP::Share &share);

virtual QVariant data(int col, int role);

protected:
QCP::Share m_share;
};

class LIBQCPSHARED_EXPORT EntryList : public Avahi::model::EntryList {
public:
EntryList(Row *parent, const Avahi::ServiceKey &k);

virtual int columnCount();

virtual QVariant data(int col, int role);

protected:
virtual Avahi::model::Entry* newEntry(const Avahi::Service &s, void *priv);
};

class LIBQCPSHARED_EXPORT Root : public Avahi::model::Root {
public:
virtual int columnCount();

protected:
virtual EntryList* newList(const Avahi::ServiceKey &k);
};
}

class LIBQCPSHARED_EXPORT BrowseModel : public Avahi::BrowseModel {
Q_OBJECT

public:
BrowseModel(QObject *parent = 0);

QVariant headerData(int section, Qt::Orientation orientation, int role) const;

QCP::Browse *browse() { return m_browse; }

protected slots:
void foundShare(QCP::Share s);
void lostShare(QCP::Share s);

private:
QCP::Browse *m_browse;
};
}

#endif // QCPMODEL_H

+ 17
- 0
libqtavahi/libqtavahi.pro View File

@@ -0,0 +1,17 @@
# -------------------------------------------------
# Project created by QtCreator 2010-07-23T21:04:59
# -------------------------------------------------
QT += network
QT -= gui
TARGET = qtavahi
TEMPLATE = lib
DEFINES += LIBQTAVAHI_LIBRARY
SOURCES += qtavahi.cpp \
qtavahimodel.cpp
HEADERS += qtavahi.h \
libqtavahi_global.h \
qtavahi_p.h \
qtavahimodel.h
LIBS += -lavahi-common \
-lavahi-client \
-lavahi-qt4

+ 12
- 0
libqtavahi/libqtavahi_global.h View File

@@ -0,0 +1,12 @@
#ifndef LIBQTAVAHI_GLOBAL_H
#define LIBQTAVAHI_GLOBAL_H

#include <QtCore/qglobal.h>

#if defined(LIBQTAVAHI_LIBRARY)
# define LIBQTAVAHISHARED_EXPORT Q_DECL_EXPORT
#else
# define LIBQTAVAHISHARED_EXPORT Q_DECL_IMPORT
#endif

#endif // LIBQTAVAHI_GLOBAL_H

+ 563
- 0
libqtavahi/qtavahi.cpp View File

@@ -0,0 +1,563 @@

#include "qtavahi.h"

#include <avahi-client/client.h>
#include <avahi-client/lookup.h>
#include <avahi-client/publish.h>

#include <avahi-common/alternative.h>
#include <avahi-common/error.h>
#include <avahi-common/malloc.h>

#include <avahi-qt4/qt-watch.h>

#include <QSize>
#include <QtEndian>

#include <netinet/in.h>

namespace Avahi {

static QHostAddress fromAvahiAddress(const AvahiAddress *address, AvahiIfIndex interface) {
switch (address->proto) {
case AVAHI_PROTO_INET:
return QHostAddress(qFromBigEndian((quint32) address->data.ipv4.address));
case AVAHI_PROTO_INET6: {
struct sockaddr_in6 a;
memset(&a, 0, sizeof(a));
a.sin6_family = AF_INET6;
a.sin6_scope_id = interface;
memcpy(a.sin6_addr.s6_addr, address->data.ipv6.address, 16);
return QHostAddress((const sockaddr*) &a);
}
case AVAHI_PROTO_UNSPEC:
return QHostAddress();
}
return QHostAddress();
}

namespace priv {

static void browsep_client_callback(AvahiClient *c, AvahiClientState state, void * userdata);
static void browsep_browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event,
const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata);
static void browsep_resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event,
const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address,
uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata);
}
}

namespace Avahi {
namespace priv {

class BrowseP {
public:
static const ::AvahiPoll *m_qtpoll;

QString m_serviceType;

bool m_initialized;
bool m_running;

::AvahiClient *m_client;
::AvahiServiceBrowser *m_browser;

QByteArray m_service_type;

Avahi::Browse *m_parent;

QHash<ServiceKey, Service> m_list;

BrowseP(QString servicetype, Browse *parent)
: m_serviceType(servicetype),
m_initialized(false), m_running(false), m_client(0), m_browser(0),
m_service_type(servicetype.toUtf8()), m_parent(parent) {
int err;

if (!m_qtpoll) {
if (!(m_qtpoll = avahi_qt_poll_get())) {
qWarning("avahi_qt_poll_get failed");
return;
}
}

if (!(m_client = avahi_client_new(m_qtpoll, (AvahiClientFlags) 0, browsep_client_callback, this, &err))) {
qWarning("avahi_client_new failed: %s", avahi_strerror(err));
return;
}

m_initialized = true;
}

void clearList() {
QList<Service> l = m_list.values();
m_list.clear();
for (int i = 0; i < l.size(); ++i) {
emit m_parent->lost(l.at(i));
}
}

void release() {
stop();

if (!m_initialized) return;

if (m_client) {
avahi_client_free(m_client);
m_client = 0;
}

m_initialized = false;
}

~BrowseP() {
release();
}

void client_callback(AvahiClient *c, AvahiClientState state) {
switch (state) {
case AVAHI_CLIENT_S_RUNNING:
break;
case AVAHI_CLIENT_FAILURE:
qWarning("avahi client failure: %s", avahi_strerror(avahi_client_errno(c)));
release();
break;
case AVAHI_CLIENT_S_COLLISION:
case AVAHI_CLIENT_S_REGISTERING:
break;
case AVAHI_CLIENT_CONNECTING:
break;
}
}

void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event,
const char *name, const char *type, const char *domain, AvahiLookupResultFlags /* flags */) {
if (!m_browser) m_browser = b;
Q_ASSERT(m_browser == b);

if (protocol != AVAHI_PROTO_INET && protocol != AVAHI_PROTO_INET6) return; /* ignore non IPv[46] for now */

/* Called whenever a new services becomes available on the LAN or is removed from the LAN */

switch (event) {
case AVAHI_BROWSER_FAILURE:
qWarning("(Browser) %s", avahi_strerror(avahi_client_errno(m_client)));
release();
return;

case AVAHI_BROWSER_NEW:
qDebug("(Browser) NEW: service '%s' of type '%s' in domain '%s'", name, type, domain);

/* We ignore the returned resolver object. In the callback
* function we free it. If the server is terminated before
* the callback function is called the server will free
* the resolver for us. */

if (!(avahi_service_resolver_new(m_client, interface, protocol, name, type, domain, protocol, (AvahiLookupFlags) 0, browsep_resolve_callback, this))) {
qWarning("Failed to resolve service '%s' on '%s': %s", name, domain, avahi_strerror(avahi_client_errno(m_client)));
}

break;

case AVAHI_BROWSER_REMOVE: {
ServiceKey k(interface, protocol, name, domain);
Service s = m_list.value(k);
if (s.valid()) {
m_list.remove(k);
emit m_parent->lost(s);
}
}
break;

case AVAHI_BROWSER_ALL_FOR_NOW:
break;
case AVAHI_BROWSER_CACHE_EXHAUSTED:
break;
}
}

void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event,
const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address,
uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags /* flags */) {
Q_ASSERT(r);

// if (protocol != AVAHI_PROTO_INET) goto done; /* ignore non IPv4 for now */
if (protocol != AVAHI_PROTO_INET && protocol != AVAHI_PROTO_INET6) goto done; /* ignore non IPv[46] for now */

/* Called whenever a service has been resolved successfully or timed out */

switch (event) {
case AVAHI_RESOLVER_FAILURE:
qDebug("(Resolver) Failed to resolve service '%s' of type '%s' at '%s' in domain '%s': %s", name, type, host_name, domain, avahi_strerror(avahi_client_errno(m_client)));
break;

case AVAHI_RESOLVER_FOUND: {
qDebug("(Resolver) found service '%s' of type '%s' at '%s' in domain '%s'", name, type, host_name, domain);
QHash<QString, QString> qtxt;
AvahiStringList *psl;
for (psl = txt ; psl ; psl = psl->next) {
QString e = QString::fromUtf8((const char*) psl->text, psl->size), key, value;
int eqPos = e.indexOf(QChar('"'));
if (eqPos == 0) continue; /* ignore empty keys */
if (eqPos < -1) { /* "boolean" */
key = e;
} else {
key = e.left(eqPos - 1);
value = e.mid(eqPos + 1);
}
key = key.toLower();
qtxt.insert(key, value);
}

ServiceKey k(interface, protocol, name, domain);
Service s = m_list.value(k);
if (s.valid()) {
m_list.remove(k);
emit m_parent->lost(s);
}

s = Service(QString::fromUtf8(name), QString::fromUtf8(host_name), QString::fromUtf8(domain), fromAvahiAddress(address, interface), interface, protocol, port, qtxt);
m_list.insert(k, s);
emit m_parent->found(s);
}
}

done:
avahi_service_resolver_free(r);
}

bool start() {
if (m_running) return true;
if (!m_initialized) return false;

clearList();

if (NULL == (m_browser = avahi_service_browser_new(m_client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, m_service_type.constData(), NULL, (AvahiLookupFlags) 0, browsep_browse_callback, this))) {
qWarning("Failed to create service browser: %s", avahi_strerror(avahi_client_errno(m_client)));
return false;
}

emit m_parent->started();
m_running = true;

return true;
}

void stop() {
clearList();
if (m_running) {
emit m_parent->stopped();
m_running = false;
}
if (m_browser) {
avahi_service_browser_free(m_browser);
m_browser = 0;
}
}
};
const AvahiPoll *BrowseP::m_qtpoll = 0;

static void browsep_client_callback(AvahiClient *c, AvahiClientState state, void * userdata) {
BrowseP *p = static_cast<BrowseP*>(userdata);
p->client_callback(c, state);
}

static void browsep_browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event,
const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata) {
BrowseP *p = static_cast<BrowseP*>(userdata);
p->browse_callback(b, interface, protocol, event, name, type, domain, flags);
}

static void browsep_resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event,
const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address,
uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata) {
BrowseP *p = static_cast<BrowseP*>(userdata);
p->resolve_callback(r, interface, protocol, event, name, type, domain, host_name, address, port, txt, flags);
}

static void announcep_client_callback(AvahiClient *c, AvahiClientState state, void * userdata);
static void announcep_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata);

class AnnounceP {
public:
static const AvahiPoll *m_qtpoll;

QString m_serviceType, m_name;
quint16 m_port;
QHash<QString,QString> m_txt;

bool m_initialized;
bool m_running;

AvahiClient *m_client;
AvahiEntryGroup *m_group;

QByteArray m_service_type;

Announce *m_parent;

char *m_raw_name; /* avahi string */
QList<QByteArray> m_txt_list;
::AvahiStringList *m_raw_txt;

AnnounceP(QString serviceType, QString name, quint16 port, QHash<QString,QString> txt, Announce *parent)
: m_serviceType(serviceType), m_name(name), m_port(port), m_txt(txt),
m_initialized(false), m_running(false), m_client(0), m_group(0),
m_service_type(serviceType.toUtf8()), m_parent(parent),
m_raw_name(avahi_strdup(name.toUtf8().constData())), m_raw_txt(0) {
int err;

QHashIterator<QString, QString> i(m_txt);
while (i.hasNext()) {
QByteArray a;
i.next();
if (i.value().isNull()) {
m_txt_list.append(a = i.key().toUtf8());
} else {
m_txt_list.append(a = QString("%1=%2").arg(i.key(), i.value()).toUtf8());
}
AvahiStringList *l = avahi_string_list_new(a.constData(), NULL);
l->next = m_raw_txt;
m_raw_txt = l;
}

if (!m_qtpoll) {
if (!(m_qtpoll = avahi_qt_poll_get())) {
qWarning("avahi_qt_poll_get failed");
return;
}
}

if (!(m_client = avahi_client_new(m_qtpoll, (AvahiClientFlags) 0, announcep_client_callback, this, &err))) {
qWarning("avahi_client_new failed: %s", avahi_strerror(err));
return;
}

m_initialized = true;

start();
}

void release() {
if (m_running) {
m_running = false;
}
if (!m_initialized) return;

if (m_group) {
qDebug("avahi unregister service");
avahi_entry_group_reset(m_group);
avahi_entry_group_free(m_group);
m_group = 0;
}

if (m_client) {
avahi_client_free(m_client);
m_client = 0;
}

m_initialized = false;

emit m_parent->stopped();
}

~AnnounceP() {
release();
if (m_raw_name) {
avahi_free(m_raw_name);
m_raw_name = 0;
}
if (m_raw_txt) {
avahi_string_list_free(m_raw_txt);
m_raw_txt = 0;
}
}

void client_callback(AvahiClient *c, AvahiClientState state) {
m_client = c;
switch (state) {
case AVAHI_CLIENT_S_RUNNING:
create_services();
break;
case AVAHI_CLIENT_FAILURE:
qDebug("avahi client failure");
release();
break;
case AVAHI_CLIENT_S_COLLISION:
case AVAHI_CLIENT_S_REGISTERING:
if (m_group) {
avahi_entry_group_reset(m_group);
}
break;
case AVAHI_CLIENT_CONNECTING:
break;
}
}

void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state) {
m_group = g;

switch (state) {
case AVAHI_ENTRY_GROUP_ESTABLISHED :
emit m_parent->established();
break;

case AVAHI_ENTRY_GROUP_COLLISION :
{
char *n = avahi_alternative_service_name(m_raw_name);
avahi_free(m_raw_name);
m_raw_name = n;
m_name = QString::fromUtf8(m_raw_name);
}

/* And recreate the services */
avahi_entry_group_reset(m_group);
create_services();
/* same name should result in AVAHI_ERR_COLLISION in create_services, emit collision there. */
break;

case AVAHI_ENTRY_GROUP_FAILURE :
qDebug("Entry group failure: %s", avahi_strerror(avahi_client_errno(m_client)));

release();
break;

case AVAHI_ENTRY_GROUP_UNCOMMITED:
case AVAHI_ENTRY_GROUP_REGISTERING:
break;
}
}

bool create_services() {
int ret;

if (!m_group) {
if (!(m_group = avahi_entry_group_new(m_client, announcep_entry_group_callback, this))) {
qDebug("avahi_entry_group_new failed: %s", avahi_strerror(avahi_client_errno(m_client)));
release();
return false;
}
}

again:
if (avahi_entry_group_is_empty(m_group)) {
if ((ret = avahi_entry_group_add_service_strlst(m_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, (AvahiPublishFlags) 0, m_raw_name, m_service_type.constData(), NULL, NULL, m_port, m_raw_txt)) < 0) {

if (ret == AVAHI_ERR_COLLISION)
goto collision;

qDebug("Failed to add '%s': %s", m_service_type.constData(), avahi_strerror(ret));
release();
return false;
}
if ((ret = avahi_entry_group_commit(m_group)) < 0) {
qDebug("Failed to commit entry group: %s", avahi_strerror(ret));
release();
return false;
}
}

return true;

collision:
qDebug("avahi name collision: %s, trying a new one", m_raw_name);
{
char *n = avahi_alternative_service_name(m_raw_name);
avahi_free(m_raw_name);
m_raw_name = n;
m_name = QString::fromUtf8(m_raw_name);
}
avahi_entry_group_reset(m_group);
emit m_parent->collision();
goto again;
}

bool start() {
if (m_running) return true;
if (!m_initialized) return false;

m_running = true;
emit m_parent->started();

return true;
}

void stop() {
release();
}
};
const AvahiPoll *AnnounceP::m_qtpoll = 0;

static void announcep_client_callback(AvahiClient *c, AvahiClientState state, void * userdata) {
AnnounceP *p = static_cast<AnnounceP*>(userdata);
p->client_callback(c, state);
}
static void announcep_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
AnnounceP *p = static_cast<AnnounceP*>(userdata);
p->entry_group_callback(g, state);
}
}

/* public interfaces */

Browse::Browse(QString serviceType, QObject *parent)
: QObject(parent), m_priv(new priv::BrowseP(serviceType, this)) {
}

Browse::~Browse() {
delete m_priv;
m_priv = 0;
}

bool Browse::start() {
return m_priv->start();
}

void Browse::stop() {
m_priv->stop();
}

QString Browse::serviceType() const {
return m_priv->m_serviceType;
}

QList<Service> Browse::currentList() const {
return m_priv->m_list.values();
}


Announce::Announce(QString serviceType, QString name, quint16 port, QHash<QString,QString> txt, QObject *parent)
: QObject(parent), m_priv(new priv::AnnounceP(serviceType, name, port, txt, this)) {
}

Announce::~Announce() {
delete m_priv;
m_priv = 0;
}

bool Announce::start() {
return m_priv->start();
}

void Announce::stop() {
m_priv->stop();
}

QString Announce::serviceType() const {
return m_priv->m_serviceType;
}

QString Announce::name() const {
return m_priv->m_name;
}
quint16 Announce::port() const {
return m_priv->m_port;
}
QHash<QString, QString> Announce::txt() const {
return m_priv->m_txt;
}
QString Announce::txt(QString key) const {
return m_priv->m_txt.value(key);
}

}

+ 186
- 0
libqtavahi/qtavahi.h View File

@@ -0,0 +1,186 @@
#ifndef QTAVAHI_H
#define QTAVAHI_H

#include <QObject>

#include "libqtavahi_global.h"

namespace Avahi {
class LIBQTAVAHISHARED_EXPORT Service;
class LIBQTAVAHISHARED_EXPORT ServiceKey;
class LIBQTAVAHISHARED_EXPORT Browse;
class LIBQTAVAHISHARED_EXPORT Announce;
}

/* we need this before #include <QHash> so it finds our qHash function */
inline uint qHash(const Avahi::ServiceKey& k);

#include <QHostAddress>
#include <QAbstractItemModel>
#include <QAtomicInt>
#include <QString>
#include <QHash>

#include "qtavahi_p.h"

namespace Avahi {

class LIBQTAVAHISHARED_EXPORT Service {
public:
inline Service() : m_priv(0) { }
inline Service(QString name, QString hostname, QString domain, QHostAddress address, int interface, int protocol, quint16 port, QHash<QString, QString> txt)
: m_priv(new Avahi::priv::ServiceP(name, hostname, domain, address, interface, protocol, port, txt)) { }

inline Service(const Service &s) : m_priv(0) { assign(s.m_priv); }
inline Service &operator=(const Service &s) { assign(s.m_priv); return *this; }

~Service() { assign(0); }

inline QString name() const { return m_priv ? m_priv->m_name : QString(); }
inline QString hostname() const { return m_priv ? m_priv->m_hostname : QString(); }
inline QString domain() const { return m_priv ? m_priv->m_domain : QString(); }
inline QHostAddress address() const { return m_priv ? m_priv->m_address : QHostAddress(); }
inline quint16 port() const { return m_priv ? m_priv->m_port : 0; }
inline QHash<QString, QString> txt() const { return m_priv ? m_priv->m_txt : QHash<QString, QString>(); }
inline QString txt(QString key) const { return m_priv ? m_priv->m_txt.value(key) : QString(); }
inline bool valid() const { return 0 != m_priv; }

/* save custom data for a service (the service is probably associated with a Browse instance, so all
code using the same Browse instance uses the same private data here */
inline void* data() const { return m_priv ? m_priv->m_data : 0; }
inline void setData(void *data) const { if (m_priv) m_priv->m_data = data; }

private:
friend class Avahi::ServiceKey;

inline void assign(Avahi::priv::ServiceP *p) {
if (p == m_priv) return;
if (p) p->ref();
if (m_priv) m_priv->deref();
m_priv = p;
}

Avahi::priv::ServiceP *m_priv;
};

class LIBQTAVAHISHARED_EXPORT ServiceKey {
public:
inline ServiceKey(int interface, int protocol, const char *name, const char *domain)
: interface(interface), protocol(protocol), name(QString::fromUtf8(name)), domain(QString::fromUtf8(domain)) {
}
inline ServiceKey(int interface, int protocol, QString name, QString domain)
: interface(interface), protocol(protocol), name(name), domain(domain) {
}
inline ServiceKey(const Service &s)
: interface(-1), protocol(-1), name(), domain() {
if (s.m_priv) {
interface = s.m_priv->m_interface;
protocol = s.m_priv->m_protocol;
name = s.m_priv->m_name;
domain = s.m_priv->m_domain;
}
}

int interface; /* AvahiIfIndex */
int protocol; /* AvahiProtocol */
QString name;
QString domain;
};

inline bool operator==(const ServiceKey &a, const ServiceKey &b) {
return a.interface == b.interface && a.protocol == b.protocol && a.name == b.name && a.domain == b.domain;
}
inline bool operator<(const ServiceKey &a, const ServiceKey &b) {
if (a.interface < b.interface) return true;
if (a.interface > b.interface) return false;
if (a.protocol < b.protocol) return true;
if (a.protocol > b.protocol) return false;
if (a.name < b.name) return true;
if (a.name > b.name) return false;
return a.domain < b.domain;
}
inline bool operator>(const ServiceKey &a, const ServiceKey &b) { return b < a; }
inline bool operator<=(const ServiceKey &a, const ServiceKey &b) { return !(b < a); }
inline bool operator>=(const ServiceKey &a, const ServiceKey &b) { return !(a < b); }



class LIBQTAVAHISHARED_EXPORT Browse : public QObject {
Q_OBJECT
private:
Q_DISABLE_COPY(Browse);

public:
typedef Avahi::priv::Protocol Protocol;
inline Browse(QString servtype, Protocol prot, QObject *parent = 0) {
Browse(Avahi::priv::serv_prot2str(servtype, prot), parent);
}
Browse(QString serviceType, QObject *parent = 0);
virtual ~Browse();

bool start();
void stop();

QString serviceType() const;

QList<Service> currentList() const;

private:
void clearList();

signals:
void found(Avahi::Service i);
void lost(Avahi::Service i);

void started();
void stopped();

private:
Avahi::priv::BrowseP *m_priv;

friend class Avahi::priv::BrowseP;
};

class LIBQTAVAHISHARED_EXPORT Announce : public QObject {
Q_OBJECT
private:
Q_DISABLE_COPY(Announce);

public:
typedef Avahi::priv::Protocol Protocol;
inline Announce(QString servtype, Protocol prot, QString name, quint16 port, QHash<QString,QString> txt, QObject *parent = 0) {
Announce(Avahi::priv::serv_prot2str(servtype, prot), name, port, txt, parent);
}
Announce(QString serviceType, QString name, quint16 port, QHash<QString,QString> txt, QObject *parent = 0);
virtual ~Announce();

QString serviceType() const;
QString name() const;
quint16 port() const;
QHash<QString, QString> txt() const;
QString txt(QString key) const;

bool start();
void stop();

signals:
void established(); /* the name may have been changed after collisions */
void collision();
void started();
void stopped();

private:
Avahi::priv::AnnounceP *m_priv;

friend class Avahi::priv::AnnounceP;
};

}

QT_BEGIN_NAMESPACE
inline uint qHash(const Avahi::ServiceKey& k) {
return qHash(k.interface ^ k.protocol) ^ qHash(k.name) ^ qHash(k.domain);
}
QT_END_NAMESPACE

#endif // QTAVAHI_H

+ 50
- 0
libqtavahi/qtavahi_p.h View File

@@ -0,0 +1,50 @@
#ifndef QTAVAHI_P_H
#define QTAVAHI_P_H

namespace Avahi {

namespace priv {
class LIBQTAVAHISHARED_EXPORT ServiceP;
class BrowseP;
class AnnounceP;
};
}

namespace Avahi {
namespace priv {
class LIBQTAVAHISHARED_EXPORT ServiceP {
private:
friend class Avahi::Service;
friend class Avahi::ServiceKey;
friend class Avahi::Browse;
inline ServiceP(QString name, QString hostname, QString domain, QHostAddress address, int interface, int protocol, quint16 port, QHash<QString, QString> txt)
: m_ref(1), m_name(name), m_hostname(hostname), m_domain(domain), m_address(address), m_interface(interface), m_protocol(protocol), m_port(port), m_txt(txt), m_data(0) { }

Q_DISABLE_COPY(ServiceP);

inline void ref() { m_ref.ref(); }
inline void deref() { if (!m_ref.deref()) { delete this; } }

QAtomicInt m_ref;

QString m_name, m_hostname, m_domain;
QHostAddress m_address;
int m_interface, m_protocol;
quint16 m_port;
QHash<QString, QString> m_txt;

void* m_data;
};

typedef enum { TCP, UDP } Protocol;
inline LIBQTAVAHISHARED_EXPORT QString serv_prot2str(QString service, Protocol prot) {
switch (prot) {
case TCP: return QString("_%1._tcp").arg(service); break;
case UDP: return QString("_%1._udp").arg(service); break;
default: return service;
}
}
}
}

#endif // QTAVAHI_P_H

+ 91
- 0
libqtavahi/qtavahimodel.cpp View File

@@ -0,0 +1,91 @@
#include "qtavahimodel.h"

namespace Avahi {
namespace model {
Row::~Row() { }

int Row::columnCount() { return 0; } /* columns of child rows */
Row* Row::index(int /* row */) { return 0; }
int Row::rowCount() { return 0; }

Row* RowList::index(int row) {
return list.value(row);
}
int RowList::rowCount() {
return list.length();
}

QVariant Root::data(int , int ) { return QVariant(); }
}


BrowseModel::BrowseModel(Avahi::model::Root *root, QObject *parent)
: QAbstractItemModel(parent), m_root(root) { }

BrowseModel::Row *BrowseModel::getRow(const QModelIndex &index) const {
if (index.isValid()) {
return static_cast< Row* >(index.internalPointer());
} else {
return m_root;
}
}

int BrowseModel::columnCount(const QModelIndex & parent) const {
Row *row = getRow(parent);
return row ? row->columnCount() : 0;
}
QVariant BrowseModel::data(const QModelIndex & index, int role) const {
Row *row = getRow(index);
return row ? row->data(index.column(), role) : QVariant();
}
QModelIndex BrowseModel::index(int row, int column, const QModelIndex & parent) const {
Row *r = getRow(parent);
if (!r) return QModelIndex(); /* invalid */
Row *child = r->index(row);
if (!child) return QModelIndex(); /* invalid */
return createIndex(row, column, (void*) child);
}
QModelIndex BrowseModel::parent(const QModelIndex & index) const {
Row *row = getRow(index);
if (!row) return QModelIndex(); /* invalid */
Row *p = row->parent();
if (!p || p == m_root) return QModelIndex();
return createIndex(0, 0, (void*) p);
}
int BrowseModel::rowCount(const QModelIndex & parent) const {
Row *row = getRow(parent);
return row ? row->rowCount() : 0;
}

Avahi::ServiceKey BrowseModel::key(const Avahi::Service &s) {
Avahi::ServiceKey k(s);
k.interface = k.protocol = -1;
return k;
}

void BrowseModel::insert(const Avahi::Service &s, void *priv) {
Avahi::ServiceKey k = key(s);
if (m_services.contains(k)) {
EntryList *el = m_services.value(k);
beginInsertRows(createIndex(0, 0, (void*) el), 0, 0);
el->list.push_front(el->newEntry(s, priv));
endInsertRows();
} else {
EntryList *el = m_root->newList(k);
QMap<Avahi::ServiceKey, EntryList*> tmp(m_services);
tmp.insert(k, el);
int row = tmp.keys().indexOf(k);
beginInsertRows(QModelIndex(), row, row);
m_services = tmp;
m_root->list.insert(row, el);
endInsertRows();
beginInsertRows(createIndex(0, 0, (void*) el), 0, 0);
el->list.push_front(el->newEntry(s, priv));
endInsertRows();
}
}

void BrowseModel::remove(const Avahi::Service &) {
}

}

+ 115
- 0
libqtavahi/qtavahimodel.h View File

@@ -0,0 +1,115 @@
#ifndef QTAVAHIMODEL_H
#define QTAVAHIMODEL_H

#include "qtavahi.h"

#include <QAbstractItemModel>

namespace Avahi {
class BrowseModel;

namespace model {
class Row {
public:
Row(Row *parent) : m_parent(parent) { }
Row() : m_parent(0) { }
virtual ~Row();

virtual int columnCount(); /* columns of child rows */
virtual QVariant data(int col, int role) = 0;
virtual Row* index(int row);
virtual int rowCount();

Row *parent() { return m_parent; }

protected:
Row *m_parent;
};

class RowList : public Row {
public:
typedef QList< Row* > List;

RowList(Row *parent) : Row(parent) { }
RowList() : Row() { }

virtual int columnCount() = 0; /* columns of child rows */
virtual Row* index(int row);
virtual int rowCount();

protected:
friend class Avahi::BrowseModel;

List list;
};

class Entry : public Row {
public:
Entry(Row *parent, const Avahi::Service &s) : Row(parent), service(s) { }

protected:
friend class Avahi::BrowseModel;

Avahi::Service service;
};

class EntryList : public RowList {
public:
EntryList(Row *parent, const Avahi::ServiceKey &k) : RowList(parent), key(k) { }

protected:
friend class Avahi::BrowseModel;

virtual Entry* newEntry(const Avahi::Service &s, void *priv) = 0;

Avahi::ServiceKey key;
};

class Root : public RowList {
public:
virtual QVariant data(int , int );

protected:
friend class Avahi::BrowseModel;

virtual EntryList* newList(const Avahi::ServiceKey &k) = 0;
};
}


class BrowseModel : public QAbstractItemModel {
Q_OBJECT

protected:
typedef Avahi::model::Row Row;
typedef Avahi::model::RowList RowList;
typedef Avahi::model::Entry Entry;
typedef Avahi::model::EntryList EntryList;

Avahi::model::Root *m_root;

BrowseModel(Avahi::model::Root *root, QObject *parent = 0);

Row *getRow(const QModelIndex &index) const;

public:
int columnCount(const QModelIndex & parent) const;
QVariant data(const QModelIndex & index, int role) const;
QModelIndex index(int row, int column, const QModelIndex & parent) const;
QModelIndex parent(const QModelIndex & index) const;
int rowCount(const QModelIndex & parent) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const = 0;

protected:
virtual Avahi::ServiceKey key(const Avahi::Service &s);

void insert(const Avahi::Service &s, void *priv);
void remove(const Avahi::Service &s);

private:
Avahi::Browse *m_browse;
QMap<Avahi::ServiceKey, EntryList*> m_services;
};
}

#endif // QCPMODEL_H

+ 2
- 0
qcp.pro View File

@@ -0,0 +1,2 @@
TEMPLATE = subdirs
SUBDIRS = libqtavahi libqcp qcp

+ 1
- 0
qcp/libqcp.so.1 View File

@@ -0,0 +1 @@
../libqcp/libqcp.so.1

+ 1
- 0
qcp/libqtavahi.so.1 View File

@@ -0,0 +1 @@
../libqtavahi/libqtavahi.so.1

+ 9
- 0
qcp/main.cpp View File

@@ -0,0 +1,9 @@
#include <QtGui/QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

+ 26
- 0
qcp/mainwindow.cpp View File

@@ -0,0 +1,26 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
ui->setupUi(this);

m_browsemodel = new QCP::BrowseModel(this);
ui->viewShares->setModel(m_browsemodel);
}

MainWindow::~MainWindow() {
delete ui;
}

void MainWindow::changeEvent(QEvent *e) {
QMainWindow::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}

+ 27
- 0
qcp/mainwindow.h View File

@@ -0,0 +1,27 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include "../libqcp/qcp.h"
#include "../libqcp/qcpmodel.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();

protected:
void changeEvent(QEvent *e);

private:
Ui::MainWindow *ui;
QCP::BrowseModel *m_browsemodel;
};

#endif // MAINWINDOW_H

+ 55
- 0
qcp/mainwindow.ui View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Shares</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QTreeView" name="viewShares"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

+ 21
- 0
qcp/qcp.pro View File

@@ -0,0 +1,21 @@
#-------------------------------------------------
#
# Project created by QtCreator 2010-07-24T12:24:31
#
#-------------------------------------------------

QT += network

TARGET = qcp
TEMPLATE = app


SOURCES += main.cpp\
mainwindow.cpp

HEADERS += mainwindow.h

FORMS += mainwindow.ui

TARGETDEPS += ../libqcp/libqcp.so ../libqtavahi/libqtavahi.so
LIBS += ../libqcp/libqcp.so ../libqtavahi/libqtavahi.so

Loading…
Cancel
Save