diff options
Diffstat (limited to 'Swiften/LinkLocal')
55 files changed, 2963 insertions, 0 deletions
diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h new file mode 100644 index 0000000..229f97e --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h @@ -0,0 +1,64 @@ +#pragma once + +#include <boost/bind.hpp> + +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class AvahiQuerier; + + class AvahiBrowseQuery : public DNSSDBrowseQuery, public AvahiQuery { + public: + AvahiBrowseQuery(boost::shared_ptr<AvahiQuerier> q) : AvahiQuery(q) { + } + + void startBrowsing() { + std::cout << "Start browsing" << std::endl; + avahi_threaded_poll_lock(querier->getThreadedPoll()); + std::cout << "Creating browser" << std::endl; + AvahiServiceBrowser* browser = avahi_service_browser_new(querier->getClient(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_presence._tcp", NULL, (AvahiLookupFlags) 0, &handleServiceDiscoveredStatic, this); + if (!browser) { + std::cout << "Error" << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this()); + } + std::cout << "Unlocking" << std::endl; + avahi_threaded_poll_unlock(querier->getThreadedPoll()); + std::cout << "Browse started" << std::endl; + } + + void stopBrowsing() { + // TODO + } + + private: + static void handleServiceDiscoveredStatic(AvahiServiceBrowser *b, AvahiIfIndex interfaceIndex, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* context) { + static_cast<AvahiBrowseQuery*>(context)->handleServiceDiscovered(b, interfaceIndex, protocol, event, name, type, domain, flags); + } + + void handleServiceDiscovered(AvahiServiceBrowser *, AvahiIfIndex interfaceIndex, AvahiProtocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags) { + switch (event) { + case AVAHI_BROWSER_FAILURE: + std::cout << "Service browse error" << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this()); + break; + case AVAHI_BROWSER_NEW: { + DNSSDServiceID service(name, domain, type, interfaceIndex); + std::cout << "Service discovered " << name << " " << type << " " << domain << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this()); + break; + } + case AVAHI_BROWSER_REMOVE: { + std::cout << "Service went away " << name << " " << type << " " << domain << std::endl; + DNSSDServiceID service(name, domain, type, interfaceIndex); + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceRemoved), service), shared_from_this()); + break; + } + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + break; + } + } + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp new file mode 100644 index 0000000..c4bfcb4 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp @@ -0,0 +1,60 @@ +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h" + +#include <iostream> + +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h" +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h" +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h" +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h" + +namespace Swift { + +AvahiQuerier::AvahiQuerier() : client(NULL), threadedPoll(NULL) { +} + +AvahiQuerier::~AvahiQuerier() { +} + +boost::shared_ptr<DNSSDBrowseQuery> AvahiQuerier::createBrowseQuery() { + return boost::shared_ptr<DNSSDBrowseQuery>(new AvahiBrowseQuery(shared_from_this())); +} + +boost::shared_ptr<DNSSDRegisterQuery> AvahiQuerier::createRegisterQuery(const String& name, int port, const ByteArray& info) { + return boost::shared_ptr<DNSSDRegisterQuery>(new AvahiRegisterQuery(name, port, info, shared_from_this())); +} + +boost::shared_ptr<DNSSDResolveServiceQuery> AvahiQuerier::createResolveServiceQuery(const DNSSDServiceID& service) { + return boost::shared_ptr<DNSSDResolveServiceQuery>(new AvahiResolveServiceQuery(service, shared_from_this())); +} + +boost::shared_ptr<DNSSDResolveHostnameQuery> AvahiQuerier::createResolveHostnameQuery(const String& hostname, int interfaceIndex) { + return boost::shared_ptr<DNSSDResolveHostnameQuery>(new AvahiResolveHostnameQuery(hostname, interfaceIndex, shared_from_this())); +} + +void AvahiQuerier::start() { + std::cout << "Starrting querier" << std::endl; + assert(!threadedPoll); + threadedPoll = avahi_threaded_poll_new(); + int error; + assert(!client); + client = avahi_client_new( + avahi_threaded_poll_get(threadedPoll), + static_cast<AvahiClientFlags>(0), NULL, this, &error); // TODO + if (!client) { + // TODO + std::cerr << "Avahi Error: " << avahi_strerror(error) << std::endl; + return; + } + std::cout << "Starrting event loop" << std::endl; + avahi_threaded_poll_start(threadedPoll); +} + +void AvahiQuerier::stop() { + assert(threadedPoll); + avahi_threaded_poll_stop(threadedPoll); + assert(client); + avahi_client_free(client); + avahi_threaded_poll_free(threadedPoll); +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h new file mode 100644 index 0000000..ffb8441 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h @@ -0,0 +1,47 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <avahi-client/client.h> +#include <avahi-client/lookup.h> +#include <avahi-common/thread-watch.h> +#include <avahi-common/watch.h> +#include <avahi-common/malloc.h> +#include <avahi-common/error.h> + +#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h" + +namespace Swift { + class ByteArray; + + class AvahiQuerier : + public DNSSDQuerier, + public boost::enable_shared_from_this<AvahiQuerier> { + public: + AvahiQuerier(); + ~AvahiQuerier(); + + boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery(); + boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery( + const String& name, int port, const ByteArray& info); + boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery( + const DNSSDServiceID&); + boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery( + const String& hostname, int interfaceIndex); + + void start(); + void stop(); + + AvahiThreadedPoll* getThreadedPoll() const { + return threadedPoll; + } + + AvahiClient* getClient() const { + return client; + } + + private: + AvahiClient* client; + AvahiThreadedPoll* threadedPoll; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp new file mode 100644 index 0000000..6b7c7f9 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp @@ -0,0 +1,13 @@ +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h" +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h" + +namespace Swift { + +AvahiQuery::AvahiQuery(boost::shared_ptr<AvahiQuerier> q) : querier(q) { +} + +AvahiQuery::~AvahiQuery() { +} + +} + diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h new file mode 100644 index 0000000..847f3f7 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h @@ -0,0 +1,22 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> + +#include "Swiften/EventLoop/EventOwner.h" + +namespace Swift { + class AvahiQuerier; + + class AvahiQuery : + public EventOwner, + public boost::enable_shared_from_this<AvahiQuery> { + public: + AvahiQuery(boost::shared_ptr<AvahiQuerier>); + virtual ~AvahiQuery(); + + protected: + boost::shared_ptr<AvahiQuerier> querier; + }; +} + diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h new file mode 100644 index 0000000..f42950e --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h @@ -0,0 +1,123 @@ +#pragma once + +#include <avahi-client/publish.h> + +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class AvahiQuerier; + + class AvahiRegisterQuery : public DNSSDRegisterQuery, public AvahiQuery { + public: + AvahiRegisterQuery(const String& name, int port, const ByteArray& txtRecord, boost::shared_ptr<AvahiQuerier> querier) : AvahiQuery(querier), name(name), port(port), txtRecord(txtRecord), group(0) { + } + + void registerService() { + std::cout << "Registering service " << name << ":" << port << std::endl; + avahi_threaded_poll_lock(querier->getThreadedPoll()); + if (!group) { + std::cout << "Creating entry group" << std::endl; + group = avahi_entry_group_new(querier->getClient(), handleEntryGroupChange, this); + if (!group) { + std::cout << "Error ceating entry group" << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } + } + + doRegisterService(); + avahi_threaded_poll_unlock(querier->getThreadedPoll()); + } + + void unregisterService() { + } + + void updateServiceInfo(const ByteArray& txtRecord) { + this->txtRecord = txtRecord; + avahi_threaded_poll_lock(querier->getThreadedPoll()); + assert(group); + avahi_entry_group_reset(group); + doRegisterService(); + avahi_threaded_poll_unlock(querier->getThreadedPoll()); + } + + private: + void doRegisterService() { + AvahiStringList* txtList; + avahi_string_list_parse(txtRecord.getData(), txtRecord.getSize(), &txtList); + + int result = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, (AvahiPublishFlags) 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port, txtList); + if (result < 0) { + std::cout << "Error registering service: " << avahi_strerror(result) << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } + result = avahi_entry_group_commit(group); + if (result < 0) { + std::cout << "Error registering service: " << avahi_strerror(result) << std::endl; + } + } + + static void handleEntryGroupChange(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { + static_cast<AvahiRegisterQuery*>(userdata)->handleEntryGroupChange(g, state); + } + + void handleEntryGroupChange(AvahiEntryGroup* g, AvahiEntryGroupState state) { + std::cout << "ENtry group callback: " << state << std::endl; + switch (state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED : + // Domain is a hack! + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>(DNSSDServiceID(name, "local", "_presence._tcp", 0))), shared_from_this()); + std::cout << "Entry group established" << std::endl; + break; + case AVAHI_ENTRY_GROUP_COLLISION : { + std::cout << "Entry group collision" << std::endl; + /*char *n; + n = avahi_alternative_service_name(name); + avahi_free(name); + name = n;*/ + break; + } + + case AVAHI_ENTRY_GROUP_FAILURE : + std::cout << "Entry group failure " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))) << std::endl; + break; + + case AVAHI_ENTRY_GROUP_UNCOMMITED: + case AVAHI_ENTRY_GROUP_REGISTERING: + ; + + /* + DNSServiceErrorType result = DNSServiceRegister( + &sdRef, 0, 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port, + txtRecord.getSize(), txtRecord.getData(), + &AvahiRegisterQuery::handleServiceRegisteredStatic, this); + if (result != kDNSServiceErr_NoError) { + sdRef = NULL; + }*/ + //MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } + } + +/* + static void handleServiceRegisteredStatic(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) { + static_cast<AvahiRegisterQuery*>(context)->handleServiceRegistered(errorCode, name, regtype, domain); + } + + void handleServiceRegistered(DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain) { + if (errorCode != kDNSServiceErr_NoError) { + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } + else { + } + } + */ + + private: + String name; + int port; + ByteArray txtRecord; + AvahiEntryGroup* group; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h new file mode 100644 index 0000000..ee0e837 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Swiften/Base/String.h" +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h" +#include "Swiften/EventLoop/MainEventLoop.h" +#include "Swiften/Network/HostAddress.h" + +#include <netinet/in.h> + +namespace Swift { + class AvahiQuerier; + + class AvahiResolveHostnameQuery : public DNSSDResolveHostnameQuery, public AvahiQuery { + public: + AvahiResolveHostnameQuery(const String& hostname, int, boost::shared_ptr<AvahiQuerier> querier) : AvahiQuery(querier), hostname(hostname) { + std::cout << "Resolving hostname " << hostname << std::endl; + } + + void run() { + MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional<HostAddress>(HostAddress(hostname))), shared_from_this()); + } + + void finish() { + } + + private: + HostAddress hostAddress; + String hostname; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h new file mode 100644 index 0000000..8577837 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h @@ -0,0 +1,73 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" +#include "Swiften/LinkLocal/LinkLocalServiceInfo.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class AvahiQuerier; + + class AvahiResolveServiceQuery : public DNSSDResolveServiceQuery, public AvahiQuery { + public: + AvahiResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr<AvahiQuerier> querier) : AvahiQuery(querier), service(service), resolver(NULL) { + } + + void start() { + std::cout << "Resolving " << service.getName() << std::endl; + avahi_threaded_poll_lock(querier->getThreadedPoll()); + assert(!resolver); + resolver = avahi_service_resolver_new(querier->getClient(), service.getNetworkInterfaceID(), AVAHI_PROTO_UNSPEC, service.getName().getUTF8Data(), service.getType().getUTF8Data(), service.getDomain().getUTF8Data(), AVAHI_PROTO_UNSPEC, (AvahiLookupFlags) 0, handleServiceResolvedStatic, this); + if (!resolver) { + std::cout << "Error starting resolver" << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this()); + } + avahi_threaded_poll_unlock(querier->getThreadedPoll()); + } + + void stop() { + avahi_threaded_poll_lock(querier->getThreadedPoll()); + avahi_service_resolver_free(resolver); + resolver = NULL; + avahi_threaded_poll_unlock(querier->getThreadedPoll()); + } + + private: + static void handleServiceResolvedStatic(AvahiServiceResolver* resolver, AvahiIfIndex interfaceIndex, 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* context) { + static_cast<AvahiResolveServiceQuery*>(context)->handleServiceResolved(resolver, interfaceIndex, protocol, event, name, type, domain, host_name, address, port, txt, flags); + } + + void handleServiceResolved(AvahiServiceResolver* resolver, AvahiIfIndex, AvahiProtocol, AvahiResolverEvent event, const char *name, const char * type, const char* domain, const char * /*host_name*/, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags) { + std::cout << "Resolve finished" << std::endl; + switch(event) { + case AVAHI_RESOLVER_FAILURE: + std::cout << "Resolve error " << avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(resolver))) << std::endl; + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this()); + break; + case AVAHI_RESOLVER_FOUND: { + char a[AVAHI_ADDRESS_STR_MAX]; + avahi_address_snprint(a, sizeof(a), address); + + ByteArray txtRecord; + txtRecord.resize(1024); + avahi_string_list_serialize(txt, txtRecord.getData(), txtRecord.getSize()); + + // FIXME: Probably not accurate + String fullname = String(name) + "." + String(type) + "." + String(domain) + "."; + std::cout << "Result: " << fullname << "->" << String(a) << ":" << port << std::endl; + MainEventLoop::postEvent( + boost::bind( + boost::ref(onServiceResolved), + Result(fullname, String(a), port, txtRecord)), + shared_from_this()); + break; + } + } + } + + private: + DNSSDServiceID service; + AvahiServiceResolver* resolver; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h new file mode 100644 index 0000000..2dec2fb --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class BonjourQuerier; + + class BonjourBrowseQuery : public DNSSDBrowseQuery, public BonjourQuery { + public: + BonjourBrowseQuery(boost::shared_ptr<BonjourQuerier> q) : BonjourQuery(q) { + DNSServiceErrorType result = DNSServiceBrowse( + &sdRef, 0, 0, "_presence._tcp", 0, + &BonjourBrowseQuery::handleServiceDiscoveredStatic, this); + if (result != kDNSServiceErr_NoError) { + sdRef = NULL; + } + } + + void startBrowsing() { + if (!sdRef) { + MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this()); + } + else { + run(); + } + } + + void stopBrowsing() { + finish(); + } + + private: + static void handleServiceDiscoveredStatic(DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *name, const char *type, const char *domain, void *context) { + static_cast<BonjourBrowseQuery*>(context)->handleServiceDiscovered(flags, interfaceIndex, errorCode, name, type, domain); + } + + void handleServiceDiscovered(DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *name, const char *type, const char *domain) { + if (errorCode != kDNSServiceErr_NoError) { + MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this()); + } + else { + //std::cout << "Discovered service: name:" << name << " domain:" << domain << " type: " << type << std::endl; + DNSSDServiceID service(name, domain, type, interfaceIndex); + if (flags & kDNSServiceFlagsAdd) { + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this()); + } + else { + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceRemoved), service), shared_from_this()); + } + } + } + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp new file mode 100644 index 0000000..9c9e64e --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp @@ -0,0 +1,129 @@ +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h" + +#include <unistd.h> +#include <sys/socket.h> +#include <fcntl.h> + +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h" +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h" +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h" +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h" +#include "Swiften/Base/foreach.h" + +namespace Swift { + +BonjourQuerier::BonjourQuerier() : stopRequested(false), thread(0) { + int fds[2]; + int result = pipe(fds); + assert(result == 0); + interruptSelectReadSocket = fds[0]; + fcntl(interruptSelectReadSocket, F_SETFL, fcntl(interruptSelectReadSocket, F_GETFL)|O_NONBLOCK); + interruptSelectWriteSocket = fds[1]; +} + +BonjourQuerier::~BonjourQuerier() { + assert(!thread); +} + +boost::shared_ptr<DNSSDBrowseQuery> BonjourQuerier::createBrowseQuery() { + return boost::shared_ptr<DNSSDBrowseQuery>(new BonjourBrowseQuery(shared_from_this())); +} + +boost::shared_ptr<DNSSDRegisterQuery> BonjourQuerier::createRegisterQuery(const String& name, int port, const ByteArray& info) { + return boost::shared_ptr<DNSSDRegisterQuery>(new BonjourRegisterQuery(name, port, info, shared_from_this())); +} + +boost::shared_ptr<DNSSDResolveServiceQuery> BonjourQuerier::createResolveServiceQuery(const DNSSDServiceID& service) { + return boost::shared_ptr<DNSSDResolveServiceQuery>(new BonjourResolveServiceQuery(service, shared_from_this())); +} + +boost::shared_ptr<DNSSDResolveHostnameQuery> BonjourQuerier::createResolveHostnameQuery(const String& hostname, int interfaceIndex) { + return boost::shared_ptr<DNSSDResolveHostnameQuery>(new BonjourResolveHostnameQuery(hostname, interfaceIndex, shared_from_this())); +} + +void BonjourQuerier::addRunningQuery(boost::shared_ptr<BonjourQuery> query) { + { + boost::lock_guard<boost::mutex> lock(runningQueriesMutex); + runningQueries.push_back(query); + } + runningQueriesAvailableEvent.notify_one(); + interruptSelect(); +} + +void BonjourQuerier::removeRunningQuery(boost::shared_ptr<BonjourQuery> query) { + { + boost::lock_guard<boost::mutex> lock(runningQueriesMutex); + runningQueries.erase(std::remove( + runningQueries.begin(), runningQueries.end(), query), runningQueries.end()); + } +} + +void BonjourQuerier::interruptSelect() { + char c = 0; + write(interruptSelectWriteSocket, &c, 1); +} + +void BonjourQuerier::start() { + assert(!thread); + thread = new boost::thread(boost::bind(&BonjourQuerier::run, shared_from_this())); +} + +void BonjourQuerier::stop() { + if (thread) { + stopRequested = true; + assert(runningQueries.empty()); + runningQueriesAvailableEvent.notify_one(); + interruptSelect(); + thread->join(); + delete thread; + thread = NULL; + stopRequested = false; + } +} + +void BonjourQuerier::run() { + while (!stopRequested) { + fd_set fdSet; + int maxSocket; + { + boost::unique_lock<boost::mutex> lock(runningQueriesMutex); + if (runningQueries.empty()) { + runningQueriesAvailableEvent.wait(lock); + if (runningQueries.empty()) { + continue; + } + } + + // Run all running queries + FD_ZERO(&fdSet); + maxSocket = interruptSelectReadSocket; + FD_SET(interruptSelectReadSocket, &fdSet); + + foreach(const boost::shared_ptr<BonjourQuery>& query, runningQueries) { + int socketID = query->getSocketID(); + maxSocket = std::max(maxSocket, socketID); + FD_SET(socketID, &fdSet); + } + } + + if (select(maxSocket+1, &fdSet, NULL, NULL, 0) <= 0) { + continue; + } + + if (FD_ISSET(interruptSelectReadSocket, &fdSet)) { + char dummy; + while (read(interruptSelectReadSocket, &dummy, 1) > 0) {} + } + + { + boost::lock_guard<boost::mutex> lock(runningQueriesMutex); + foreach(boost::shared_ptr<BonjourQuery> query, runningQueries) { + if (FD_ISSET(query->getSocketID(), &fdSet)) { + query->processResult(); + } + } + } + } +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h new file mode 100644 index 0000000..d12f94f --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h @@ -0,0 +1,50 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <list> +#include <boost/thread.hpp> +#include <boost/thread/mutex.hpp> + +#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h" +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h" + +namespace Swift { + class ByteArray; + + class BonjourQuerier : + public DNSSDQuerier, + public boost::enable_shared_from_this<BonjourQuerier> { + public: + BonjourQuerier(); + ~BonjourQuerier(); + + boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery(); + boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery( + const String& name, int port, const ByteArray& info); + boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery( + const DNSSDServiceID&); + boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery( + const String& hostname, int interfaceIndex); + + void start(); + void stop(); + + private: + friend class BonjourQuery; + + void addRunningQuery(boost::shared_ptr<BonjourQuery>); + void removeRunningQuery(boost::shared_ptr<BonjourQuery>); + void interruptSelect(); + void run(); + + private: + bool stopRequested; + boost::thread* thread; + boost::mutex runningQueriesMutex; + std::list< boost::shared_ptr<BonjourQuery> > runningQueries; + int interruptSelectReadSocket; + int interruptSelectWriteSocket; + boost::condition_variable runningQueriesAvailableEvent; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.cpp b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.cpp new file mode 100644 index 0000000..c1c481b --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.cpp @@ -0,0 +1,31 @@ +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h" +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h" + +namespace Swift { + +BonjourQuery::BonjourQuery(boost::shared_ptr<BonjourQuerier> q) : querier(q), sdRef(0) { +} + +BonjourQuery::~BonjourQuery() { + DNSServiceRefDeallocate(sdRef); +} + +void BonjourQuery::processResult() { + boost::lock_guard<boost::mutex> lock(sdRefMutex); + DNSServiceProcessResult(sdRef); +} + +int BonjourQuery::getSocketID() const { + boost::lock_guard<boost::mutex> lock(sdRefMutex); + return DNSServiceRefSockFD(sdRef); +} + +void BonjourQuery::run() { + querier->addRunningQuery(shared_from_this()); +} + +void BonjourQuery::finish() { + querier->removeRunningQuery(shared_from_this()); +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h new file mode 100644 index 0000000..bdb91a4 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h @@ -0,0 +1,32 @@ +#pragma once + +#include <dns_sd.h> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/thread/mutex.hpp> + +#include "Swiften/EventLoop/EventOwner.h" + +namespace Swift { + class BonjourQuerier; + + class BonjourQuery : + public EventOwner, + public boost::enable_shared_from_this<BonjourQuery> { + public: + BonjourQuery(boost::shared_ptr<BonjourQuerier>); + virtual ~BonjourQuery(); + + void processResult(); + int getSocketID() const; + + protected: + void run(); + void finish(); + + protected: + boost::shared_ptr<BonjourQuerier> querier; + mutable boost::mutex sdRefMutex; + DNSServiceRef sdRef; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h new file mode 100644 index 0000000..ddc2788 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class BonjourQuerier; + + class BonjourRegisterQuery : public DNSSDRegisterQuery, public BonjourQuery { + public: + BonjourRegisterQuery(const String& name, int port, const ByteArray& txtRecord, boost::shared_ptr<BonjourQuerier> querier) : BonjourQuery(querier) { + DNSServiceErrorType result = DNSServiceRegister( + &sdRef, 0, 0, name.getUTF8Data(), "_presence._tcp", NULL, NULL, port, + txtRecord.getSize(), txtRecord.getData(), + &BonjourRegisterQuery::handleServiceRegisteredStatic, this); + if (result != kDNSServiceErr_NoError) { + sdRef = NULL; + } + } + + void registerService() { + if (sdRef) { + run(); + } + else { + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } + } + + void unregisterService() { + finish(); + } + + void updateServiceInfo(const ByteArray& txtRecord) { + boost::lock_guard<boost::mutex> lock(sdRefMutex); + DNSServiceUpdateRecord(sdRef, NULL, NULL, txtRecord.getSize(), txtRecord.getData(), 0); + } + + private: + static void handleServiceRegisteredStatic(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) { + static_cast<BonjourRegisterQuery*>(context)->handleServiceRegistered(errorCode, name, regtype, domain); + } + + void handleServiceRegistered(DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain) { + if (errorCode != kDNSServiceErr_NoError) { + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } + else { + MainEventLoop::postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>(DNSSDServiceID(name, domain, regtype, 0))), shared_from_this()); + } + } + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h new file mode 100644 index 0000000..7b5f19a --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h @@ -0,0 +1,63 @@ +#pragma once + +#include "Swiften/Base/String.h" +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h" +#include "Swiften/EventLoop/MainEventLoop.h" +#include "Swiften/Network/HostAddress.h" + +#include <netinet/in.h> + +namespace Swift { + class BonjourQuerier; + + class BonjourResolveHostnameQuery : public DNSSDResolveHostnameQuery, public BonjourQuery { + public: + BonjourResolveHostnameQuery(const String& hostname, int interfaceIndex, boost::shared_ptr<BonjourQuerier> querier) : BonjourQuery(querier) { + DNSServiceErrorType result = DNSServiceGetAddrInfo( + &sdRef, 0, interfaceIndex, kDNSServiceProtocol_IPv4, + hostname.getUTF8Data(), + &BonjourResolveHostnameQuery::handleHostnameResolvedStatic, this); + if (result != kDNSServiceErr_NoError) { + sdRef = NULL; + } + } + + //void DNSSDResolveHostnameQuery::run() { + void run() { + if (sdRef) { + BonjourQuery::run(); + } + else { + MainEventLoop::postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional<HostAddress>()), shared_from_this()); + } + } + + void finish() { + BonjourQuery::finish(); + } + + private: + static void handleHostnameResolvedStatic(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType errorCode, const char*, const struct sockaddr *address, uint32_t, void *context) { + static_cast<BonjourResolveHostnameQuery*>(context)->handleHostnameResolved(errorCode, address); + } + + void handleHostnameResolved(DNSServiceErrorType errorCode, const struct sockaddr *rawAddress) { + if (errorCode) { + MainEventLoop::postEvent( + boost::bind(boost::ref(onHostnameResolved), + boost::optional<HostAddress>()), + shared_from_this()); + } + else { + assert(rawAddress->sa_family == AF_INET); + const sockaddr_in* sa = reinterpret_cast<const sockaddr_in*>(rawAddress); + uint32_t address = ntohl(sa->sin_addr.s_addr); + MainEventLoop::postEvent(boost::bind( + boost::ref(onHostnameResolved), + HostAddress(reinterpret_cast<unsigned char*>(&address), 4)), + shared_from_this()); + } + } + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h new file mode 100644 index 0000000..1c38179 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h @@ -0,0 +1,58 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" +#include "Swiften/LinkLocal/LinkLocalServiceInfo.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class BonjourQuerier; + + class BonjourResolveServiceQuery : public DNSSDResolveServiceQuery, public BonjourQuery { + public: + BonjourResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr<BonjourQuerier> querier) : BonjourQuery(querier) { + DNSServiceErrorType result = DNSServiceResolve( + &sdRef, 0, service.getNetworkInterfaceID(), + service.getName().getUTF8Data(), service.getType().getUTF8Data(), + service.getDomain().getUTF8Data(), + &BonjourResolveServiceQuery::handleServiceResolvedStatic, this); + if (result != kDNSServiceErr_NoError) { + sdRef = NULL; + } + } + + void start() { + if (sdRef) { + run(); + } + else { + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this()); + } + } + + void stop() { + finish(); + } + + private: + static void handleServiceResolvedStatic(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context) { + static_cast<BonjourResolveServiceQuery*>(context)->handleServiceResolved(errorCode, fullname, hosttarget, port, txtLen, txtRecord); + } + + void handleServiceResolved(DNSServiceErrorType errorCode, const char* fullName, const char* host, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord) { + if (errorCode != kDNSServiceErr_NoError) { + MainEventLoop::postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this()); + } + else { + //std::cout << "Service resolved: name:" << fullName << " host:" << host << " port:" << port << std::endl; + MainEventLoop::postEvent( + boost::bind( + boost::ref(onServiceResolved), + Result(String(fullName), String(host), port, + ByteArray(reinterpret_cast<const char*>(txtRecord), txtLen))), + shared_from_this()); + } + } + }; +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.cpp b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.cpp new file mode 100644 index 0000000..1dbff2c --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.cpp @@ -0,0 +1,8 @@ +#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h" + +namespace Swift { + +DNSSDBrowseQuery::~DNSSDBrowseQuery() { +} + +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h new file mode 100644 index 0000000..9b30858 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h @@ -0,0 +1,19 @@ +#pragma once + +#include <boost/signal.hpp> + +#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" + +namespace Swift { + class DNSSDBrowseQuery { + public: + virtual ~DNSSDBrowseQuery(); + + virtual void startBrowsing() = 0; + virtual void stopBrowsing() = 0; + + boost::signal<void (const DNSSDServiceID&)> onServiceAdded; + boost::signal<void (const DNSSDServiceID&)> onServiceRemoved; + boost::signal<void ()> onError; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDQuerier.cpp b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.cpp new file mode 100644 index 0000000..cc8d6ef --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.cpp @@ -0,0 +1,8 @@ +#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h" + +namespace Swift { + +DNSSDQuerier::~DNSSDQuerier() { +} + +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h new file mode 100644 index 0000000..efcc140 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h @@ -0,0 +1,29 @@ +#pragma once + +#include <boost/shared_ptr.hpp> + +namespace Swift { + class String; + class ByteArray; + class DNSSDServiceID; + class DNSSDBrowseQuery; + class DNSSDRegisterQuery; + class DNSSDResolveServiceQuery; + class DNSSDResolveHostnameQuery; + + class DNSSDQuerier { + public: + virtual ~DNSSDQuerier(); + + virtual void start() = 0; + virtual void stop() = 0; + + virtual boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery() = 0; + virtual boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery( + const String& name, int port, const ByteArray& info) = 0; + virtual boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery( + const DNSSDServiceID&) = 0; + virtual boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery( + const String& hostname, int interfaceIndex) = 0; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.cpp b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.cpp new file mode 100644 index 0000000..bbb8692 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.cpp @@ -0,0 +1,8 @@ +#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h" + +namespace Swift { + +DNSSDRegisterQuery::~DNSSDRegisterQuery() { +} + +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h new file mode 100644 index 0000000..4a04fa9 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h @@ -0,0 +1,21 @@ +#pragma once + +#include <boost/signal.hpp> +#include <boost/optional.hpp> + +#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" + +namespace Swift { + class ByteArray; + + class DNSSDRegisterQuery { + public: + virtual ~DNSSDRegisterQuery(); + + virtual void registerService() = 0; + virtual void unregisterService() = 0; + virtual void updateServiceInfo(const ByteArray& info) = 0; + + boost::signal<void (boost::optional<DNSSDServiceID>)> onRegisterFinished; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp b/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp new file mode 100644 index 0000000..e247e39 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp @@ -0,0 +1,8 @@ +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h" + +namespace Swift { + +DNSSDResolveHostnameQuery::~DNSSDResolveHostnameQuery() { +} + +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h new file mode 100644 index 0000000..1b9f291 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h @@ -0,0 +1,18 @@ +#pragma once + +#include <boost/signal.hpp> +#include <boost/optional.hpp> + +#include "Swiften/Network/HostAddress.h" + +namespace Swift { + class DNSSDResolveHostnameQuery { + public: + virtual ~DNSSDResolveHostnameQuery(); + + virtual void run() = 0; + virtual void finish() = 0; + + boost::signal<void (const boost::optional<HostAddress>&)> onHostnameResolved; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.cpp b/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.cpp new file mode 100644 index 0000000..67a5d66 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.cpp @@ -0,0 +1,8 @@ +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" + +namespace Swift { + +DNSSDResolveServiceQuery::~DNSSDResolveServiceQuery() { +} + +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h new file mode 100644 index 0000000..217e396 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h @@ -0,0 +1,28 @@ +#pragma once + +#include <boost/signal.hpp> +#include <boost/optional.hpp> + +#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" +#include "Swiften/Base/ByteArray.h" + +namespace Swift { + class DNSSDResolveServiceQuery { + public: + struct Result { + Result(const String& fullName, const String& host, int port, const ByteArray& info) : + fullName(fullName), host(host), port(port), info(info) {} + String fullName; + String host; + int port; + ByteArray info; + }; + + virtual ~DNSSDResolveServiceQuery(); + + virtual void start() = 0; + virtual void stop() = 0; + + boost::signal<void (const boost::optional<Result>&)> onServiceResolved; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDServiceID.cpp b/Swiften/LinkLocal/DNSSD/DNSSDServiceID.cpp new file mode 100644 index 0000000..b360ee5 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDServiceID.cpp @@ -0,0 +1,7 @@ +#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" + +namespace Swift { + +const char* DNSSDServiceID::PresenceServiceType = "_presence._tcp"; + +} diff --git a/Swiften/LinkLocal/DNSSD/DNSSDServiceID.h b/Swiften/LinkLocal/DNSSD/DNSSDServiceID.h new file mode 100644 index 0000000..ba7828b --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/DNSSDServiceID.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Swiften/Base/String.h" + +namespace Swift { + class DNSSDServiceID { + public: + static const char* PresenceServiceType; + + DNSSDServiceID( + const String& name, + const String& domain, + const String& type = PresenceServiceType, + int networkInterface = 0) : + name(name), + domain(domain), + type(type), + networkInterface(networkInterface) { + } + + bool operator==(const DNSSDServiceID& o) const { + return name == o.name && domain == o.domain && type == o.type && (networkInterface != 0 && o.networkInterface != 0 ? networkInterface == o.networkInterface : true); + } + + bool operator<(const DNSSDServiceID& o) const { + if (o.name == name) { + if (o.domain == domain) { + if (o.type == type) { + return networkInterface < o.networkInterface; + } + else { + return type < o.type; + } + } + else { + return domain < o.domain; + } + } + else { + return o.name < name; + } + } + + const String& getName() const { + return name; + } + + const String& getDomain() const { + return domain; + } + + const String& getType() const { + return type; + } + + int getNetworkInterfaceID() const { + return networkInterface; + } + + private: + String name; + String domain; + String type; + int networkInterface; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h new file mode 100644 index 0000000..5a0b93b --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h" + +namespace Swift { + class FakeDNSSDQuerier; + + class FakeDNSSDBrowseQuery : public DNSSDBrowseQuery, public FakeDNSSDQuery { + public: + FakeDNSSDBrowseQuery(boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier) { + } + + void startBrowsing() { + run(); + } + + void stopBrowsing() { + finish(); + } + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp new file mode 100644 index 0000000..c26f8ee --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp @@ -0,0 +1,130 @@ +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h" + +#include <boost/bind.hpp> + +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h" +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h" +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h" +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + +FakeDNSSDQuerier::FakeDNSSDQuerier(const String& domain) : domain(domain) { +} + +FakeDNSSDQuerier::~FakeDNSSDQuerier() { + if (!runningQueries.empty()) { + std::cerr << "FakeDNSSDQuerier: Running queries not empty at destruction time" << std::endl; + } +} + +boost::shared_ptr<DNSSDBrowseQuery> FakeDNSSDQuerier::createBrowseQuery() { + return boost::shared_ptr<DNSSDBrowseQuery>(new FakeDNSSDBrowseQuery(shared_from_this())); +} + +boost::shared_ptr<DNSSDRegisterQuery> FakeDNSSDQuerier::createRegisterQuery(const String& name, int port, const ByteArray& info) { + return boost::shared_ptr<DNSSDRegisterQuery>(new FakeDNSSDRegisterQuery(name, port, info, shared_from_this())); +} + +boost::shared_ptr<DNSSDResolveServiceQuery> FakeDNSSDQuerier::createResolveServiceQuery(const DNSSDServiceID& service) { + return boost::shared_ptr<DNSSDResolveServiceQuery>(new FakeDNSSDResolveServiceQuery(service, shared_from_this())); +} + +boost::shared_ptr<DNSSDResolveHostnameQuery> FakeDNSSDQuerier::createResolveHostnameQuery(const String& hostname, int interfaceIndex) { + return boost::shared_ptr<DNSSDResolveHostnameQuery>(new FakeDNSSDResolveHostnameQuery(hostname, interfaceIndex, shared_from_this())); +} + +void FakeDNSSDQuerier::addRunningQuery(boost::shared_ptr<FakeDNSSDQuery> query) { + runningQueries.push_back(query); + if (boost::shared_ptr<FakeDNSSDBrowseQuery> browseQuery = boost::dynamic_pointer_cast<FakeDNSSDBrowseQuery>(query)) { + foreach(const DNSSDServiceID& service, services) { + MainEventLoop::postEvent(boost::bind(boost::ref(browseQuery->onServiceAdded), service), shared_from_this()); + } + } + else if (boost::shared_ptr<FakeDNSSDResolveServiceQuery> resolveQuery = boost::dynamic_pointer_cast<FakeDNSSDResolveServiceQuery>(query)) { + for(ServiceInfoMap::const_iterator i = serviceInfo.begin(); i != serviceInfo.end(); ++i) { + if (i->first == resolveQuery->service) { + MainEventLoop::postEvent(boost::bind(boost::ref(resolveQuery->onServiceResolved), i->second), shared_from_this()); + } + } + } + else if (boost::shared_ptr<FakeDNSSDRegisterQuery> registerQuery = boost::dynamic_pointer_cast<FakeDNSSDRegisterQuery>(query)) { + DNSSDServiceID service(registerQuery->name, domain); + MainEventLoop::postEvent(boost::bind(boost::ref(registerQuery->onRegisterFinished), service), shared_from_this()); + } + else if (boost::shared_ptr<FakeDNSSDResolveHostnameQuery> resolveHostnameQuery = boost::dynamic_pointer_cast<FakeDNSSDResolveHostnameQuery>(query)) { + std::map<String,boost::optional<HostAddress> >::const_iterator i = addresses.find(resolveHostnameQuery->hostname); + if (i != addresses.end()) { + MainEventLoop::postEvent( + boost::bind( + boost::ref(resolveHostnameQuery->onHostnameResolved), i->second), + shared_from_this()); + } + } +} + +void FakeDNSSDQuerier::removeRunningQuery(boost::shared_ptr<FakeDNSSDQuery> query) { + runningQueries.erase(std::remove( + runningQueries.begin(), runningQueries.end(), query), runningQueries.end()); +} + +void FakeDNSSDQuerier::addService(const DNSSDServiceID& id) { + services.insert(id); + foreach(const boost::shared_ptr<FakeDNSSDBrowseQuery>& query, getQueries<FakeDNSSDBrowseQuery>()) { + MainEventLoop::postEvent(boost::bind(boost::ref(query->onServiceAdded), id), shared_from_this()); + } +} + +void FakeDNSSDQuerier::removeService(const DNSSDServiceID& id) { + services.erase(id); + serviceInfo.erase(id); + foreach(const boost::shared_ptr<FakeDNSSDBrowseQuery>& query, getQueries<FakeDNSSDBrowseQuery>()) { + MainEventLoop::postEvent(boost::bind(boost::ref(query->onServiceRemoved), id), shared_from_this()); + } +} + +void FakeDNSSDQuerier::setServiceInfo(const DNSSDServiceID& id, const DNSSDResolveServiceQuery::Result& info) { + std::pair<ServiceInfoMap::iterator, bool> r = serviceInfo.insert(std::make_pair(id, info)); + if (!r.second) { + r.first->second = info; + } + foreach(const boost::shared_ptr<FakeDNSSDResolveServiceQuery>& query, getQueries<FakeDNSSDResolveServiceQuery>()) { + if (query->service == id) { + MainEventLoop::postEvent(boost::bind(boost::ref(query->onServiceResolved), info), shared_from_this()); + } + } +} + +bool FakeDNSSDQuerier::isServiceRegistered(const String& name, int port, const ByteArray& info) { + foreach(const boost::shared_ptr<FakeDNSSDRegisterQuery>& query, getQueries<FakeDNSSDRegisterQuery>()) { + if (query->name == name && query->port == port && query->info == info) { + return true; + } + } + return false; +} + +void FakeDNSSDQuerier::setBrowseError() { + foreach(const boost::shared_ptr<FakeDNSSDBrowseQuery>& query, getQueries<FakeDNSSDBrowseQuery>()) { + MainEventLoop::postEvent(boost::ref(query->onError), shared_from_this()); + } +} + +void FakeDNSSDQuerier::setRegisterError() { + foreach(const boost::shared_ptr<FakeDNSSDRegisterQuery>& query, getQueries<FakeDNSSDRegisterQuery>()) { + MainEventLoop::postEvent(boost::bind(boost::ref(query->onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); + } +} + +void FakeDNSSDQuerier::setAddress(const String& hostname, boost::optional<HostAddress> address) { + addresses[hostname] = address; + foreach(const boost::shared_ptr<FakeDNSSDResolveHostnameQuery>& query, getQueries<FakeDNSSDResolveHostnameQuery>()) { + if (query->hostname == hostname) { + MainEventLoop::postEvent(boost::bind( + boost::ref(query->onHostnameResolved), address), shared_from_this()); + } + } +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h new file mode 100644 index 0000000..22bca0c --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h @@ -0,0 +1,71 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <list> +#include <set> + +#include "Swiften/Base/foreach.h" +#include "Swiften/Base/String.h" +#include "Swiften/EventLoop/EventOwner.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" +#include "Swiften/Network/HostAddress.h" + +namespace Swift { + class ByteArray; + class FakeDNSSDQuery; + class FakeDNSSDBrowseQuery; + + class FakeDNSSDQuerier : + public DNSSDQuerier, + public EventOwner, + public boost::enable_shared_from_this<FakeDNSSDQuerier> { + public: + FakeDNSSDQuerier(const String& domain); + ~FakeDNSSDQuerier(); + + void start() {} + void stop() {} + + boost::shared_ptr<DNSSDBrowseQuery> createBrowseQuery(); + boost::shared_ptr<DNSSDRegisterQuery> createRegisterQuery( + const String& name, int port, const ByteArray& info); + boost::shared_ptr<DNSSDResolveServiceQuery> createResolveServiceQuery( + const DNSSDServiceID&); + boost::shared_ptr<DNSSDResolveHostnameQuery> createResolveHostnameQuery( + const String& hostname, int interfaceIndex); + + void addRunningQuery(boost::shared_ptr<FakeDNSSDQuery>); + void removeRunningQuery(boost::shared_ptr<FakeDNSSDQuery>); + + void addService(const DNSSDServiceID& id); + void removeService(const DNSSDServiceID& id); + void setServiceInfo(const DNSSDServiceID& id, const DNSSDResolveServiceQuery::Result& info); + bool isServiceRegistered(const String& name, int port, const ByteArray& info); + void setAddress(const String& hostname, boost::optional<HostAddress> address); + + void setBrowseError(); + void setRegisterError(); + + private: + template<typename T> + std::vector< boost::shared_ptr<T> > getQueries() const { + std::vector< boost::shared_ptr<T> > result; + foreach(const boost::shared_ptr<FakeDNSSDQuery>& query, runningQueries) { + if (boost::shared_ptr<T> resultQuery = boost::dynamic_pointer_cast<T>(query)) { + result.push_back(resultQuery); + } + } + return result; + } + + private: + String domain; + std::list< boost::shared_ptr<FakeDNSSDQuery> > runningQueries; + std::set<DNSSDServiceID> services; + typedef std::map<DNSSDServiceID,DNSSDResolveServiceQuery::Result> ServiceInfoMap; + ServiceInfoMap serviceInfo; + std::map<String, boost::optional<HostAddress> > addresses; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp new file mode 100644 index 0000000..ced7850 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp @@ -0,0 +1,20 @@ +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h" +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h" + +namespace Swift { + +FakeDNSSDQuery::FakeDNSSDQuery(boost::shared_ptr<FakeDNSSDQuerier> querier) : querier(querier) { +} + +FakeDNSSDQuery::~FakeDNSSDQuery() { +} + +void FakeDNSSDQuery::run() { + querier->addRunningQuery(shared_from_this()); +} + +void FakeDNSSDQuery::finish() { + querier->removeRunningQuery(shared_from_this()); +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h new file mode 100644 index 0000000..9fca1d4 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h @@ -0,0 +1,25 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> + +#include "Swiften/EventLoop/EventOwner.h" + +namespace Swift { + class FakeDNSSDQuerier; + + class FakeDNSSDQuery : + public EventOwner, + public boost::enable_shared_from_this<FakeDNSSDQuery> { + public: + FakeDNSSDQuery(boost::shared_ptr<FakeDNSSDQuerier>); + virtual ~FakeDNSSDQuery(); + + protected: + void run(); + void finish(); + + protected: + boost::shared_ptr<FakeDNSSDQuerier> querier; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h new file mode 100644 index 0000000..82ec623 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/Base/String.h" + +namespace Swift { + class FakeDNSSDQuerier; + + class FakeDNSSDRegisterQuery : public DNSSDRegisterQuery, public FakeDNSSDQuery { + public: + FakeDNSSDRegisterQuery(const String& name, int port, const ByteArray& info, boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier), name(name), port(port), info(info) { + } + + void registerService() { + run(); + } + + void updateServiceInfo(const ByteArray& i) { + info = i; + } + + void unregisterService() { + finish(); + } + + String name; + int port; + ByteArray info; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h new file mode 100644 index 0000000..5ed42af --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Swiften/Base/String.h" +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h" +#include "Swiften/Network/HostAddress.h" + +namespace Swift { + class FakeDNSSDQuerier; + + class FakeDNSSDResolveHostnameQuery : public DNSSDResolveHostnameQuery, public FakeDNSSDQuery { + public: + FakeDNSSDResolveHostnameQuery(const String& hostname, int interfaceIndex, boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier), hostname(hostname), interfaceIndex(interfaceIndex) { + } + + void run() { + FakeDNSSDQuery::run(); + } + + void finish() { + FakeDNSSDQuery::finish(); + } + + String hostname; + int interfaceIndex; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h new file mode 100644 index 0000000..60d35e5 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" +#include "Swiften/LinkLocal/LinkLocalServiceInfo.h" + +namespace Swift { + class FakeDNSSDQuerier; + + class FakeDNSSDResolveServiceQuery : public DNSSDResolveServiceQuery, public FakeDNSSDQuery { + public: + FakeDNSSDResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr<FakeDNSSDQuerier> querier) : FakeDNSSDQuery(querier), service(service) { + } + + void start() { + run(); + } + + void stop() { + finish(); + } + + DNSSDServiceID service; + }; +} diff --git a/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp new file mode 100644 index 0000000..7b06350 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp @@ -0,0 +1,22 @@ +#include "Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h" + +#if defined(HAVE_BONJOUR) +#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h" +#elif defined(HAVE_AVAHI) +#include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h" +#endif + + +namespace Swift { + +boost::shared_ptr<DNSSDQuerier> PlatformDNSSDQuerierFactory::createQuerier() { +#if defined(HAVE_BONJOUR) + return boost::shared_ptr<DNSSDQuerier>(new BonjourQuerier()); +#elif defined(HAVE_AVAHI) + return boost::shared_ptr<DNSSDQuerier>(new AvahiQuerier()); +#else + return boost::shared_ptr<DNSSDQuerier>(); +#endif +} + +} diff --git a/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h new file mode 100644 index 0000000..b52814b --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h @@ -0,0 +1,12 @@ +#pragma once + +#include <boost/shared_ptr.hpp> + +namespace Swift { + class DNSSDQuerier; + + class PlatformDNSSDQuerierFactory { + public: + boost::shared_ptr<DNSSDQuerier> createQuerier(); + }; +} diff --git a/Swiften/LinkLocal/IncomingLinkLocalSession.cpp b/Swiften/LinkLocal/IncomingLinkLocalSession.cpp new file mode 100644 index 0000000..77910e6 --- /dev/null +++ b/Swiften/LinkLocal/IncomingLinkLocalSession.cpp @@ -0,0 +1,62 @@ +#include "Swiften/LinkLocal/IncomingLinkLocalSession.h" + +#include <boost/bind.hpp> + +#include "Swiften/Elements/ProtocolHeader.h" +#include "Swiften/Network/Connection.h" +#include "Swiften/StreamStack/StreamStack.h" +#include "Swiften/StreamStack/ConnectionLayer.h" +#include "Swiften/StreamStack/XMPPLayer.h" +#include "Swiften/Elements/StreamFeatures.h" +#include "Swiften/Elements/IQ.h" + +namespace Swift { + +IncomingLinkLocalSession::IncomingLinkLocalSession( + const JID& localJID, + boost::shared_ptr<Connection> connection, + PayloadParserFactoryCollection* payloadParserFactories, + PayloadSerializerCollection* payloadSerializers) : + Session(connection, payloadParserFactories, payloadSerializers), + initialized(false) { + setLocalJID(localJID); +} + +void IncomingLinkLocalSession::handleStreamStart(const ProtocolHeader& incomingHeader) { + setRemoteJID(JID(incomingHeader.getFrom())); + if (!getRemoteJID().isValid()) { + finishSession(); + return; + } + + ProtocolHeader header; + header.setFrom(getLocalJID()); + getXMPPLayer()->writeHeader(header); + + if (incomingHeader.getVersion() == "1.0") { + getXMPPLayer()->writeElement(boost::shared_ptr<StreamFeatures>(new StreamFeatures())); + } + else { + setInitialized(); + } +} + +void IncomingLinkLocalSession::handleElement(boost::shared_ptr<Element> element) { + boost::shared_ptr<Stanza> stanza = boost::dynamic_pointer_cast<Stanza>(element); + // If we get our first stanza before streamfeatures, our session is implicitly + // initialized + if (stanza && !isInitialized()) { + setInitialized(); + } + + onElementReceived(element); +} + +void IncomingLinkLocalSession::setInitialized() { + initialized = true; + onSessionStarted(); +} + + + +} diff --git a/Swiften/LinkLocal/IncomingLinkLocalSession.h b/Swiften/LinkLocal/IncomingLinkLocalSession.h new file mode 100644 index 0000000..e3f0460 --- /dev/null +++ b/Swiften/LinkLocal/IncomingLinkLocalSession.h @@ -0,0 +1,37 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/signal.hpp> + +#include "Swiften/Session/Session.h" +#include "Swiften/JID/JID.h" +#include "Swiften/Network/Connection.h" + +namespace Swift { + class ProtocolHeader; + class String; + class Element; + class PayloadParserFactoryCollection; + class PayloadSerializerCollection; + + class IncomingLinkLocalSession : public Session { + public: + IncomingLinkLocalSession( + const JID& localJID, + boost::shared_ptr<Connection> connection, + PayloadParserFactoryCollection* payloadParserFactories, + PayloadSerializerCollection* payloadSerializers); + + boost::signal<void ()> onSessionStarted; + + private: + void handleElement(boost::shared_ptr<Element>); + void handleStreamStart(const ProtocolHeader&); + void setInitialized(); + bool isInitialized() const { + return initialized; + } + + bool initialized; + }; +} diff --git a/Swiften/LinkLocal/LinkLocalConnector.cpp b/Swiften/LinkLocal/LinkLocalConnector.cpp new file mode 100644 index 0000000..fba4a4e --- /dev/null +++ b/Swiften/LinkLocal/LinkLocalConnector.cpp @@ -0,0 +1,68 @@ +#include "Swiften/LinkLocal/LinkLocalConnector.h" + +#include <boost/bind.hpp> + +#include "Swiften/Network/Connection.h" +#include "Swiften/Network/ConnectionFactory.h" +#include "Swiften/Network/HostAddress.h" +#include "Swiften/Network/HostAddressPort.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h" + +namespace Swift { + +LinkLocalConnector::LinkLocalConnector( + const LinkLocalService& service, + boost::shared_ptr<DNSSDQuerier> querier, + boost::shared_ptr<Connection> connection) : + service(service), + querier(querier), + connection(connection) { +} + +LinkLocalConnector::~LinkLocalConnector() { + assert(!resolveQuery); +} + +void LinkLocalConnector::connect() { + resolveQuery = querier->createResolveHostnameQuery( + service.getHostname(), + service.getID().getNetworkInterfaceID()); + resolveQuery->onHostnameResolved.connect(boost::bind( + &LinkLocalConnector::handleHostnameResolved, + boost::dynamic_pointer_cast<LinkLocalConnector>(shared_from_this()), + _1)); + resolveQuery->run(); +} + +void LinkLocalConnector::cancel() { + if (resolveQuery) { + resolveQuery->finish(); + } + resolveQuery.reset(); + connection->disconnect(); +} + +void LinkLocalConnector::handleHostnameResolved(const boost::optional<HostAddress>& address) { + resolveQuery->finish(); + resolveQuery.reset(); + if (address) { + connection->onConnectFinished.connect( + boost::bind(boost::ref(onConnectFinished), _1)); + connection->connect(HostAddressPort(*address, service.getPort())); + } + else { + onConnectFinished(true); + } +} + +void LinkLocalConnector::handleConnected(bool error) { + onConnectFinished(error); +} + +void LinkLocalConnector::queueElement(boost::shared_ptr<Element> element) { + queuedElements.push_back(element); +} + + +} diff --git a/Swiften/LinkLocal/LinkLocalConnector.h b/Swiften/LinkLocal/LinkLocalConnector.h new file mode 100644 index 0000000..0b6baef --- /dev/null +++ b/Swiften/LinkLocal/LinkLocalConnector.h @@ -0,0 +1,57 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/signal.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <vector> + +#include "Swiften/Network/Connection.h" +#include "Swiften/LinkLocal/LinkLocalService.h" + +namespace Swift { + class ConnectionFactory; + class HostAddress; + class Element; + class PayloadParserFactoryCollection; + class PayloadSerializerCollection; + class DNSSDQuerier; + class DNSSDResolveHostnameQuery; + + class LinkLocalConnector : public boost::enable_shared_from_this<LinkLocalConnector> { + public: + LinkLocalConnector( + const LinkLocalService& service, + boost::shared_ptr<DNSSDQuerier> querier, + boost::shared_ptr<Connection> connection); + ~LinkLocalConnector(); + + const LinkLocalService& getService() const { + return service; + } + + void connect(); + void cancel(); + void queueElement(boost::shared_ptr<Element> element); + + const std::vector<boost::shared_ptr<Element> >& getQueuedElements() const { + return queuedElements; + } + + boost::shared_ptr<Connection> getConnection() const { + return connection; + } + + boost::signal<void (bool)> onConnectFinished; + + private: + void handleHostnameResolved(const boost::optional<HostAddress>& address); + void handleConnected(bool error); + + private: + LinkLocalService service; + boost::shared_ptr<DNSSDQuerier> querier; + boost::shared_ptr<DNSSDResolveHostnameQuery> resolveQuery; + boost::shared_ptr<Connection> connection; + std::vector<boost::shared_ptr<Element> > queuedElements; + }; +} diff --git a/Swiften/LinkLocal/LinkLocalService.cpp b/Swiften/LinkLocal/LinkLocalService.cpp new file mode 100644 index 0000000..f1114ed --- /dev/null +++ b/Swiften/LinkLocal/LinkLocalService.cpp @@ -0,0 +1,27 @@ +#include "Swiften/LinkLocal/LinkLocalService.h" + +namespace Swift { + +String LinkLocalService::getDescription() const { + LinkLocalServiceInfo info = getInfo(); + if (!info.getNick().isEmpty()) { + return info.getNick(); + } + else if (!info.getFirstName().isEmpty()) { + String result = info.getFirstName(); + if (!info.getLastName().isEmpty()) { + result += " " + info.getLastName(); + } + return result; + } + else if (!info.getLastName().isEmpty()) { + return info.getLastName(); + } + return getName(); +} + +JID LinkLocalService::getJID() const { + return JID(getName()); +} + +} diff --git a/Swiften/LinkLocal/LinkLocalService.h b/Swiften/LinkLocal/LinkLocalService.h new file mode 100644 index 0000000..8ae593c --- /dev/null +++ b/Swiften/LinkLocal/LinkLocalService.h @@ -0,0 +1,46 @@ +#pragma once + +#include "Swiften/Base/String.h" +#include "Swiften/JID/JID.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" +#include "Swiften/LinkLocal/LinkLocalServiceInfo.h" + +namespace Swift { + class LinkLocalService { + public: + LinkLocalService( + const DNSSDServiceID& id, + const DNSSDResolveServiceQuery::Result& info) : + id(id), + info(info) {} + + const DNSSDServiceID& getID() const { + return id; + } + + const String& getName() const { + return id.getName(); + } + + int getPort() const { + return info.port; + } + + const String& getHostname() const { + return info.host; + } + + LinkLocalServiceInfo getInfo() const { + return LinkLocalServiceInfo::createFromTXTRecord(info.info); + } + + String getDescription() const; + + JID getJID() const; + + private: + DNSSDServiceID id; + DNSSDResolveServiceQuery::Result info; + }; +} diff --git a/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp b/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp new file mode 100644 index 0000000..061bf2c --- /dev/null +++ b/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp @@ -0,0 +1,147 @@ +#include <boost/bind.hpp> +#include <iostream> + +#include "Swiften/LinkLocal/LinkLocalServiceBrowser.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h" +#include "Swiften/Network/HostAddress.h" + +namespace Swift { + +LinkLocalServiceBrowser::LinkLocalServiceBrowser(boost::shared_ptr<DNSSDQuerier> querier) : querier(querier), haveError(false) { +} + +LinkLocalServiceBrowser::~LinkLocalServiceBrowser() { + if (isRunning()) { + std::cerr << "WARNING: LinkLocalServiceBrowser still running on destruction" << std::endl; + } +} + + +void LinkLocalServiceBrowser::start() { + assert(!isRunning()); + haveError = false; + browseQuery = querier->createBrowseQuery(); + browseQuery->onServiceAdded.connect( + boost::bind(&LinkLocalServiceBrowser::handleServiceAdded, this, _1)); + browseQuery->onServiceRemoved.connect( + boost::bind(&LinkLocalServiceBrowser::handleServiceRemoved, this, _1)); + browseQuery->onError.connect( + boost::bind(&LinkLocalServiceBrowser::handleBrowseError, this)); + browseQuery->startBrowsing(); +} + +void LinkLocalServiceBrowser::stop() { + assert(isRunning()); + if (isRegistered()) { + unregisterService(); + } + for (ResolveQueryMap::const_iterator i = resolveQueries.begin(); i != resolveQueries.end(); ++i) { + i->second->stop(); + } + resolveQueries.clear(); + services.clear(); + browseQuery->stopBrowsing(); + browseQuery.reset(); + onStopped(haveError); +} + +bool LinkLocalServiceBrowser::isRunning() const { + return browseQuery; +} + +bool LinkLocalServiceBrowser::hasError() const { + return haveError; +} + +bool LinkLocalServiceBrowser::isRegistered() const { + return registerQuery; +} + +void LinkLocalServiceBrowser::registerService(const String& name, int port, const LinkLocalServiceInfo& info) { + assert(!registerQuery); + registerQuery = querier->createRegisterQuery(name, port, info.toTXTRecord()); + registerQuery->onRegisterFinished.connect( + boost::bind(&LinkLocalServiceBrowser::handleRegisterFinished, this, _1)); + registerQuery->registerService(); +} + +void LinkLocalServiceBrowser::updateService(const LinkLocalServiceInfo& info) { + assert(registerQuery); + registerQuery->updateServiceInfo(info.toTXTRecord()); +} + +void LinkLocalServiceBrowser::unregisterService() { + assert(registerQuery); + registerQuery->unregisterService(); + registerQuery.reset(); + selfService.reset(); +} + +std::vector<LinkLocalService> LinkLocalServiceBrowser::getServices() const { + std::vector<LinkLocalService> result; + for (ServiceMap::const_iterator i = services.begin(); i != services.end(); ++i) { + result.push_back(LinkLocalService(i->first, i->second)); + } + return result; +} + +void LinkLocalServiceBrowser::handleServiceAdded(const DNSSDServiceID& service) { + if (selfService && service == *selfService) { + return; + } + boost::shared_ptr<DNSSDResolveServiceQuery> resolveQuery = querier->createResolveServiceQuery(service); + resolveQuery->onServiceResolved.connect( + boost::bind(&LinkLocalServiceBrowser::handleServiceResolved, this, service, _1)); + std::pair<ResolveQueryMap::iterator, bool> r = resolveQueries.insert(std::make_pair(service, resolveQuery)); + if (!r.second) { + r.first->second = resolveQuery; + } + resolveQuery->start(); +} + +void LinkLocalServiceBrowser::handleServiceRemoved(const DNSSDServiceID& service) { + ResolveQueryMap::iterator i = resolveQueries.find(service); + if (i == resolveQueries.end()) { + // Can happen after an unregister(), when getting the old 'self' + // service remove notification. + return; + } + i->second->stop(); + resolveQueries.erase(i); + ServiceMap::iterator j = services.find(service); + assert(j != services.end()); + LinkLocalService linkLocalService(j->first, j->second); + services.erase(j); + onServiceRemoved(linkLocalService); +} + +void LinkLocalServiceBrowser::handleServiceResolved(const DNSSDServiceID& service, const boost::optional<DNSSDResolveServiceQuery::Result>& result) { + if (result) { + std::pair<ServiceMap::iterator, bool> r = services.insert(std::make_pair(service, *result)); + if (r.second) { + onServiceAdded(LinkLocalService(r.first->first, r.first->second)); + } + else { + r.first->second = *result; + onServiceChanged(LinkLocalService(r.first->first, r.first->second)); + } + } +} + +void LinkLocalServiceBrowser::handleRegisterFinished(const boost::optional<DNSSDServiceID>& result) { + if (result) { + selfService = result; + onServiceRegistered(*result); + } + else { + haveError = true; + stop(); + } +} + +void LinkLocalServiceBrowser::handleBrowseError() { + haveError = true; + stop(); +} + +} diff --git a/Swiften/LinkLocal/LinkLocalServiceBrowser.h b/Swiften/LinkLocal/LinkLocalServiceBrowser.h new file mode 100644 index 0000000..66973d5 --- /dev/null +++ b/Swiften/LinkLocal/LinkLocalServiceBrowser.h @@ -0,0 +1,68 @@ +#pragma once + +#include <boost/signal.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/optional.hpp> +#include <map> +#include <vector> + +#include "Swiften/Base/String.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" +#include "Swiften/LinkLocal/LinkLocalService.h" +#include "Swiften/LinkLocal/LinkLocalServiceInfo.h" + +namespace Swift { + class LinkLocalServiceBrowser { + public: + LinkLocalServiceBrowser(boost::shared_ptr<DNSSDQuerier> querier); + ~LinkLocalServiceBrowser(); + + void start(); + void stop(); + bool isRunning() const; + bool hasError() const; + + void registerService( + const String& name, + int port, + const LinkLocalServiceInfo& info = LinkLocalServiceInfo()); + void updateService( + const LinkLocalServiceInfo& info = LinkLocalServiceInfo()); + void unregisterService(); + bool isRegistered() const; + + std::vector<LinkLocalService> getServices() const; + + // FIXME: Ugly that we need this + boost::shared_ptr<DNSSDQuerier> getQuerier() const { + return querier; + } + + boost::signal<void (const LinkLocalService&)> onServiceAdded; + boost::signal<void (const LinkLocalService&)> onServiceChanged; + boost::signal<void (const LinkLocalService&)> onServiceRemoved; + boost::signal<void (const DNSSDServiceID&)> onServiceRegistered; + boost::signal<void (bool)> onStopped; + + private: + void handleServiceAdded(const DNSSDServiceID&); + void handleServiceRemoved(const DNSSDServiceID&); + void handleServiceResolved(const DNSSDServiceID& service, const boost::optional<DNSSDResolveServiceQuery::Result>& result); + void handleRegisterFinished(const boost::optional<DNSSDServiceID>&); + void handleBrowseError(); + + private: + boost::shared_ptr<DNSSDQuerier> querier; + boost::optional<DNSSDServiceID> selfService; + boost::shared_ptr<DNSSDBrowseQuery> browseQuery; + boost::shared_ptr<DNSSDRegisterQuery> registerQuery; + typedef std::map<DNSSDServiceID, boost::shared_ptr<DNSSDResolveServiceQuery> > ResolveQueryMap; + ResolveQueryMap resolveQueries; + typedef std::map<DNSSDServiceID, DNSSDResolveServiceQuery::Result> ServiceMap; + ServiceMap services; + bool haveError; + }; +} diff --git a/Swiften/LinkLocal/LinkLocalServiceInfo.cpp b/Swiften/LinkLocal/LinkLocalServiceInfo.cpp new file mode 100644 index 0000000..8ee7ae0 --- /dev/null +++ b/Swiften/LinkLocal/LinkLocalServiceInfo.cpp @@ -0,0 +1,114 @@ +#include "Swiften/LinkLocal/LinkLocalServiceInfo.h" + +#include <boost/lexical_cast.hpp> + +namespace Swift { + +ByteArray LinkLocalServiceInfo::toTXTRecord() const { + ByteArray result(getEncoded("txtvers=1")); + if (!firstName.isEmpty()) { + result += getEncoded("1st=" + firstName); + } + if (!lastName.isEmpty()) { + result += getEncoded("last=" + lastName); + } + if (!email.isEmpty()) { + result += getEncoded("email=" + email); + } + if (jid.isValid()) { + result += getEncoded("jid=" + jid.toString()); + } + if (!message.isEmpty()) { + result += getEncoded("msg=" + message); + } + if (!nick.isEmpty()) { + result += getEncoded("nick=" + nick); + } + if (port) { + result += getEncoded("port.p2pj=" + String(boost::lexical_cast<std::string>(*port))); + } + + switch (status) { + case Available: result += getEncoded("status=avail"); break; + case Away: result += getEncoded("status=away"); break; + case DND: result += getEncoded("status=dnd"); break; + } + + return result; +} + +ByteArray LinkLocalServiceInfo::getEncoded(const String& s) { + ByteArray sizeByte; + sizeByte.resize(1); + assert(s.getLength() < 256); + sizeByte[0] = s.getUTF8Size(); + return sizeByte + ByteArray(s); +} + +LinkLocalServiceInfo LinkLocalServiceInfo::createFromTXTRecord(const ByteArray& record) { + LinkLocalServiceInfo info; + size_t i = 0; + while (i < record.getSize()) { + std::pair<String,String> entry = readEntry(record, &i); + if (entry.first.isEmpty()) { + break; + } + else if (entry.first == "1st") { + info.setFirstName(entry.second); + } + else if (entry.first == "last") { + info.setLastName(entry.second); + } + else if (entry.first == "email") { + info.setEMail(entry.second); + } + else if (entry.first == "jid") { + info.setJID(JID(entry.second)); + } + else if (entry.first == "msg") { + info.setMessage(entry.second); + } + else if (entry.first == "nick") { + info.setNick(entry.second); + } + else if (entry.first == "port.p2pj") { + info.setPort(boost::lexical_cast<int>(entry.second)); + } + else if (entry.first == "status") { + if (entry.second == "away") { + info.setStatus(Away); + } + else if (entry.second == "dnd") { + info.setStatus(DND); + } + } + } + return info; +} + +std::pair<String,String> LinkLocalServiceInfo::readEntry(const ByteArray& record, size_t* index) { + size_t& i = *index; + String key; + String value; + + size_t entryEnd = i + 1 + record[i]; + ++i; + bool inKey = true; + while (i < entryEnd && i < record.getSize()) { + if (inKey) { + if (record[i] == '=') { + inKey = false; + } + else { + key += record[i]; + } + } + else { + value += record[i]; + } + ++i; + } + return std::make_pair(key, value); +} + +} diff --git a/Swiften/LinkLocal/LinkLocalServiceInfo.h b/Swiften/LinkLocal/LinkLocalServiceInfo.h new file mode 100644 index 0000000..d78b70c --- /dev/null +++ b/Swiften/LinkLocal/LinkLocalServiceInfo.h @@ -0,0 +1,59 @@ +#pragma once + +#include <boost/optional.hpp> + +#include "Swiften/Base/ByteArray.h" +#include "Swiften/Base/String.h" +#include "Swiften/JID/JID.h" + +namespace Swift { + + class LinkLocalServiceInfo { + public: + enum Status { Available, Away, DND }; + + LinkLocalServiceInfo() : status(Available) {} + + const String& getFirstName() const { return firstName; } + void setFirstName(const String& f) { firstName = f; } + + const String& getLastName() const { return lastName; } + void setLastName(const String& l) { lastName = l; } + + const String& getEMail() const { return email; } + void setEMail(const String& e) { email = e; } + + const JID& getJID() const { return jid; } + void setJID(const JID& j) { jid = j; } + + const String& getMessage() const { return message; } + void setMessage(const String& m) { message = m; } + + const String& getNick() const { return nick; } + void setNick(const String& n) { nick = n; } + + Status getStatus() const { return status; } + void setStatus(Status s) { status = s; } + + boost::optional<int> getPort() const { return port; } + void setPort(int p) { port = p; } + + ByteArray toTXTRecord() const; + + static LinkLocalServiceInfo createFromTXTRecord(const ByteArray& record); + + private: + static ByteArray getEncoded(const String&); + static std::pair<String,String> readEntry(const ByteArray&, size_t*); + + private: + String firstName; + String lastName; + String email; + JID jid; + String message; + String nick; + Status status; + boost::optional<int> port; + }; +} diff --git a/Swiften/LinkLocal/OutgoingLinkLocalSession.cpp b/Swiften/LinkLocal/OutgoingLinkLocalSession.cpp new file mode 100644 index 0000000..7b71f82 --- /dev/null +++ b/Swiften/LinkLocal/OutgoingLinkLocalSession.cpp @@ -0,0 +1,45 @@ +#include "Swiften/LinkLocal/OutgoingLinkLocalSession.h" + +#include <boost/bind.hpp> + +#include "Swiften/StreamStack/XMPPLayer.h" +#include "Swiften/Elements/ProtocolHeader.h" +#include "Swiften/Elements/StreamFeatures.h" +#include "Swiften/Elements/IQ.h" + +namespace Swift { + +OutgoingLinkLocalSession::OutgoingLinkLocalSession( + const JID& localJID, + const JID& remoteJID, + boost::shared_ptr<Connection> connection, + PayloadParserFactoryCollection* payloadParserFactories, + PayloadSerializerCollection* payloadSerializers) : + Session(connection, payloadParserFactories, payloadSerializers) { + setLocalJID(localJID); + setRemoteJID(remoteJID); +} + +void OutgoingLinkLocalSession::handleSessionStarted() { + ProtocolHeader header; + header.setFrom(getLocalJID()); + getXMPPLayer()->writeHeader(header); +} + +void OutgoingLinkLocalSession::handleStreamStart(const ProtocolHeader&) { + foreach(const boost::shared_ptr<Element>& stanza, queuedElements_) { + sendElement(stanza); + } + queuedElements_.clear(); +} + +void OutgoingLinkLocalSession::handleElement(boost::shared_ptr<Element> element) { + onElementReceived(element); +} + +void OutgoingLinkLocalSession::queueElement(boost::shared_ptr<Element> element) { + queuedElements_.push_back(element); +} + + +} diff --git a/Swiften/LinkLocal/OutgoingLinkLocalSession.h b/Swiften/LinkLocal/OutgoingLinkLocalSession.h new file mode 100644 index 0000000..32f78e4 --- /dev/null +++ b/Swiften/LinkLocal/OutgoingLinkLocalSession.h @@ -0,0 +1,37 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/signal.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <vector> + +#include "Swiften/Session/Session.h" +#include "Swiften/JID/JID.h" + +namespace Swift { + class ConnectionFactory; + class String; + class Element; + class PayloadParserFactoryCollection; + class PayloadSerializerCollection; + + class OutgoingLinkLocalSession : public Session { + public: + OutgoingLinkLocalSession( + const JID& localJID, + const JID& remoteJID, + boost::shared_ptr<Connection> connection, + PayloadParserFactoryCollection* payloadParserFactories, + PayloadSerializerCollection* payloadSerializers); + + void queueElement(boost::shared_ptr<Element> element); + + private: + void handleSessionStarted(); + void handleElement(boost::shared_ptr<Element>); + void handleStreamStart(const ProtocolHeader&); + + private: + std::vector<boost::shared_ptr<Element> > queuedElements_; + }; +} diff --git a/Swiften/LinkLocal/SConscript b/Swiften/LinkLocal/SConscript new file mode 100644 index 0000000..b929db1 --- /dev/null +++ b/Swiften/LinkLocal/SConscript @@ -0,0 +1,37 @@ +Import("swiften_env") + +myenv = swiften_env.Clone() +myenv.MergeFlags(swiften_env.get("BONJOUR_FLAGS", "")) + +sources = [ + "DNSSD/DNSSDBrowseQuery.cpp", + "DNSSD/DNSSDQuerier.cpp", + "DNSSD/DNSSDRegisterQuery.cpp", + "DNSSD/DNSSDResolveHostnameQuery.cpp", + "DNSSD/DNSSDResolveServiceQuery.cpp", + "DNSSD/DNSSDServiceID.cpp", + "DNSSD/Fake/FakeDNSSDQuerier.cpp", + "DNSSD/Fake/FakeDNSSDQuery.cpp", + "DNSSD/PlatformDNSSDQuerierFactory.cpp", + "IncomingLinkLocalSession.cpp", + "LinkLocalConnector.cpp", + "LinkLocalService.cpp", + "LinkLocalServiceBrowser.cpp", + "LinkLocalServiceInfo.cpp", + "OutgoingLinkLocalSession.cpp", + ] + +if myenv.get("HAVE_BONJOUR", 0) : + myenv.Append(CPPDEFINES = "HAVE_BONJOUR") + sources += [ + "DNSSD/Bonjour/BonjourQuerier.cpp", + "DNSSD/Bonjour/BonjourQuery.cpp", + ] +elif myenv.get("HAVE_AVAHI", 0) : + sources += [ + "DNSSD/Avahi/AvahiQuerier.cpp", + "DNSSD/Avahi/AvahiQuery.cpp" + ] + +objects = myenv.StaticObject(sources) +swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp b/Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp new file mode 100644 index 0000000..ee5e414 --- /dev/null +++ b/Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp @@ -0,0 +1,135 @@ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/LinkLocal/LinkLocalConnector.h" +#include "Swiften/LinkLocal/LinkLocalService.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h" +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h" +#include "Swiften/EventLoop/DummyEventLoop.h" +#include "Swiften/Network/FakeConnection.h" + +using namespace Swift; + +class LinkLocalConnectorTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(LinkLocalConnectorTest); + CPPUNIT_TEST(testConnect); + CPPUNIT_TEST(testConnect_UnableToResolve); + CPPUNIT_TEST(testConnect_UnableToConnect); + CPPUNIT_TEST(testCancel_DuringResolve); + CPPUNIT_TEST(testCancel_DuringConnect); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + eventLoop = new DummyEventLoop(); + querier = boost::shared_ptr<FakeDNSSDQuerier>( + new FakeDNSSDQuerier("rabbithole.local")); + connection = boost::shared_ptr<FakeConnection>(new FakeConnection()); + connectFinished = false; + } + + void tearDown() { + delete eventLoop; + } + + void testConnect() { + boost::shared_ptr<LinkLocalConnector> + testling(createConnector("rabbithole.local", 1234)); + querier->setAddress("rabbithole.local", HostAddress("192.168.1.1")); + + testling->connect(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(connectFinished); + CPPUNIT_ASSERT(!connectError); + CPPUNIT_ASSERT(connection->connectedTo); + CPPUNIT_ASSERT_EQUAL(String(connection->connectedTo->getAddress().toString()), String("192.168.1.1")); + CPPUNIT_ASSERT_EQUAL(connection->connectedTo->getPort(), 1234); + } + + void testConnect_UnableToResolve() { + boost::shared_ptr<LinkLocalConnector> + testling(createConnector("rabbithole.local", 1234)); + querier->setAddress("rabbithole.local", boost::optional<HostAddress>()); + + testling->connect(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(connectFinished); + CPPUNIT_ASSERT(connectError); + CPPUNIT_ASSERT(!connection->connectedTo); + } + + void testConnect_UnableToConnect() { + boost::shared_ptr<LinkLocalConnector> + testling(createConnector("rabbithole.local", 1234)); + querier->setAddress("rabbithole.local", HostAddress("192.168.1.1")); + connection->setError(Connection::ReadError); + + testling->connect(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(connectFinished); + CPPUNIT_ASSERT(connectError); + CPPUNIT_ASSERT(!connection->connectedTo); + } + + void testCancel_DuringResolve() { + boost::shared_ptr<LinkLocalConnector> + testling(createConnector("rabbithole.local", 1234)); + testling->connect(); + eventLoop->processEvents(); + CPPUNIT_ASSERT(!connectFinished); + + testling->cancel(); + eventLoop->processEvents(); + querier->setAddress("rabbithole.local", HostAddress("192.168.1.1")); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(FakeConnection::Disconnected == connection->state); + } + + void testCancel_DuringConnect() { + boost::shared_ptr<LinkLocalConnector> + testling(createConnector("rabbithole.local", 1234)); + querier->setAddress("rabbithole.local", HostAddress("192.168.1.1")); + connection->setDelayConnect(); + testling->connect(); + eventLoop->processEvents(); + CPPUNIT_ASSERT(FakeConnection::Connecting == connection->state); + + testling->cancel(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(FakeConnection::Disconnected == connection->state); + } + + private: + boost::shared_ptr<LinkLocalConnector> createConnector(const String& hostname, int port) { + LinkLocalService service( + DNSSDServiceID("myname", "local."), + DNSSDResolveServiceQuery::Result( + "myname._presence._tcp.local", hostname, port, + LinkLocalServiceInfo().toTXTRecord())); + boost::shared_ptr<LinkLocalConnector> result( + new LinkLocalConnector(service, querier, connection)); + result->onConnectFinished.connect( + boost::bind(&LinkLocalConnectorTest::handleConnected, this, _1)); + return result; + } + + void handleConnected(bool e) { + connectFinished = true; + connectError = e; + } + + private: + DummyEventLoop* eventLoop; + boost::shared_ptr<FakeDNSSDQuerier> querier; + boost::shared_ptr<FakeConnection> connection; + bool connectFinished; + bool connectError; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalConnectorTest); diff --git a/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp b/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp new file mode 100644 index 0000000..9f91269 --- /dev/null +++ b/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp @@ -0,0 +1,379 @@ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <boost/bind.hpp> +#include <map> + +#include "Swiften/LinkLocal/LinkLocalServiceBrowser.h" +#include "Swiften/LinkLocal/LinkLocalService.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" +#include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h" +#include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h" +#include "Swiften/EventLoop/DummyEventLoop.h" + +using namespace Swift; + +class LinkLocalServiceBrowserTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(LinkLocalServiceBrowserTest); + CPPUNIT_TEST(testConstructor); + CPPUNIT_TEST(testStart); + CPPUNIT_TEST(testServiceAdded); + CPPUNIT_TEST(testServiceAdded_NoServiceInfo); + CPPUNIT_TEST(testServiceAdded_RegisteredService); + CPPUNIT_TEST(testServiceAdded_UnregisteredService); + CPPUNIT_TEST(testServiceChanged); + CPPUNIT_TEST(testServiceRemoved); + CPPUNIT_TEST(testServiceRemoved_UnregisteredService); + CPPUNIT_TEST(testError_BrowseErrorAfterStart); + CPPUNIT_TEST(testError_BrowseErrorAfterResolve); + CPPUNIT_TEST(testRegisterService); + CPPUNIT_TEST(testRegisterService_Error); + CPPUNIT_TEST(testRegisterService_Reregister); + CPPUNIT_TEST(testUpdateService); + CPPUNIT_TEST_SUITE_END(); + + public: + LinkLocalServiceBrowserTest() {} + + void setUp() { + eventLoop = new DummyEventLoop(); + querier = boost::shared_ptr<FakeDNSSDQuerier>(new FakeDNSSDQuerier("wonderland.lit")); + aliceServiceID = new DNSSDServiceID("alice", "wonderland.lit"); + aliceServiceInfo = new DNSSDResolveServiceQuery::Result("_presence._tcp.wonderland.lit", "xmpp.wonderland.lit", 1234, LinkLocalServiceInfo().toTXTRecord()); + testServiceID = new DNSSDServiceID("foo", "bar.local"); + testServiceInfo = new DNSSDResolveServiceQuery::Result("_presence._tcp.bar.local", "xmpp.bar.local", 1234, LinkLocalServiceInfo().toTXTRecord()); + testServiceInfo2 = new DNSSDResolveServiceQuery::Result("_presence.tcp.bar.local", "xmpp.foo.local", 2345, LinkLocalServiceInfo().toTXTRecord()); + errorStopReceived = false; + normalStopReceived = false; + } + + void tearDown() { + addedServices.clear(); + removedServices.clear(); + changedServices.clear(); + + delete aliceServiceID; + delete aliceServiceInfo; + delete testServiceInfo2; + delete testServiceInfo; + delete testServiceID; + delete eventLoop; + } + + void testConstructor() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + + CPPUNIT_ASSERT(!testling->isRunning()); + CPPUNIT_ASSERT(!testling->hasError()); + } + + void testStart() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + + CPPUNIT_ASSERT(testling->isRunning()); + CPPUNIT_ASSERT(!testling->hasError()); + + testling->stop(); + } + + void testServiceAdded() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + eventLoop->processEvents(); + + querier->setServiceInfo(*testServiceID,*testServiceInfo); + querier->addService(*testServiceID); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size())); + CPPUNIT_ASSERT(addedServices[0].getID() == *testServiceID); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changedServices.size())); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(removedServices.size())); + std::vector<LinkLocalService> services = testling->getServices(); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(services.size())); + CPPUNIT_ASSERT(*testServiceID == services[0].getID()); + CPPUNIT_ASSERT(testServiceInfo->port == services[0].getPort()); + CPPUNIT_ASSERT(testServiceInfo->host == services[0].getHostname()); + + testling->stop(); + } + + void testServiceAdded_NoServiceInfo() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + eventLoop->processEvents(); + + querier->addService(*testServiceID); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(addedServices.size())); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling->getServices().size())); + + testling->stop(); + } + + void testServiceAdded_RegisteredService() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + eventLoop->processEvents(); + + testling->registerService("alice", 1234, LinkLocalServiceInfo()); + eventLoop->processEvents(); + querier->setServiceInfo(*aliceServiceID, *aliceServiceInfo); + querier->addService(*aliceServiceID); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(addedServices.size())); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling->getServices().size())); + + testling->stop(); + } + + void testServiceAdded_UnregisteredService() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + eventLoop->processEvents(); + testling->registerService("alice", 1234, LinkLocalServiceInfo()); + eventLoop->processEvents(); + testling->unregisterService(); + eventLoop->processEvents(); + + querier->setServiceInfo(*aliceServiceID, *aliceServiceInfo); + querier->addService(*aliceServiceID); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size())); + CPPUNIT_ASSERT(addedServices[0].getID() == *aliceServiceID); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changedServices.size())); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(removedServices.size())); + std::vector<LinkLocalService> services = testling->getServices(); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(services.size())); + CPPUNIT_ASSERT(*aliceServiceID == services[0].getID()); + CPPUNIT_ASSERT(aliceServiceInfo->port == services[0].getPort()); + CPPUNIT_ASSERT(aliceServiceInfo->host == services[0].getHostname()); + + testling->stop(); + } + + void testServiceRemoved_UnregisteredService() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + eventLoop->processEvents(); + testling->registerService("alice", 1234, LinkLocalServiceInfo()); + eventLoop->processEvents(); + testling->unregisterService(); + eventLoop->processEvents(); + + querier->removeService(*aliceServiceID); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(removedServices.size())); + + testling->stop(); + } + + void testServiceChanged() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + querier->setServiceInfo(*testServiceID,*testServiceInfo); + querier->addService(*testServiceID); + eventLoop->processEvents(); + + querier->setServiceInfo(*testServiceID,*testServiceInfo2); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size())); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changedServices.size())); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(removedServices.size())); + CPPUNIT_ASSERT(changedServices[0].getID() == *testServiceID); + std::vector<LinkLocalService> services = testling->getServices(); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(services.size())); + CPPUNIT_ASSERT(*testServiceID == services[0].getID()); + CPPUNIT_ASSERT(testServiceInfo2->port == services[0].getPort()); + CPPUNIT_ASSERT(testServiceInfo2->host == services[0].getHostname()); + + testling->stop(); + } + + void testServiceRemoved() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + querier->setServiceInfo(*testServiceID,*testServiceInfo); + querier->addService(*testServiceID); + eventLoop->processEvents(); + + querier->removeService(*testServiceID); + eventLoop->processEvents(); + querier->setServiceInfo(*testServiceID,*testServiceInfo2); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(addedServices.size())); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changedServices.size())); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(removedServices.size())); + CPPUNIT_ASSERT(removedServices[0].getID() == *testServiceID); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling->getServices().size())); + + testling->stop(); + } + + void testError_BrowseErrorAfterStart() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + + querier->setBrowseError(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(!testling->isRunning()); + CPPUNIT_ASSERT(testling->hasError()); + CPPUNIT_ASSERT(errorStopReceived); + } + + void testError_BrowseErrorAfterResolve() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + querier->setServiceInfo(*testServiceID,*testServiceInfo); + querier->addService(*testServiceID); + eventLoop->processEvents(); + + querier->setBrowseError(); + eventLoop->processEvents(); + querier->setServiceInfo(*testServiceID,*testServiceInfo2); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(!testling->isRunning()); + CPPUNIT_ASSERT(testling->hasError()); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling->getServices().size())); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changedServices.size())); + CPPUNIT_ASSERT(errorStopReceived); + } + + void testRegisterService() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + eventLoop->processEvents(); + + LinkLocalServiceInfo info; + info.setFirstName("Foo"); + testling->registerService("foo@bar", 1234, info); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(querier->isServiceRegistered("foo@bar", 1234, info.toTXTRecord())); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(registeredServices.size())); + CPPUNIT_ASSERT(registeredServices[0] == DNSSDServiceID("foo@bar", "wonderland.lit")); + testling->stop(); + } + + void testRegisterService_Error() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + LinkLocalServiceInfo info; + testling->registerService("foo@bar", 1234, info); + eventLoop->processEvents(); + + querier->setRegisterError(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(!testling->isRunning()); + CPPUNIT_ASSERT(testling->hasError()); + CPPUNIT_ASSERT(errorStopReceived); + CPPUNIT_ASSERT(!querier->isServiceRegistered("foo@bar", 1234, info.toTXTRecord())); + } + + void testRegisterService_Reregister() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + eventLoop->processEvents(); + LinkLocalServiceInfo info; + info.setFirstName("Foo"); + testling->registerService("foo@bar", 1234, info); + eventLoop->processEvents(); + testling->unregisterService(); + eventLoop->processEvents(); + + info.setFirstName("Bar"); + testling->registerService("bar@baz", 3456, info); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(querier->isServiceRegistered("bar@baz", 3456, info.toTXTRecord())); + + testling->stop(); + } + + void testUpdateService() { + boost::shared_ptr<LinkLocalServiceBrowser> testling = createTestling(); + testling->start(); + eventLoop->processEvents(); + + LinkLocalServiceInfo info; + info.setFirstName("Foo"); + testling->registerService("foo@bar", 1234, info); + eventLoop->processEvents(); + info.setFirstName("Bar"); + testling->updateService(info); + + CPPUNIT_ASSERT(querier->isServiceRegistered("foo@bar", 1234, info.toTXTRecord())); + + testling->stop(); + } + + private: + boost::shared_ptr<LinkLocalServiceBrowser> createTestling() { + boost::shared_ptr<LinkLocalServiceBrowser> testling( + new LinkLocalServiceBrowser(querier)); + testling->onServiceAdded.connect(boost::bind( + &LinkLocalServiceBrowserTest::handleServiceAdded, this, _1)); + testling->onServiceChanged.connect(boost::bind( + &LinkLocalServiceBrowserTest::handleServiceChanged, this, _1)); + testling->onServiceRemoved.connect(boost::bind( + &LinkLocalServiceBrowserTest::handleServiceRemoved, this, _1)); + testling->onServiceRegistered.connect(boost::bind( + &LinkLocalServiceBrowserTest::handleServiceRegistered, this, _1)); + testling->onStopped.connect(boost::bind( + &LinkLocalServiceBrowserTest::handleStopped, this, _1)); + return testling; + } + + void handleServiceAdded(const LinkLocalService& service) { + addedServices.push_back(service); + } + + void handleServiceRemoved(const LinkLocalService& service) { + removedServices.push_back(service); + } + + void handleServiceChanged(const LinkLocalService& service) { + changedServices.push_back(service); + } + + void handleServiceRegistered(const DNSSDServiceID& service) { + registeredServices.push_back(service); + } + + void handleStopped(bool error) { + CPPUNIT_ASSERT(!errorStopReceived); + CPPUNIT_ASSERT(!normalStopReceived); + if (error) { + errorStopReceived = true; + } + else { + normalStopReceived = true; + } + } + + private: + DummyEventLoop* eventLoop; + boost::shared_ptr<FakeDNSSDQuerier> querier; + std::vector<LinkLocalService> addedServices; + std::vector<LinkLocalService> changedServices; + std::vector<LinkLocalService> removedServices; + std::vector<DNSSDServiceID> registeredServices; + DNSSDServiceID* aliceServiceID; + DNSSDResolveServiceQuery::Result* aliceServiceInfo; + DNSSDServiceID* testServiceID; + DNSSDResolveServiceQuery::Result* testServiceInfo; + DNSSDResolveServiceQuery::Result* testServiceInfo2; + bool errorStopReceived; + bool normalStopReceived; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalServiceBrowserTest); diff --git a/Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp b/Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp new file mode 100644 index 0000000..b5d7ef5 --- /dev/null +++ b/Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp @@ -0,0 +1,63 @@ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/LinkLocal/LinkLocalServiceInfo.h" + +using namespace Swift; + +class LinkLocalServiceInfoTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(LinkLocalServiceInfoTest); + CPPUNIT_TEST(testGetTXTRecord); + CPPUNIT_TEST(testCreateFromTXTRecord); + CPPUNIT_TEST(testCreateFromTXTRecord_InvalidSize); + CPPUNIT_TEST(testGetTXTRecordCreateFromTXTRecord_RoundTrip); + CPPUNIT_TEST_SUITE_END(); + + public: + void testGetTXTRecord() { + LinkLocalServiceInfo info; + info.setFirstName("Remko"); + info.setLastName("Tron\xc3\xe7on"); + info.setStatus(LinkLocalServiceInfo::Away); + + CPPUNIT_ASSERT_EQUAL(ByteArray("\x09txtvers=1\x09" + String("1st=Remko\x0dlast=Tron\xc3\xe7on\x0bstatus=away")), info.toTXTRecord()); + } + + void testCreateFromTXTRecord() { + LinkLocalServiceInfo info = LinkLocalServiceInfo::createFromTXTRecord(ByteArray("\x09txtvers=1\x09" + String("1st=Remko\x0dlast=Tron\xc3\xe7on\x0bstatus=away"))); + + CPPUNIT_ASSERT_EQUAL(String("Remko"), info.getFirstName()); + CPPUNIT_ASSERT_EQUAL(String("Tron\xc3\xe7on"), info.getLastName()); + CPPUNIT_ASSERT_EQUAL(LinkLocalServiceInfo::Away, info.getStatus()); + } + + void testCreateFromTXTRecord_InvalidSize() { + LinkLocalServiceInfo info = LinkLocalServiceInfo::createFromTXTRecord(ByteArray("\x10last=a")); + + CPPUNIT_ASSERT_EQUAL(String("a"), info.getLastName()); + } + + void testGetTXTRecordCreateFromTXTRecord_RoundTrip() { + LinkLocalServiceInfo info; + info.setFirstName("Remko"); + info.setLastName("Tron\xc3\xe7on"); + info.setEMail("remko-email@swift.im"); + info.setJID(JID("remko-jid@swift.im")); + info.setMessage("I'm busy"); + info.setNick("el-tramo"); + info.setStatus(LinkLocalServiceInfo::DND); + info.setPort(1234); + + LinkLocalServiceInfo info2 = LinkLocalServiceInfo::createFromTXTRecord(info.toTXTRecord()); + CPPUNIT_ASSERT_EQUAL(info.getFirstName(), info2.getFirstName()); + CPPUNIT_ASSERT_EQUAL(info.getLastName(), info2.getLastName()); + CPPUNIT_ASSERT_EQUAL(info.getEMail(), info2.getEMail()); + CPPUNIT_ASSERT_EQUAL(info.getJID(), info2.getJID()); + CPPUNIT_ASSERT_EQUAL(info.getMessage(), info2.getMessage()); + CPPUNIT_ASSERT_EQUAL(info.getNick(), info2.getNick()); + CPPUNIT_ASSERT(info.getStatus() == info2.getStatus()); + CPPUNIT_ASSERT(info.getPort() == info2.getPort()); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalServiceInfoTest); diff --git a/Swiften/LinkLocal/UnitTest/LinkLocalServiceTest.cpp b/Swiften/LinkLocal/UnitTest/LinkLocalServiceTest.cpp new file mode 100644 index 0000000..69ec718 --- /dev/null +++ b/Swiften/LinkLocal/UnitTest/LinkLocalServiceTest.cpp @@ -0,0 +1,62 @@ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/LinkLocal/LinkLocalService.h" + +using namespace Swift; + +class LinkLocalServiceTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(LinkLocalServiceTest); + CPPUNIT_TEST(testGetDescription_WithNick); + CPPUNIT_TEST(testGetDescription_WithFirstName); + CPPUNIT_TEST(testGetDescription_WithLastName); + CPPUNIT_TEST(testGetDescription_WithFirstAndLastName); + CPPUNIT_TEST(testGetDescription_NoInfo); + CPPUNIT_TEST_SUITE_END(); + + public: + void testGetDescription_WithNick() { + LinkLocalService testling = createService("alice@wonderland", "Alice", "Alice In", "Wonderland"); + + CPPUNIT_ASSERT_EQUAL(String("Alice"), testling.getDescription()); + } + + void testGetDescription_WithFirstName() { + LinkLocalService testling = createService("alice@wonderland", "", "Alice In"); + + CPPUNIT_ASSERT_EQUAL(String("Alice In"), testling.getDescription()); + } + + void testGetDescription_WithLastName() { + LinkLocalService testling = createService("alice@wonderland", "", "", "Wonderland"); + + CPPUNIT_ASSERT_EQUAL(String("Wonderland"), testling.getDescription()); + } + + void testGetDescription_WithFirstAndLastName() { + LinkLocalService testling = createService("alice@wonderland", "", "Alice In", "Wonderland"); + + CPPUNIT_ASSERT_EQUAL(String("Alice In Wonderland"), testling.getDescription()); + } + + void testGetDescription_NoInfo() { + LinkLocalService testling = createService("alice@wonderland"); + + CPPUNIT_ASSERT_EQUAL(String("alice@wonderland"), testling.getDescription()); + } + + private: + LinkLocalService createService(const String& name, const String& nickName = String(), const String& firstName = String(), const String& lastName = String()) { + DNSSDServiceID service(name, "local."); + LinkLocalServiceInfo info; + info.setFirstName(firstName); + info.setLastName(lastName); + info.setNick(nickName); + return LinkLocalService(service, + DNSSDResolveServiceQuery::Result( + name + "._presence._tcp.local", "rabbithole.local", 1234, + info.toTXTRecord())); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalServiceTest); |