diff options
63 files changed, 1498 insertions, 355 deletions
diff --git a/Slimber/Cocoa/CocoaAction.h b/Slimber/Cocoa/CocoaAction.h new file mode 100644 index 0000000..d02c8b5 --- /dev/null +++ b/Slimber/Cocoa/CocoaAction.h @@ -0,0 +1,20 @@ +#pragma once + +#include <Cocoa/Cocoa.h> +#include <boost/function.hpp> + +@interface CocoaAction : NSObject { + boost::function<void ()>* function; +} + +/** + * Acquires ownership of 'f'. + */ +- (id) initWithFunction: (boost::function<void()>*) f; + +/** + * Calls the functor passed as a parameter to the contsructor. + */ +- (void) doAction: (id) sender; + +@end diff --git a/Slimber/Cocoa/CocoaAction.mm b/Slimber/Cocoa/CocoaAction.mm new file mode 100644 index 0000000..15498a1 --- /dev/null +++ b/Slimber/Cocoa/CocoaAction.mm @@ -0,0 +1,22 @@ +#include "Slimber/Cocoa/CocoaAction.h" + +@implementation CocoaAction + +- (id) initWithFunction: (boost::function<void()>*) f { + if ([super init]) { + function = f; + } + return self; +} + +- (void) dealloc { + delete function; + [super dealloc]; +} + +- (void) doAction: (id) sender { + (void) sender; + (*function)(); +} + +@end diff --git a/Slimber/Cocoa/CocoaController.h b/Slimber/Cocoa/CocoaController.h new file mode 100644 index 0000000..f4be87d --- /dev/null +++ b/Slimber/Cocoa/CocoaController.h @@ -0,0 +1,11 @@ +#include <Cocoa/Cocoa.h> + +class MainController; +class CocoaMenulet; + +@interface CocoaController : NSObject { + CocoaMenulet* menulet; + MainController* main; +} + +@end diff --git a/Slimber/Cocoa/CocoaController.mm b/Slimber/Cocoa/CocoaController.mm new file mode 100644 index 0000000..437d85a --- /dev/null +++ b/Slimber/Cocoa/CocoaController.mm @@ -0,0 +1,19 @@ +#include "Slimber/Cocoa/CocoaController.h" + +#include "Slimber/MainController.h" +#include "Slimber/Cocoa/CocoaMenulet.h" + +@implementation CocoaController + +- (void) dealloc { + delete main; + delete menulet; + [super dealloc]; +} + +- (void) awakeFromNib { + menulet = new CocoaMenulet(); + main = new MainController(menulet); +} + +@end diff --git a/Slimber/Cocoa/CocoaMenulet.h b/Slimber/Cocoa/CocoaMenulet.h new file mode 100644 index 0000000..913731f --- /dev/null +++ b/Slimber/Cocoa/CocoaMenulet.h @@ -0,0 +1,26 @@ +#pragma once + +#include <Cocoa/Cocoa.h> + +#include "Slimber/Menulet.h" +#include "Slimber/Cocoa/CocoaAction.h" + +class CocoaMenulet : public Menulet { + public: + CocoaMenulet(); + ~CocoaMenulet(); + + private: + virtual void clear(); + virtual void addItem(const Swift::String& name, const Swift::String& icon); + virtual void addSeparator(); + void setIcon(const Swift::String& icon); + virtual void addAboutItem(); + virtual void addRestartItem(); + virtual void addExitItem(); + + private: + NSStatusItem* statusItem; + NSMenu* menu; + CocoaAction* restartAction; +}; diff --git a/Slimber/Cocoa/CocoaMenulet.mm b/Slimber/Cocoa/CocoaMenulet.mm new file mode 100644 index 0000000..72ab000 --- /dev/null +++ b/Slimber/Cocoa/CocoaMenulet.mm @@ -0,0 +1,79 @@ +#include "Slimber/Cocoa/CocoaMenulet.h" + +#include <boost/function.hpp> + +using namespace Swift; + +CocoaMenulet::CocoaMenulet() { + restartAction = [[CocoaAction alloc] initWithFunction: + new boost::function<void()>(boost::ref(onRestartClicked))]; + menu = [[NSMenu alloc] init]; + + statusItem = [[[NSStatusBar systemStatusBar] + statusItemWithLength: NSVariableStatusItemLength] retain]; + [statusItem setHighlightMode: YES]; + [statusItem setEnabled: YES]; + [statusItem setToolTip: @"Slimber"]; + [statusItem setMenu: menu]; +} + +CocoaMenulet::~CocoaMenulet() { + [statusItem release]; + [menu release]; + [restartAction release]; +} + +void CocoaMenulet::setIcon(const String& icon) { + NSString* path = [[NSBundle mainBundle] pathForResource: + [NSString stringWithUTF8String: icon.getUTF8Data()] ofType:@"png"]; + NSImage* image = [[NSImage alloc] initWithContentsOfFile: path]; + [statusItem setImage: image]; + [image release]; +} + +void CocoaMenulet::clear() { + while ([menu numberOfItems] > 0) { + [menu removeItemAtIndex: 0]; + } +} + +void CocoaMenulet::addItem(const Swift::String& name, const String& icon) { + NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: + [NSString stringWithUTF8String: name.getUTF8Data()] + action: NULL keyEquivalent: @""]; + if (!icon.isEmpty()) { + NSString* path = [[NSBundle mainBundle] pathForResource: + [NSString stringWithUTF8String: icon.getUTF8Data()] ofType:@"png"]; + NSImage* image = [[NSImage alloc] initWithContentsOfFile: path]; + [item setImage: [[NSImage alloc] initWithContentsOfFile: path]]; + [image release]; + } + [menu addItem: item]; + [item release]; +} + +void CocoaMenulet::addAboutItem() { + NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"About Slimber" action: @selector(orderFrontStandardAboutPanel:) keyEquivalent: @""]; + [item setTarget: [NSApplication sharedApplication]]; + [menu addItem: item]; + [item release]; +} + +void CocoaMenulet::addRestartItem() { + NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: + @"Restart" action: @selector(doAction:) keyEquivalent: @""]; + [item setTarget: restartAction]; + [menu addItem: item]; + [item release]; +} + +void CocoaMenulet::addExitItem() { + NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"Exit" action: @selector(terminate:) keyEquivalent: @""]; + [item setTarget: [NSApplication sharedApplication]]; + [menu addItem: item]; + [item release]; +} + +void CocoaMenulet::addSeparator() { + [menu addItem: [NSMenuItem separatorItem]]; +} diff --git a/Slimber/Cocoa/Info.plist b/Slimber/Cocoa/Info.plist index f934a10..d954dba 100644 --- a/Slimber/Cocoa/Info.plist +++ b/Slimber/Cocoa/Info.plist @@ -18,6 +18,8 @@ <string>????</string> <key>CFBundleVersion</key> <string>1.0</string> + <key>CFBundleIconFile</key> + <string>Slimber</string> <key>NSMainNibFile</key> <string>MainMenu</string> <key>NSPrincipalClass</key> @@ -25,6 +27,7 @@ <key>LSUIElement</key> <string>1</string> <key>NSHumanReadableCopyright</key> - <string>© 2009 Swift Development Team. All Rights Reserved.</string> + <string>© 2009 Swift Development Team. +All Rights Reserved.</string> </dict> </plist> diff --git a/Slimber/Cocoa/MainController.h b/Slimber/Cocoa/MainController.h deleted file mode 100644 index c6e20c5..0000000 --- a/Slimber/Cocoa/MainController.h +++ /dev/null @@ -1,9 +0,0 @@ -#include <Cocoa/Cocoa.h> - -class Slimber; - -@interface MainController : NSObject { - Slimber* slimber; -} - -@end diff --git a/Slimber/Cocoa/MainController.mm b/Slimber/Cocoa/MainController.mm deleted file mode 100644 index 30b7a16..0000000 --- a/Slimber/Cocoa/MainController.mm +++ /dev/null @@ -1,15 +0,0 @@ -#include "MainController.h" -#include "Slimber.h" - -@implementation MainController - -- (void) dealloc { - delete slimber; - [super dealloc]; -} - -- (void) awakeFromNib { - slimber = new Slimber(); -} - -@end diff --git a/Slimber/Cocoa/MainMenu.xib b/Slimber/Cocoa/MainMenu.xib index 50e4ca3..bed7223 100644 --- a/Slimber/Cocoa/MainMenu.xib +++ b/Slimber/Cocoa/MainMenu.xib @@ -180,7 +180,7 @@ <string key="NSName">_NSMainMenu</string> </object> <object class="NSCustomObject" id="16040424"> - <string key="NSClassName">MainController</string> + <string key="NSClassName">CocoaController</string> </object> </object> <object class="IBObjectContainer" key="IBDocument.Objects"> @@ -482,11 +482,11 @@ <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.1+"> <bool key="EncodedWithXMLCoder">YES</bool> <object class="IBPartialClassDescription"> - <string key="className">MainController</string> + <string key="className">CocoaController</string> <string key="superclassName">NSObject</string> <object class="IBClassDescriptionSource" key="sourceIdentifier"> <string key="majorKey">IBDocumentRelativeSource</string> - <string key="minorKey">MainController.h</string> + <string key="minorKey">CocoaController.h</string> </object> </object> </object> diff --git a/Slimber/Cocoa/Makefile.inc b/Slimber/Cocoa/Makefile.inc index 8cd72cd..cdb19b7 100644 --- a/Slimber/Cocoa/Makefile.inc +++ b/Slimber/Cocoa/Makefile.inc @@ -2,13 +2,14 @@ SLIMBER_COCOA_TARGET = Slimber/Cocoa/Slimber.app SLIMBER_COCOA_BINARY = \ Slimber/Cocoa/Slimber SLIMBER_COCOA_SOURCES = \ - Slimber/Cocoa/MainController.mm \ - Slimber/Cocoa/Slimber.mm \ Slimber/Cocoa/main.mm \ - Slimber/Cocoa/Menulet.m + Slimber/Cocoa/CocoaController.mm \ + Slimber/Cocoa/CocoaMenulet.mm \ + Slimber/Cocoa/CocoaAction.mm SLIMBER_COCOA_XIBS = \ Slimber/Cocoa/MainMenu.xib SLIMBER_COCOA_RESOURCES = \ + Slimber/Resources/Slimber.icns \ Slimber/Resources/Credits.html \ Slimber/Resources/Online.png \ Slimber/Resources/Offline.png \ @@ -26,8 +27,8 @@ CLEANFILES += \ $(SLIMBER_COCOA_NIBS) \ $(SLIMBER_COCOA_TARGET) \ $(SLIMBER_COCOA_BINARY) -//DEPS += \ -// $(SLIMBER_COCOA_SOURCES:.m=.dep) \ +DEPS += \ + $(patsubst %.m, %.dep, $(patsubst %.mm, %.dep, $(SLIMBER_COCOA_SOURCES))) .PHONY: slimber-cocoa slimber-cocoa: $(SLIMBER_COCOA_TARGET) @@ -42,5 +43,5 @@ $(SLIMBER_COCOA_TARGET): $(SLIMBER_COCOA_BINARY) $(SLIMBER_COCOA_NIBS) Slimber/C cp $(SLIMBER_COCOA_NIBS) $(SLIMBER_COCOA_TARGET)/Contents/Resources cp $(SLIMBER_COCOA_RESOURCES) $(SLIMBER_COCOA_TARGET)/Contents/Resources -$(SLIMBER_COCOA_BINARY): $(SLIMBER_COCOA_OBJECTS) $(SWIFTEN_TARGET) $(SLIMBER_TRGET) +$(SLIMBER_COCOA_BINARY): $(SLIMBER_COCOA_OBJECTS) $(SWIFTEN_TARGET) $(SLIMBER_TARGET) $(QUIET_LINK)$(CXX) -o $@ $(SLIMBER_COCOA_OBJECTS) $(LDFLAGS) $(SLIMBER_TARGET) $(SWIFTEN_TARGET) $(LIBS) -framework Cocoa diff --git a/Slimber/Cocoa/Menulet.h b/Slimber/Cocoa/Menulet.h deleted file mode 100644 index fabaa80..0000000 --- a/Slimber/Cocoa/Menulet.h +++ /dev/null @@ -1,16 +0,0 @@ -#include <Cocoa/Cocoa.h> - -@interface Menulet : NSObject { - NSStatusItem* statusItem; - NSMenu* statusMenu; - NSImage* menuIcon; - NSArray* userNames; - BOOL selfOnline; -} - -- (id) init; -- (void) updateMenu; -- (void) setUserNames: (NSArray*) names; -- (void) setSelfConnected: (BOOL) online; - -@end diff --git a/Slimber/Cocoa/Menulet.m b/Slimber/Cocoa/Menulet.m deleted file mode 100644 index 1cc1f1f..0000000 --- a/Slimber/Cocoa/Menulet.m +++ /dev/null @@ -1,104 +0,0 @@ -#import "Menulet.h" - -@implementation Menulet - -- (id) init { - if ([super init]) { - statusMenu = [[NSMenu alloc] init]; - - statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength: NSVariableStatusItemLength] retain]; - [statusItem setHighlightMode: YES]; - [statusItem setEnabled: YES]; - [statusItem setToolTip: @"Slimber"]; - [statusItem setMenu: statusMenu]; - - userNames = [[NSArray alloc] init]; - selfOnline = NO; - - [self updateMenu]; - } - return self; -} - -- (void) dealloc { - [statusItem release]; - [menuIcon release]; - [super dealloc]; -} - -- (void) updateIcon: (BOOL) online { - NSBundle* bundle = [NSBundle bundleForClass: [self class]]; - NSString* path; - if (online) { - path = [bundle pathForResource: @"UsersOnline" ofType:@"png"]; - } - else { - path = [bundle pathForResource: @"UsersOffline" ofType:@"png"]; - } - [statusItem setImage: [[NSImage alloc] initWithContentsOfFile: path]]; -} - -- (void) updateMenu { - // Clear the menu - while ([statusMenu numberOfItems] > 0) { - [statusMenu removeItemAtIndex: 0]; - } - - // User items - if ([userNames count] > 0) { - [statusMenu addItem: [[NSMenuItem alloc] initWithTitle: @"Online users:" action: NULL keyEquivalent: @""]]; - int i; - for (i = 0; i < [userNames count]; ++i) { - NSString* text = [NSString stringWithFormat: @" %@", [userNames objectAtIndex: i]]; - NSMenuItem* userItem = [[NSMenuItem alloc] initWithTitle: text action: NULL keyEquivalent: @""]; - [statusMenu addItem: userItem]; - [userItem release]; - } - } - else { - [statusMenu addItem: [[NSMenuItem alloc] initWithTitle: @"No online users" action: NULL keyEquivalent: @""]]; - } - [self updateIcon: [userNames count] > 0]; - [statusMenu addItem: [NSMenuItem separatorItem]]; - - // Self item - NSMenuItem* loggedInItem; - NSBundle* bundle = [NSBundle bundleForClass: [self class]]; - NSString* path; - if (selfOnline) { - loggedInItem = [[NSMenuItem alloc] initWithTitle: @"You are logged in" action: NULL keyEquivalent: @""]; - path = [bundle pathForResource: @"Online" ofType:@"png"]; - } - else { - loggedInItem = [[NSMenuItem alloc] initWithTitle: @"You are not logged in" action: NULL keyEquivalent: @""]; - path = [bundle pathForResource: @"Offline" ofType:@"png"]; - } - [loggedInItem setImage: [[NSImage alloc] initWithContentsOfFile: path]]; - [statusMenu addItem: loggedInItem]; - [statusMenu addItem: [NSMenuItem separatorItem]]; - - // About menu - NSMenuItem* aboutMenuItem = [[NSMenuItem alloc] initWithTitle: @"About Slimber" action: @selector(orderFrontStandardAboutPanel:) keyEquivalent: @""]; - [aboutMenuItem setTarget: [NSApplication sharedApplication]]; - [statusMenu addItem: aboutMenuItem]; - [statusMenu addItem: [NSMenuItem separatorItem]]; - - // Exit item - NSMenuItem* exitMenuItem = [[NSMenuItem alloc] initWithTitle: @"Exit" action: @selector(terminate:) keyEquivalent: @""]; - [exitMenuItem setTarget: [NSApplication sharedApplication]]; - [statusMenu addItem: exitMenuItem]; -} - -- (void) setSelfConnected: (BOOL) online { - selfOnline = online; - [self updateMenu]; -} - -- (void) setUserNames: (NSArray*) names { - [names retain]; - [userNames release]; - userNames = names; - [self updateMenu]; -} - -@end diff --git a/Slimber/Cocoa/Slimber.h b/Slimber/Cocoa/Slimber.h deleted file mode 100644 index b62f9e5..0000000 --- a/Slimber/Cocoa/Slimber.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include <boost/shared_ptr.hpp> - -@class Menulet; -namespace Swift { - class Server; - class VCardCollection; - class LinkLocalServiceBrowser; - class BonjourQuerier; -} - -class Slimber { - public: - Slimber(); - ~Slimber(); - - private: - void handleSelfConnected(bool b); - void handleServicesChanged(); - - private: - boost::shared_ptr<Swift::BonjourQuerier> dnsSDQuerier; - Swift::LinkLocalServiceBrowser* linkLocalServiceBrowser; - Swift::VCardCollection* vCardCollection; - Swift::Server* server; - Menulet* menulet; -}; diff --git a/Slimber/Cocoa/Slimber.mm b/Slimber/Cocoa/Slimber.mm deleted file mode 100644 index d4851c8..0000000 --- a/Slimber/Cocoa/Slimber.mm +++ /dev/null @@ -1,61 +0,0 @@ -#include "Slimber/Cocoa/Slimber.h" - -#include <boost/bind.hpp> - -#include "Swiften/Base/foreach.h" -#include "Swiften/Application/Platform/PlatformApplication.h" -#include "Swiften/LinkLocal/LinkLocalService.h" -#include "Swiften/LinkLocal/LinkLocalServiceBrowser.h" -#include "Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h" -#include "Slimber/Cocoa/Menulet.h" -#include "Slimber/Server.h" -#include "Slimber/FileVCardCollection.h" - -using namespace Swift; - -Slimber::Slimber() { - dnsSDQuerier = boost::shared_ptr<BonjourQuerier>(new BonjourQuerier()); - dnsSDQuerier->start(); - - linkLocalServiceBrowser = new LinkLocalServiceBrowser(dnsSDQuerier); - linkLocalServiceBrowser->onServiceAdded.connect( - boost::bind(&Slimber::handleServicesChanged, this)); - linkLocalServiceBrowser->onServiceRemoved.connect( - boost::bind(&Slimber::handleServicesChanged, this)); - linkLocalServiceBrowser->onServiceChanged.connect( - boost::bind(&Slimber::handleServicesChanged, this)); - linkLocalServiceBrowser->start(); - - vCardCollection = new FileVCardCollection( - PlatformApplication("Slimber").getSettingsDir()); - - server = new Server(5222, 5562, linkLocalServiceBrowser, vCardCollection); - server->onSelfConnected.connect( - boost::bind(&Slimber::handleSelfConnected, this, _1)); - - menulet = [[Menulet alloc] init]; - handleServicesChanged(); -} - -Slimber::~Slimber() { - [menulet release]; - delete server; - delete vCardCollection; - linkLocalServiceBrowser->stop(); - delete linkLocalServiceBrowser; - dnsSDQuerier->stop(); -} - -void Slimber::handleSelfConnected(bool b) { - [menulet setSelfConnected: b]; -} - -void Slimber::handleServicesChanged() { - NSMutableArray* names = [[NSMutableArray alloc] init]; - foreach(const LinkLocalService& service, linkLocalServiceBrowser->getServices()) { - [names addObject: [NSString stringWithUTF8String: service.getDescription().getUTF8Data()]]; - } - - [menulet setUserNames: names]; - [names release]; -} diff --git a/Slimber/LinkLocalPresenceManager.cpp b/Slimber/LinkLocalPresenceManager.cpp index b964786..d5834c7 100644 --- a/Slimber/LinkLocalPresenceManager.cpp +++ b/Slimber/LinkLocalPresenceManager.cpp @@ -18,6 +18,15 @@ LinkLocalPresenceManager::LinkLocalPresenceManager(LinkLocalServiceBrowser* brow boost::bind(&LinkLocalPresenceManager::handleServiceRemoved, this, _1)); } +boost::optional<LinkLocalService> LinkLocalPresenceManager::getServiceForJID(const JID& j) const { + foreach(const LinkLocalService& service, browser->getServices()) { + if (service.getJID() == j) { + return service; + } + } + return boost::optional<LinkLocalService>(); +} + void LinkLocalPresenceManager::handleServiceAdded(const LinkLocalService& service) { boost::shared_ptr<RosterPayload> roster(new RosterPayload()); roster->addItem(getRosterItem(service)); @@ -31,7 +40,7 @@ void LinkLocalPresenceManager::handleServiceChanged(const LinkLocalService& serv void LinkLocalPresenceManager::handleServiceRemoved(const LinkLocalService& service) { boost::shared_ptr<RosterPayload> roster(new RosterPayload()); - roster->addItem(RosterItemPayload(getJIDForService(service), "", RosterItemPayload::Remove)); + roster->addItem(RosterItemPayload(service.getJID(), "", RosterItemPayload::Remove)); onRosterChanged(roster); } @@ -52,7 +61,7 @@ std::vector<boost::shared_ptr<Presence> > LinkLocalPresenceManager::getAllPresen } RosterItemPayload LinkLocalPresenceManager::getRosterItem(const LinkLocalService& service) const { - return RosterItemPayload(getJIDForService(service), getRosterName(service), RosterItemPayload::Both); + return RosterItemPayload(service.getJID(), getRosterName(service), RosterItemPayload::Both); } String LinkLocalPresenceManager::getRosterName(const LinkLocalService& service) const { @@ -73,13 +82,9 @@ String LinkLocalPresenceManager::getRosterName(const LinkLocalService& service) return ""; } -JID LinkLocalPresenceManager::getJIDForService(const LinkLocalService& service) const { - return JID(service.getName()); -} - boost::shared_ptr<Presence> LinkLocalPresenceManager::getPresence(const LinkLocalService& service) const { boost::shared_ptr<Presence> presence(new Presence()); - presence->setFrom(getJIDForService(service)); + presence->setFrom(service.getJID()); switch (service.getInfo().getStatus()) { case LinkLocalServiceInfo::Available: presence->setShow(StatusShow::Online); diff --git a/Slimber/LinkLocalPresenceManager.h b/Slimber/LinkLocalPresenceManager.h index 2af2313..c8f77e9 100644 --- a/Slimber/LinkLocalPresenceManager.h +++ b/Slimber/LinkLocalPresenceManager.h @@ -13,13 +13,15 @@ namespace Swift { class RosterPayload; class Presence; - class LinkLocalPresenceManager { + class LinkLocalPresenceManager : public boost::bsignals::trackable { public: LinkLocalPresenceManager(LinkLocalServiceBrowser*); boost::shared_ptr<RosterPayload> getRoster() const; std::vector<boost::shared_ptr<Presence> > getAllPresence() const; + boost::optional<LinkLocalService> getServiceForJID(const JID&) const; + boost::signal<void (boost::shared_ptr<RosterPayload>)> onRosterChanged; boost::signal<void (boost::shared_ptr<Presence>)> onPresenceChanged; @@ -30,7 +32,6 @@ namespace Swift { RosterItemPayload getRosterItem(const LinkLocalService& service) const; String getRosterName(const LinkLocalService& service) const; - JID getJIDForService(const LinkLocalService& service) const; boost::shared_ptr<Presence> getPresence(const LinkLocalService& service) const; private: diff --git a/Slimber/MainController.cpp b/Slimber/MainController.cpp new file mode 100644 index 0000000..c8c2ad1 --- /dev/null +++ b/Slimber/MainController.cpp @@ -0,0 +1,123 @@ +#include "Slimber/MainController.h" + +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <iostream> + +#include "Swiften/Base/foreach.h" +#include "Swiften/Application/Platform/PlatformApplication.h" +#include "Swiften/LinkLocal/LinkLocalService.h" +#include "Swiften/LinkLocal/LinkLocalServiceBrowser.h" +#include "Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h" +#include "Slimber/Server.h" +#include "Slimber/FileVCardCollection.h" +#include "Slimber/MenuletController.h" +#include "Slimber/Menulet.h" + +using namespace Swift; + +MainController::MainController(Menulet* menulet) : menulet(menulet) { + menuletController = new MenuletController(menulet); + menuletController->onRestartRequested.connect(boost::bind( + &MainController::handleRestartRequested, this)); + + dnsSDQuerier = PlatformDNSSDQuerierFactory().createQuerier(); + if (!dnsSDQuerier) { + // TODO + assert(false); + } + + linkLocalServiceBrowser = new LinkLocalServiceBrowser(dnsSDQuerier); + linkLocalServiceBrowser->onServiceAdded.connect( + boost::bind(&MainController::handleServicesChanged, this)); + linkLocalServiceBrowser->onServiceRemoved.connect( + boost::bind(&MainController::handleServicesChanged, this)); + linkLocalServiceBrowser->onServiceChanged.connect( + boost::bind(&MainController::handleServicesChanged, this)); + + vCardCollection = new FileVCardCollection( + PlatformApplication("Slimber").getSettingsDir()); + + server = new Server(5222, 5562, linkLocalServiceBrowser, vCardCollection); + server->onStopped.connect( + boost::bind(&MainController::handleServerStopped, this, _1)); + server->onSelfConnected.connect( + boost::bind(&MainController::handleSelfConnected, this, _1)); + + start(); +} + +MainController::~MainController() { + delete menuletController; + delete server; + delete vCardCollection; + linkLocalServiceBrowser->stop(); + delete linkLocalServiceBrowser; + dnsSDQuerier->stop(); +} + +void MainController::start() { + dnsSDQuerier->start(); + linkLocalServiceBrowser->start(); + + handleSelfConnected(false); + handleServicesChanged(); + + server->start(); +} + +void MainController::stop() { + server->stop(); + linkLocalServiceBrowser->stop(); + dnsSDQuerier->stop(); +} + +void MainController::handleSelfConnected(bool b) { + if (b) { + menuletController->setXMPPStatus("You are logged in", MenuletController::Online); + } + else { + menuletController->setXMPPStatus("You are not logged in", MenuletController::Offline); + } +} + +void MainController::handleServicesChanged() { + std::vector<String> names; + foreach(const LinkLocalService& service, linkLocalServiceBrowser->getServices()) { + String description = service.getDescription(); + if (description != service.getName()) { + description += " (" + service.getName() + ")"; + } + names.push_back(description); + } + menuletController->setUserNames(names); +} + +void MainController::handleServerStopped(boost::optional<ServerError> error) { + if (error) { + String message; + switch (error->getType()) { + case ServerError::C2SPortConflict: + message = String("Error: Port ") + boost::lexical_cast<std::string>(server->getClientToServerPort()) + String(" in use"); + break; + case ServerError::C2SError: + message = String("Local connection server error"); + break; + case ServerError::LinkLocalPortConflict: + message = String("Error: Port ") + boost::lexical_cast<std::string>(server->getLinkLocalPort()) + String(" in use"); + break; + case ServerError::LinkLocalError: + message = String("External connection server error"); + break; + } + menuletController->setXMPPStatus(message, MenuletController::Offline); + } + else { + menuletController->setXMPPStatus("XMPP Server Not Running", MenuletController::Offline); + } +} + +void MainController::handleRestartRequested() { + stop(); + start(); +} diff --git a/Slimber/MainController.h b/Slimber/MainController.h new file mode 100644 index 0000000..2c74e4c --- /dev/null +++ b/Slimber/MainController.h @@ -0,0 +1,39 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/optional.hpp> + +#include "Slimber/ServerError.h" + +namespace Swift { + class DNSSDQuerier; + class LinkLocalServiceBrowser; + class VCardCollection; + class Server; +} + +class MenuletController; +class Menulet; + +class MainController { + public: + MainController(Menulet* menulet); + virtual ~MainController(); + + private: + void handleSelfConnected(bool b); + void handleServicesChanged(); + void handleServerStopped(boost::optional<Swift::ServerError> error); + void handleRestartRequested(); + + void start(); + void stop(); + + private: + Menulet* menulet; + boost::shared_ptr<Swift::DNSSDQuerier> dnsSDQuerier; + Swift::LinkLocalServiceBrowser* linkLocalServiceBrowser; + Swift::VCardCollection* vCardCollection; + Swift::Server* server; + MenuletController* menuletController; +}; diff --git a/Slimber/Makefile.inc b/Slimber/Makefile.inc index 16fcb8f..2821d18 100644 --- a/Slimber/Makefile.inc +++ b/Slimber/Makefile.inc @@ -3,7 +3,10 @@ SLIMBER_SOURCES = \ Slimber/LinkLocalPresenceManager.cpp \ Slimber/FileVCardCollection.cpp \ Slimber/VCardCollection.cpp \ - Slimber/Server.cpp + Slimber/Server.cpp \ + Slimber/MainController.cpp \ + Slimber/MenuletController.cpp \ + Slimber/Menulet.cpp SLIMBER_OBJECTS = \ $(SLIMBER_SOURCES:.cpp=.o) diff --git a/Slimber/Menulet.cpp b/Slimber/Menulet.cpp new file mode 100644 index 0000000..bdadb98 --- /dev/null +++ b/Slimber/Menulet.cpp @@ -0,0 +1,4 @@ +#include "Slimber/Menulet.h" + +Menulet::~Menulet() { +} diff --git a/Slimber/Menulet.h b/Slimber/Menulet.h new file mode 100644 index 0000000..1b8ed18 --- /dev/null +++ b/Slimber/Menulet.h @@ -0,0 +1,20 @@ +#pragma once + +#include <boost/signal.hpp> + +#include "Swiften/Base/String.h" + +class Menulet { + public: + virtual ~Menulet(); + + virtual void clear() = 0; + virtual void addItem(const Swift::String& name, const Swift::String& icon = Swift::String()) = 0; + virtual void addAboutItem() = 0; + virtual void addRestartItem() = 0; + virtual void addExitItem() = 0; + virtual void addSeparator() = 0; + virtual void setIcon(const Swift::String&) = 0; + + boost::signal<void ()> onRestartClicked; +}; diff --git a/Slimber/MenuletController.cpp b/Slimber/MenuletController.cpp new file mode 100644 index 0000000..1532459 --- /dev/null +++ b/Slimber/MenuletController.cpp @@ -0,0 +1,51 @@ +#include "Slimber/MenuletController.h" + +#include "Swiften/Base/foreach.h" +#include "Swiften/Base/String.h" +#include "Slimber/Menulet.h" + +#include <iostream> + +using namespace Swift; + +MenuletController::MenuletController(Menulet* menulet) : + menulet(menulet), xmppStatus(Offline) { + menulet->onRestartClicked.connect(boost::ref(onRestartRequested)); + update(); +} + +MenuletController::~MenuletController() { +} + +void MenuletController::setXMPPStatus(const String& message, Status status) { + xmppStatus = status; + xmppStatusMessage = message; + update(); +} + +void MenuletController::setUserNames(const std::vector<String>& users) { + linkLocalUsers = users; + update(); +} + +void MenuletController::update() { + menulet->clear(); + if (linkLocalUsers.empty()) { + menulet->setIcon("UsersOffline"); + menulet->addItem("No online users"); + } + else { + menulet->setIcon("UsersOnline"); + menulet->addItem("Online users:"); + foreach(const String& user, linkLocalUsers) { + menulet->addItem(String(" ") + user); + } + } + menulet->addSeparator(); + menulet->addItem(xmppStatusMessage, (xmppStatus == Online ? "Online" : "Offline")); + menulet->addSeparator(); + menulet->addAboutItem(); + menulet->addSeparator(); + menulet->addRestartItem(); + menulet->addExitItem(); +} diff --git a/Slimber/MenuletController.h b/Slimber/MenuletController.h new file mode 100644 index 0000000..5e45038 --- /dev/null +++ b/Slimber/MenuletController.h @@ -0,0 +1,33 @@ +#pragma once + +#include <vector> +#include <boost/signal.hpp> + +#include "Swiften/Base/String.h" + +class Menulet; + +class MenuletController { + public: + enum Status { + Online, + Offline + }; + + MenuletController(Menulet*); + virtual ~MenuletController(); + + void setXMPPStatus(const Swift::String& message, Status status); + void setUserNames(const std::vector<Swift::String>&); + + boost::signal<void ()> onRestartRequested; + + private: + void update(); + + private: + Menulet* menulet; + Status xmppStatus; + Swift::String xmppStatusMessage; + std::vector<Swift::String> linkLocalUsers; +}; diff --git a/Slimber/Qt/QtMenulet.h b/Slimber/Qt/QtMenulet.h new file mode 100644 index 0000000..4ac9140 --- /dev/null +++ b/Slimber/Qt/QtMenulet.h @@ -0,0 +1,65 @@ +#pragma once + +#include <QCoreApplication> +#include <QMenu> +#include <QString> +#include <QSystemTrayIcon> +#include <QObject> +#include <QPixmap> + +#include "Slimber/Menulet.h" + +class QtMenulet : public QObject, public Menulet { + Q_OBJECT + public: + QtMenulet() { + trayIcon.setIcon(QPixmap(":/icons/UsersOffline.png")); + trayIcon.setContextMenu(&menu); + trayIcon.show(); + } + + void clear() { + menu.clear(); + } + + void addItem(const Swift::String& name, const Swift::String& icon) { + menu.addAction(getIcon(icon), QString::fromUtf8(name.getUTF8Data())); + } + + void addAboutItem() { + menu.addAction("About"); + } + + void addRestartItem() { + menu.addAction("Restart", this, SLOT(restart())); + } + + void addExitItem() { + menu.addAction("Exit", qApp, SLOT(quit())); + } + + void addSeparator() { + menu.addSeparator(); + } + + void setIcon(const Swift::String& icon) { + trayIcon.setIcon(getIcon(icon)); + } + + private: + QPixmap getIcon(const Swift::String& name) { + return QPixmap(":/icons/" + QString::fromUtf8(name.getUTF8Data()) + ".png"); + } + + private slots: + void showAboutDialog() { + } + + void restart() { + onRestartClicked(); + } + + private: + QMenu menu; + QSystemTrayIcon trayIcon; +}; diff --git a/Slimber/Qt/Slimber.pro b/Slimber/Qt/Slimber.pro new file mode 100644 index 0000000..128bed6 --- /dev/null +++ b/Slimber/Qt/Slimber.pro @@ -0,0 +1,24 @@ +TEMPLATE = app +CONFIG += debug +TARGET = slimber + +DEPENDPATH += . ../.. ../../3rdParty/Boost +INCLUDEPATH += . ../.. ../../3rdParty/Boost +LIBS += ../Slimber.a ../../Swiften/Swiften.a -lexpat -L. -lavahi-client -lxml2 +include(config.pri) + +DEFINES += BOOST_SIGNALS_NAMESPACE=bsignals BOOST_ALL_NO_LIB + +HEADERS += \ + QtMenulet.h + +SOURCES += \ + main.cpp + +RESOURCES += \ + Slimber.qrc + +#win32 { +# RC_FILE = ../Resources/Swift.rc +#} + diff --git a/Slimber/Qt/Slimber.qrc b/Slimber/Qt/Slimber.qrc new file mode 100644 index 0000000..6cc21ff --- /dev/null +++ b/Slimber/Qt/Slimber.qrc @@ -0,0 +1,9 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> + <qresource> + <file alias="icons/Offline.png">../Resources/Offline.png</file> + <file alias="icons/Online.png">../Resources/Online.png</file> + <file alias="icons/UsersOffline.png">../Resources/UsersOffline.png</file> + <file alias="icons/UsersOnline.png">../Resources/UsersOnline.png</file> + </qresource> +</RCC> diff --git a/Slimber/Qt/main.cpp b/Slimber/Qt/main.cpp new file mode 100644 index 0000000..0988e7c --- /dev/null +++ b/Slimber/Qt/main.cpp @@ -0,0 +1,19 @@ +#include <QApplication> +#include <QSystemTrayIcon> +#include <QMessageBox> + +#include "QtMenulet.h" +#include "Slimber/MainController.h" + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + if (!QSystemTrayIcon::isSystemTrayAvailable()) { +QMessageBox::critical(0, QObject::tr("Systray"), QObject::tr("No system tray")); + return 1; + } + + QtMenulet menulet; + MainController controller(&menulet); + + return app.exec(); +} diff --git a/Slimber/Resources/Icon.svg b/Slimber/Resources/Icon.svg index a39e7e4..1e701a6 100644 --- a/Slimber/Resources/Icon.svg +++ b/Slimber/Resources/Icon.svg @@ -100,7 +100,7 @@ sodipodi:type="inkscape:offset" inkscape:radius="0" inkscape:original="M 35.75 21.4375 C 67.770833 28.589068 61.962345 38.980958 48.59375 47.71875 C 42.117938 49.826519 34.459217 52.284088 23.875 55.28125 C 28.767848 54.681471 33.106944 54.336611 37.125 54.125 C 33.141375 56.073855 29.188794 57.883088 25.75 59.34375 C 34.172431 57.138896 40.17062 55.635095 46.34375 54.09375 C 68.404841 54.936763 70.366936 64.588264 50.03125 83.5 C 80.52166 67.640036 88.246281 56.011556 70.21875 48.46875 C 79.529352 46.508168 83.310469 46.138239 85.28125 46.3125 C 80.487749 43.302683 77.123885 41.944256 73.40625 41.6875 C 81.713537 30.753386 64.586454 23.918546 35.75 21.4375 z M 73.9375 43.25 C 74.54083 43.25 75.03125 43.724312 75.03125 44.28125 C 75.031251 44.83819 74.54083 45.28125 73.9375 45.28125 C 73.33417 45.281249 72.84375 44.838189 72.84375 44.28125 C 72.84375 43.724313 73.33417 43.25 73.9375 43.25 z " - style="fill:#585858;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.10801075px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + style="fill:#585858;fill-opacity:1;fill-rule:nonzero;stroke:#333333;stroke-width:0.59638305999999996;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" id="path3154" d="M 35.75,21.4375 C 67.770833,28.589068 61.962345,38.980958 48.59375,47.71875 C 42.117938,49.826519 34.459217,52.284088 23.875,55.28125 C 28.767848,54.681471 33.106944,54.336611 37.125,54.125 C 33.141375,56.073855 29.188794,57.883088 25.75,59.34375 C 34.172431,57.138896 40.17062,55.635095 46.34375,54.09375 C 68.404841,54.936763 70.366936,64.588264 50.03125,83.5 C 80.52166,67.640036 88.246281,56.011556 70.21875,48.46875 C 79.529352,46.508168 83.310469,46.138239 85.28125,46.3125 C 80.487749,43.302683 77.123885,41.944256 73.40625,41.6875 C 81.713537,30.753386 64.586454,23.918546 35.75,21.4375 z M 73.9375,43.25 C 74.54083,43.25 75.03125,43.724312 75.03125,44.28125 C 75.031251,44.83819 74.54083,45.28125 73.9375,45.28125 C 73.33417,45.281249 72.84375,44.838189 72.84375,44.28125 C 72.84375,43.724313 73.33417,43.25 73.9375,43.25 z" transform="matrix(1.5329782,0,0,1.5087872,-34.813863,0.3399684)" /> diff --git a/Slimber/Resources/Slimber.icns b/Slimber/Resources/Slimber.icns Binary files differnew file mode 100644 index 0000000..5deeb40 --- /dev/null +++ b/Slimber/Resources/Slimber.icns diff --git a/Slimber/Server.cpp b/Slimber/Server.cpp index a63201b..e07fb41 100644 --- a/Slimber/Server.cpp +++ b/Slimber/Server.cpp @@ -37,35 +37,98 @@ Server::Server( clientConnectionPort(clientConnectionPort), linkLocalConnectionPort(linkLocalConnectionPort), linkLocalServiceBrowser(linkLocalServiceBrowser), - vCardCollection(vCardCollection) { + vCardCollection(vCardCollection), + presenceManager(NULL), + stopping(false) { + linkLocalServiceBrowser->onServiceRegistered.connect( + boost::bind(&Server::handleServiceRegistered, this, _1)); +} + +Server::~Server() { + stop(); +} + +void Server::start() { + assert(!serverFromClientConnectionServer); serverFromClientConnectionServer = boost::shared_ptr<BoostConnectionServer>(new BoostConnectionServer( clientConnectionPort, &boostIOServiceThread.getIOService())); - serverFromClientConnectionServer->onNewConnection.connect( - boost::bind(&Server::handleNewClientConnection, this, _1)); - serverFromClientConnectionServer->start(); + serverFromClientConnectionServerSignalConnections.push_back( + serverFromClientConnectionServer->onNewConnection.connect( + boost::bind(&Server::handleNewClientConnection, this, _1))); + serverFromClientConnectionServerSignalConnections.push_back( + serverFromClientConnectionServer->onStopped.connect( + boost::bind(&Server::handleClientConnectionServerStopped, this, _1))); + assert(!serverFromNetworkConnectionServer); + serverFromNetworkConnectionServer = + boost::shared_ptr<BoostConnectionServer>(new BoostConnectionServer( + linkLocalConnectionPort, &boostIOServiceThread.getIOService())); + serverFromNetworkConnectionServerSignalConnections.push_back( + serverFromNetworkConnectionServer->onNewConnection.connect( + boost::bind(&Server::handleNewLinkLocalConnection, this, _1))); + serverFromNetworkConnectionServerSignalConnections.push_back( + serverFromNetworkConnectionServer->onStopped.connect( + boost::bind(&Server::handleLinkLocalConnectionServerStopped, this, _1))); + + assert(!presenceManager); presenceManager = new LinkLocalPresenceManager(linkLocalServiceBrowser); presenceManager->onRosterChanged.connect( boost::bind(&Server::handleRosterChanged, this, _1)); presenceManager->onPresenceChanged.connect( boost::bind(&Server::handlePresenceChanged, this, _1)); - linkLocalServiceBrowser->onServiceRegistered.connect( - boost::bind(&Server::handleServiceRegistered, this, _1)); - - /* - serverFromNetworkConnectionServer = - boost::shared_ptr<BoostConnectionServer>(new BoostConnectionServer( - linkLocalConnectionPort, &boostIOServiceThread.getIOService())); - serverFromNetworkConnectionServer->onNewConnection.connect( - boost::bind(&Server::handleNewLinkLocalConnection, this, _1)); + serverFromClientConnectionServer->start(); serverFromNetworkConnectionServer->start(); - */ } -Server::~Server() { +void Server::stop() { + stop(boost::optional<ServerError>()); +} + +void Server::stop(boost::optional<ServerError> e) { + if (stopping) { + return; + } + + stopping = true; + delete presenceManager; + presenceManager = NULL; + + if (serverFromClientSession) { + serverFromClientSession->finishSession(); + } + serverFromClientSession.reset(); + foreach(boost::shared_ptr<Session> session, linkLocalSessions) { + session->finishSession(); + } + linkLocalSessions.clear(); + foreach(boost::shared_ptr<LinkLocalConnector> connector, connectors) { + connector->cancel(); + } + connectors.clear(); + tracers.clear(); + + if (serverFromNetworkConnectionServer) { + serverFromNetworkConnectionServer->stop(); + foreach(boost::bsignals::connection& connection, serverFromNetworkConnectionServerSignalConnections) { + connection.disconnect(); + } + serverFromNetworkConnectionServerSignalConnections.clear(); + serverFromNetworkConnectionServer.reset(); + } + if (serverFromClientConnectionServer) { + serverFromClientConnectionServer->stop(); + foreach(boost::bsignals::connection& connection, serverFromClientConnectionServerSignalConnections) { + connection.disconnect(); + } + serverFromClientConnectionServerSignalConnections.clear(); + serverFromClientConnectionServer.reset(); + } + + stopping = false; + onStopped(e); } void Server::handleNewClientConnection(boost::shared_ptr<Connection> connection) { @@ -83,8 +146,8 @@ void Server::handleNewClientConnection(boost::shared_ptr<Connection> connection) serverFromClientSession->onSessionFinished.connect( boost::bind(&Server::handleSessionFinished, this, serverFromClientSession)); - tracers.push_back(boost::shared_ptr<SessionTracer>( - new SessionTracer(serverFromClientSession))); + //tracers.push_back(boost::shared_ptr<SessionTracer>( + // new SessionTracer(serverFromClientSession))); serverFromClientSession->startSession(); } @@ -168,7 +231,6 @@ void Server::handleElementReceived(boost::shared_ptr<Element> element, boost::sh } } } - /* else { JID toJID = stanza->getTo(); boost::shared_ptr<Session> outgoingSession = @@ -177,20 +239,20 @@ void Server::handleElementReceived(boost::shared_ptr<Element> element, boost::sh outgoingSession->sendElement(stanza); } else { - if (linkLocalServiceBrowser->hasItem(toJID)) { + boost::optional<LinkLocalService> service = + presenceManager->getServiceForJID(toJID); + if (service) { boost::shared_ptr<LinkLocalConnector> connector = getLinkLocalConnectorForJID(toJID); if (!connector) { connector = boost::shared_ptr<LinkLocalConnector>( new LinkLocalConnector( - toJID, - linkLocalServiceBrowser->getHostname(toJID), - linkLocalServiceBrowser->getPort(toJID), - linkLocalServiceBrowser, + *service, + linkLocalServiceBrowser->getQuerier(), boost::shared_ptr<BoostConnection>(new BoostConnection(&boostIOServiceThread.getIOService())))); connector->onConnectFinished.connect( boost::bind(&Server::handleConnectFinished, this, connector, _1)); - connectors_.push_back(connector); + connectors.push_back(connector); connector->connect(); } connector->queueElement(element); @@ -202,10 +264,8 @@ void Server::handleElementReceived(boost::shared_ptr<Element> element, boost::sh } } } - */ } -/* void Server::handleNewLinkLocalConnection(boost::shared_ptr<Connection> connection) { boost::shared_ptr<IncomingLinkLocalSession> session( new IncomingLinkLocalSession( @@ -215,15 +275,17 @@ void Server::handleNewLinkLocalConnection(boost::shared_ptr<Connection> connecti } void Server::handleLinkLocalSessionFinished(boost::shared_ptr<Session> session) { - std::cout << "Link local session from " << session->getRemoteJID() << " ended" << std::endl; - linkLocalSessions_.erase(std::remove(linkLocalSessions_.begin(), linkLocalSessions_.end(), session), linkLocalSessions_.end()); + //std::cout << "Link local session from " << session->getRemoteJID() << " ended" << std::endl; + linkLocalSessions.erase( + std::remove(linkLocalSessions.begin(), linkLocalSessions.end(), session), + linkLocalSessions.end()); } void Server::handleLinkLocalElementReceived(boost::shared_ptr<Element> element, boost::shared_ptr<Session> session) { if (boost::shared_ptr<Stanza> stanza = boost::dynamic_pointer_cast<Stanza>(element)) { JID fromJID = session->getRemoteJID(); - if (!linkLocalServiceBrowser->hasItem(fromJID)) { - return; // TODO: Queue + if (!presenceManager->getServiceForJID(fromJID.toBare())) { + return; // TODO: Send error back } stanza->setFrom(fromJID); serverFromClientSession->sendElement(stanza); @@ -238,27 +300,28 @@ void Server::handleConnectFinished(boost::shared_ptr<LinkLocalConnector> connect else { boost::shared_ptr<OutgoingLinkLocalSession> outgoingSession( new OutgoingLinkLocalSession( - selfJID, connector->getRemoteJID(), connector->getConnection(), + selfJID, connector->getService().getJID(), connector->getConnection(), &payloadParserFactories, &payloadSerializers)); foreach(const boost::shared_ptr<Element> element, connector->getQueuedElements()) { outgoingSession->queueElement(element); } registerLinkLocalSession(outgoingSession); } - connectors_.erase(std::remove(connectors_.begin(), connectors_.end(), connector), connectors_.end()); + connectors.erase(std::remove(connectors.begin(), connectors.end(), connector), connectors.end()); } - void Server::registerLinkLocalSession(boost::shared_ptr<Session> session) { - session->onSessionFinished.connect(boost::bind(&Server::handleLinkLocalSessionFinished, this, session)); - session->onElementReceived.connect(boost::bind(&Server::handleLinkLocalElementReceived, this, _1, session)); - linkLocalSessions_.push_back(session); + session->onSessionFinished.connect( + boost::bind(&Server::handleLinkLocalSessionFinished, this, session)); + session->onElementReceived.connect( + boost::bind(&Server::handleLinkLocalElementReceived, this, _1, session)); + linkLocalSessions.push_back(session); //tracers.push_back(boost::shared_ptr<SessionTracer>(new SessionTracer(session))); session->startSession(); } boost::shared_ptr<Session> Server::getLinkLocalSessionForJID(const JID& jid) { - foreach(const boost::shared_ptr<Session> session, linkLocalSessions_) { + foreach(const boost::shared_ptr<Session> session, linkLocalSessions) { if (session->getRemoteJID() == jid) { return session; } @@ -267,14 +330,13 @@ boost::shared_ptr<Session> Server::getLinkLocalSessionForJID(const JID& jid) { } boost::shared_ptr<LinkLocalConnector> Server::getLinkLocalConnectorForJID(const JID& jid) { - foreach(const boost::shared_ptr<LinkLocalConnector> connector, connectors_) { - if (connector->getRemoteJID() == jid) { + foreach(const boost::shared_ptr<LinkLocalConnector> connector, connectors) { + if (connector->getService().getJID() == jid) { return connector; } } return boost::shared_ptr<LinkLocalConnector>(); } -*/ void Server::handleServiceRegistered(const DNSSDServiceID& service) { selfJID = JID(service.getName()); @@ -297,6 +359,34 @@ void Server::handlePresenceChanged(boost::shared_ptr<Presence> presence) { } } +void Server::handleClientConnectionServerStopped(boost::optional<BoostConnectionServer::Error> e) { + if (e) { + if (*e == BoostConnectionServer::Conflict) { + stop(ServerError(ServerError::C2SPortConflict)); + } + else { + stop(ServerError(ServerError::C2SError)); + } + } + else { + stop(); + } +} + +void Server::handleLinkLocalConnectionServerStopped(boost::optional<BoostConnectionServer::Error> e) { + if (e) { + if (*e == BoostConnectionServer::Conflict) { + stop(ServerError(ServerError::LinkLocalPortConflict)); + } + else { + stop(ServerError(ServerError::LinkLocalError)); + } + } + else { + stop(); + } +} + LinkLocalServiceInfo Server::getLinkLocalServiceInfo(boost::shared_ptr<Presence> presence) { LinkLocalServiceInfo info; boost::shared_ptr<VCard> vcard = vCardCollection->getOwnVCard(); diff --git a/Slimber/Server.h b/Slimber/Server.h index 3ed0a58..3587c50 100644 --- a/Slimber/Server.h +++ b/Slimber/Server.h @@ -1,9 +1,11 @@ #pragma once #include <boost/shared_ptr.hpp> +#include <boost/optional.hpp> #include <vector> #include "Swiften/Network/BoostIOServiceThread.h" +#include "Swiften/Network/BoostConnectionServer.h" #include "Swiften/Server/UserRegistry.h" #include "Swiften/Base/IDGenerator.h" #include "Swiften/Server/ServerFromClientSession.h" @@ -11,11 +13,13 @@ #include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" #include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h" #include "Swiften/LinkLocal/LinkLocalServiceInfo.h" +#include "Slimber/ServerError.h" namespace Swift { class DNSSDServiceID; class String; class VCardCollection; + class LinkLocalConnector; class LinkLocalServiceBrowser; class LinkLocalPresenceManager; class BoostConnectionServer; @@ -32,9 +36,23 @@ namespace Swift { VCardCollection* vCardCollection); ~Server(); + void start(); + void stop(); + + int getLinkLocalPort() const { + return linkLocalConnectionPort; + } + + int getClientToServerPort() const { + return clientConnectionPort; + } + boost::signal<void (bool)> onSelfConnected; + boost::signal<void (boost::optional<ServerError>)> onStopped; private: + void stop(boost::optional<ServerError>); + void handleNewClientConnection(boost::shared_ptr<Connection> c); void handleSessionStarted(); void handleSessionFinished(boost::shared_ptr<ServerFromClientSession>); @@ -42,16 +60,17 @@ namespace Swift { void handleRosterChanged(boost::shared_ptr<RosterPayload> roster); void handlePresenceChanged(boost::shared_ptr<Presence> presence); void handleServiceRegistered(const DNSSDServiceID& service); -/* void handleNewLinkLocalConnection(boost::shared_ptr<Connection> connection); void handleLinkLocalSessionFinished(boost::shared_ptr<Session> session); void handleLinkLocalElementReceived(boost::shared_ptr<Element> element, boost::shared_ptr<Session> session); void handleConnectFinished(boost::shared_ptr<LinkLocalConnector> connector, bool error); - void registerLinkLocalSession(boost::shared_ptr<Session> session); + void handleClientConnectionServerStopped( + boost::optional<BoostConnectionServer::Error>); + void handleLinkLocalConnectionServerStopped( + boost::optional<BoostConnectionServer::Error>); boost::shared_ptr<Session> getLinkLocalSessionForJID(const JID& jid); boost::shared_ptr<LinkLocalConnector> getLinkLocalConnectorForJID(const JID& jid); - */ - + void registerLinkLocalSession(boost::shared_ptr<Session> session); void unregisterService(); LinkLocalServiceInfo getLinkLocalServiceInfo(boost::shared_ptr<Presence> presence); @@ -78,15 +97,16 @@ namespace Swift { LinkLocalServiceBrowser* linkLocalServiceBrowser; VCardCollection* vCardCollection; LinkLocalPresenceManager* presenceManager; + bool stopping; boost::shared_ptr<BoostConnectionServer> serverFromClientConnectionServer; + std::vector<boost::bsignals::connection> serverFromClientConnectionServerSignalConnections; boost::shared_ptr<ServerFromClientSession> serverFromClientSession; boost::shared_ptr<Presence> lastPresence; JID selfJID; - /* - boost::shared_ptr<BoostConnectionServer> serverFromNetworkConnectionServer_; - std::vector< boost::shared_ptr<Session> > linkLocalSessions_; - std::vector< boost::shared_ptr<LinkLocalConnector> > connectors_; -*/ + boost::shared_ptr<BoostConnectionServer> serverFromNetworkConnectionServer; + std::vector<boost::bsignals::connection> serverFromNetworkConnectionServerSignalConnections; + std::vector< boost::shared_ptr<Session> > linkLocalSessions; + std::vector< boost::shared_ptr<LinkLocalConnector> > connectors; std::vector< boost::shared_ptr<SessionTracer> > tracers; }; } diff --git a/Slimber/ServerError.h b/Slimber/ServerError.h new file mode 100644 index 0000000..ce293c2 --- /dev/null +++ b/Slimber/ServerError.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Swiften/Base/String.h" + +namespace Swift { + class ServerError { + public: + enum Type { + C2SPortConflict, + C2SError, + LinkLocalPortConflict, + LinkLocalError + }; + + ServerError(Type type, const String& message = String()) : + type(type), message(message) { + } + + Type getType() const { + return type; + } + + const String& getMessage() const { + return message; + } + + private: + Type type; + String message; + }; +} diff --git a/Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp b/Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp index f15daf4..f77a8cb 100644 --- a/Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp +++ b/Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp @@ -29,6 +29,8 @@ class LinkLocalPresenceManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST(testGetRoster_InfoWithLastName); CPPUNIT_TEST(testGetRoster_InfoWithFirstAndLastName); CPPUNIT_TEST(testGetRoster_NoInfo); + CPPUNIT_TEST(testGetServiceForJID); + CPPUNIT_TEST(testGetServiceForJID_NoMatch); CPPUNIT_TEST_SUITE_END(); public: @@ -176,6 +178,27 @@ class LinkLocalPresenceManagerTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(String(""), item->getName()); } + void testGetServiceForJID() { + std::auto_ptr<LinkLocalPresenceManager> testling(createTestling()); + + addService("alice@wonderland"); + addService("rabbit@teaparty"); + addService("queen@garden"); + + boost::optional<LinkLocalService> service = testling->getServiceForJID(JID("rabbit@teaparty")); + CPPUNIT_ASSERT(service); + CPPUNIT_ASSERT_EQUAL(String("rabbit@teaparty"), service->getID().getName()); + } + + void testGetServiceForJID_NoMatch() { + std::auto_ptr<LinkLocalPresenceManager> testling(createTestling()); + + addService("alice@wonderland"); + addService("queen@garden"); + + CPPUNIT_ASSERT(!testling->getServiceForJID(JID("rabbit@teaparty"))); + } + private: std::auto_ptr<LinkLocalPresenceManager> createTestling() { std::auto_ptr<LinkLocalPresenceManager> testling( diff --git a/Slimber/UnitTest/Makefile.inc b/Slimber/UnitTest/Makefile.inc index f2c4db8..5bc0c8a 100644 --- a/Slimber/UnitTest/Makefile.inc +++ b/Slimber/UnitTest/Makefile.inc @@ -1,2 +1,3 @@ UNITTEST_SOURCES += \ - Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp + Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp \ + Slimber/UnitTest/MenuletControllerTest.cpp diff --git a/Slimber/UnitTest/MenuletControllerTest.cpp b/Slimber/UnitTest/MenuletControllerTest.cpp new file mode 100644 index 0000000..c666679 --- /dev/null +++ b/Slimber/UnitTest/MenuletControllerTest.cpp @@ -0,0 +1,151 @@ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Slimber/Menulet.h" +#include "Slimber/MenuletController.h" + +using namespace Swift; + +class MenuletControllerTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(MenuletControllerTest); + CPPUNIT_TEST(testConstructor); + CPPUNIT_TEST(testUpdate); + CPPUNIT_TEST(testSetXMPPStatus_Online); + CPPUNIT_TEST(testSetXMPPStatus_Offline); + CPPUNIT_TEST(testSetUserNames); + CPPUNIT_TEST(testSetUserNames_NoUsers); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + menulet = new FakeMenulet(); + } + + void tearDown() { + delete menulet; + } + + void testConstructor() { + MenuletController testling(menulet); + + CPPUNIT_ASSERT_EQUAL(8, static_cast<int>(menulet->items.size())); + int i = 0; + CPPUNIT_ASSERT_EQUAL(String("No online users"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("-"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("[Offline] "), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("-"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("*About*"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("-"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("*Restart*"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("*Exit*"), menulet->items[i++]); + } + + void testUpdate() { + MenuletController testling(menulet); + + testling.setXMPPStatus("You are connected", MenuletController::Online); + + CPPUNIT_ASSERT_EQUAL(8, static_cast<int>(menulet->items.size())); + int i = 0; + CPPUNIT_ASSERT_EQUAL(String("No online users"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("-"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("[Online] You are connected"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("-"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("*About*"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("-"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("*Restart*"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("*Exit*"), menulet->items[i++]); + } + + void testSetXMPPStatus_Online() { + MenuletController testling(menulet); + + testling.setXMPPStatus("You are connected", MenuletController::Online); + + int i = 0; + CPPUNIT_ASSERT_EQUAL(String("No online users"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("-"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("[Online] You are connected"), menulet->items[i++]); + } + + + void testSetXMPPStatus_Offline() { + MenuletController testling(menulet); + + testling.setXMPPStatus("You are not connected", MenuletController::Offline); + + int i = 0; + CPPUNIT_ASSERT_EQUAL(String("No online users"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("-"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("[Offline] You are not connected"), menulet->items[i++]); + } + + void testSetUserNames() { + MenuletController testling(menulet); + + std::vector<String> users; + users.push_back("Alice In Wonderland"); + users.push_back("The Mad Hatter"); + testling.setUserNames(users); + + int i = 0; + CPPUNIT_ASSERT_EQUAL(String("Online users:"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String(" Alice In Wonderland"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String(" The Mad Hatter"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("-"), menulet->items[i++]); + } + + void testSetUserNames_NoUsers() { + MenuletController testling(menulet); + + std::vector<String> users; + testling.setUserNames(users); + + int i = 0; + CPPUNIT_ASSERT_EQUAL(String("No online users"), menulet->items[i++]); + CPPUNIT_ASSERT_EQUAL(String("-"), menulet->items[i++]); + } + + private: + struct FakeMenulet : public Menulet { + virtual void clear() { + items.clear(); + } + + virtual void addItem(const String& name, const String& icon = String()) { + String result; + if (!icon.isEmpty()) { + result += "[" + icon + "] "; + } + result += name; + items.push_back(result); + } + + virtual void addAboutItem() { + items.push_back("*About*"); + } + + virtual void addRestartItem() { + items.push_back("*Restart*"); + } + + virtual void addExitItem() { + items.push_back("*Exit*"); + } + + virtual void addSeparator() { + items.push_back("-"); + } + + virtual void setIcon(const String& i) { + icon = i; + } + + std::vector<String> items; + String icon; + }; + + FakeMenulet* menulet; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(MenuletControllerTest); diff --git a/Swiften/Base/String.cpp b/Swiften/Base/String.cpp index 3495d9a..cc989f6 100644 --- a/Swiften/Base/String.cpp +++ b/Swiften/Base/String.cpp @@ -96,4 +96,21 @@ String String::getLowerCase() const { return String(lower); } +std::vector<String> String::split(char c) const { + assert((c & 0x80) == 0); + std::vector<String> result; + String accumulator; + for (size_t i = 0; i < data_.size(); ++i) { + if (data_[i] == c) { + result.push_back(accumulator); + accumulator = ""; + } + else { + accumulator += data_[i]; + } + } + result.push_back(accumulator); + return result; +} + } diff --git a/Swiften/Base/String.h b/Swiften/Base/String.h index 247a8a3..336a663 100644 --- a/Swiften/Base/String.h +++ b/Swiften/Base/String.h @@ -37,6 +37,8 @@ namespace Swift { */ std::pair<String,String> getSplittedAtFirst(char c) const; + std::vector<String> split(char c) const; + size_t getLength() const; String getLowerCase() const; diff --git a/Swiften/Base/UnitTest/StringTest.cpp b/Swiften/Base/UnitTest/StringTest.cpp index 2fa9f54..1dd44fb 100644 --- a/Swiften/Base/UnitTest/StringTest.cpp +++ b/Swiften/Base/UnitTest/StringTest.cpp @@ -24,6 +24,7 @@ class StringTest : public CppUnit::TestFixture CPPUNIT_TEST(testReplaceAll_ConsecutiveChars); CPPUNIT_TEST(testReplaceAll_MatchingReplace); CPPUNIT_TEST(testGetLowerCase); + CPPUNIT_TEST(testSplit); CPPUNIT_TEST_SUITE_END(); public: @@ -148,6 +149,15 @@ class StringTest : public CppUnit::TestFixture CPPUNIT_ASSERT_EQUAL(String("abcd e"), testling.getLowerCase()); } + + void testSplit() { + std::vector<String> result = String("abc def ghi").split(' '); + + CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(result.size())); + CPPUNIT_ASSERT_EQUAL(String("abc"), result[0]); + CPPUNIT_ASSERT_EQUAL(String("def"), result[1]); + CPPUNIT_ASSERT_EQUAL(String("ghi"), result[2]); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(StringTest); diff --git a/Swiften/EventLoop/DummyEventLoop.h b/Swiften/EventLoop/DummyEventLoop.h index 234ecfa..7766bd4 100644 --- a/Swiften/EventLoop/DummyEventLoop.h +++ b/Swiften/EventLoop/DummyEventLoop.h @@ -1,7 +1,7 @@ -#ifndef SWIFTEN_DummyEventLoop_H -#define SWIFTEN_DummyEventLoop_H +#pragma once #include <deque> +#include <iostream> #include <boost/function.hpp> #include "Swiften/EventLoop/EventLoop.h" @@ -13,6 +13,13 @@ namespace Swift { DummyEventLoop() { } + ~DummyEventLoop() { + if (!events_.empty()) { + std::cerr << "DummyEventLoop: Unhandled events at destruction time" << std::endl; + } + events_.clear(); + } + void processEvents() { while (!events_.empty()) { handleEvent(events_[0]); @@ -20,9 +27,9 @@ namespace Swift { } } - bool hasEvents() { - return events_.size() > 0; - } + bool hasEvents() { + return events_.size() > 0; + } virtual void post(const Event& event) { events_.push_back(event); @@ -32,6 +39,3 @@ namespace Swift { std::deque<Event> events_; }; } - -#endif - diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h index c605175..2dec2fb 100644 --- a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h @@ -41,7 +41,7 @@ namespace Swift { MainEventLoop::postEvent(boost::bind(boost::ref(onError)), shared_from_this()); } else { - std::cout << "Discovered service: name:" << name << " domain:" << domain << " type: " << type << std::endl; + //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()); diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h index 886b87b..1c38179 100644 --- a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h @@ -45,7 +45,7 @@ namespace Swift { 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; + //std::cout << "Service resolved: name:" << fullName << " host:" << host << " port:" << port << std::endl; MainEventLoop::postEvent( boost::bind( boost::ref(onServiceResolved), diff --git a/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h index 799bc0c..efcc140 100644 --- a/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h +++ b/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h @@ -15,6 +15,9 @@ namespace Swift { 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; diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp index 5079192..c26f8ee 100644 --- a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp @@ -13,6 +13,12 @@ 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())); } @@ -47,6 +53,15 @@ void FakeDNSSDQuerier::addRunningQuery(boost::shared_ptr<FakeDNSSDQuery> 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) { @@ -102,4 +117,14 @@ void FakeDNSSDQuerier::setRegisterError() { } } +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 index f2ec17b..22bca0c 100644 --- a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h @@ -6,9 +6,11 @@ #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; @@ -21,6 +23,10 @@ namespace Swift { 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( @@ -37,6 +43,7 @@ namespace Swift { 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(); @@ -59,5 +66,6 @@ namespace Swift { 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/Makefile.inc b/Swiften/LinkLocal/DNSSD/Makefile.inc index f6997ef..4b014f3 100644 --- a/Swiften/LinkLocal/DNSSD/Makefile.inc +++ b/Swiften/LinkLocal/DNSSD/Makefile.inc @@ -4,7 +4,8 @@ SWIFTEN_SOURCES += \ Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.cpp \ Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.cpp \ Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.cpp \ - Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp + Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp \ + Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp ifeq ($(HAVE_BONJOUR),yes) include Swiften/LinkLocal/DNSSD/Bonjour/Makefile.inc diff --git a/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp new file mode 100644 index 0000000..e704b72 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp @@ -0,0 +1,25 @@ +#include "Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h" + +#ifdef HAVE_SWIFTEN_CONFIG_H +#include "Swiften/config.h" +#endif +#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/LinkLocalConnector.cpp b/Swiften/LinkLocal/LinkLocalConnector.cpp index 18b5d6a..fba4a4e 100644 --- a/Swiften/LinkLocal/LinkLocalConnector.cpp +++ b/Swiften/LinkLocal/LinkLocalConnector.cpp @@ -12,22 +12,22 @@ namespace Swift { LinkLocalConnector::LinkLocalConnector( - const JID& remoteJID, - const String& hostname, - int interfaceIndex, - int port, + const LinkLocalService& service, boost::shared_ptr<DNSSDQuerier> querier, boost::shared_ptr<Connection> connection) : - remoteJID(remoteJID), - hostname(hostname), - interfaceIndex(interfaceIndex), - port(port), + service(service), querier(querier), connection(connection) { } +LinkLocalConnector::~LinkLocalConnector() { + assert(!resolveQuery); +} + void LinkLocalConnector::connect() { - resolveQuery = querier->createResolveHostnameQuery(hostname, interfaceIndex); + resolveQuery = querier->createResolveHostnameQuery( + service.getHostname(), + service.getID().getNetworkInterfaceID()); resolveQuery->onHostnameResolved.connect(boost::bind( &LinkLocalConnector::handleHostnameResolved, boost::dynamic_pointer_cast<LinkLocalConnector>(shared_from_this()), @@ -35,16 +35,24 @@ void LinkLocalConnector::connect() { 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) { - resolveQuery->finish(); - resolveQuery.reset(); connection->onConnectFinished.connect( boost::bind(boost::ref(onConnectFinished), _1)); - connection->connect(HostAddressPort(*address, port)); + connection->connect(HostAddressPort(*address, service.getPort())); } else { - onConnectFinished(false); + onConnectFinished(true); } } diff --git a/Swiften/LinkLocal/LinkLocalConnector.h b/Swiften/LinkLocal/LinkLocalConnector.h index 134656c..0b6baef 100644 --- a/Swiften/LinkLocal/LinkLocalConnector.h +++ b/Swiften/LinkLocal/LinkLocalConnector.h @@ -5,13 +5,12 @@ #include <boost/enable_shared_from_this.hpp> #include <vector> -#include "Swiften/JID/JID.h" #include "Swiften/Network/Connection.h" +#include "Swiften/LinkLocal/LinkLocalService.h" namespace Swift { class ConnectionFactory; class HostAddress; - class String; class Element; class PayloadParserFactoryCollection; class PayloadSerializerCollection; @@ -21,18 +20,17 @@ namespace Swift { class LinkLocalConnector : public boost::enable_shared_from_this<LinkLocalConnector> { public: LinkLocalConnector( - const JID& remoteJID, - const String& hostname, - int interfaceIndex, - int port, + const LinkLocalService& service, boost::shared_ptr<DNSSDQuerier> querier, boost::shared_ptr<Connection> connection); + ~LinkLocalConnector(); - const JID& getRemoteJID() const { - return remoteJID; + 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 { @@ -50,10 +48,7 @@ namespace Swift { void handleConnected(bool error); private: - JID remoteJID; - String hostname; - int interfaceIndex; - int port; + LinkLocalService service; boost::shared_ptr<DNSSDQuerier> querier; boost::shared_ptr<DNSSDResolveHostnameQuery> resolveQuery; boost::shared_ptr<Connection> connection; diff --git a/Swiften/LinkLocal/LinkLocalService.cpp b/Swiften/LinkLocal/LinkLocalService.cpp index f567a63..f1114ed 100644 --- a/Swiften/LinkLocal/LinkLocalService.cpp +++ b/Swiften/LinkLocal/LinkLocalService.cpp @@ -20,4 +20,8 @@ String LinkLocalService::getDescription() const { return getName(); } +JID LinkLocalService::getJID() const { + return JID(getName()); +} + } diff --git a/Swiften/LinkLocal/LinkLocalService.h b/Swiften/LinkLocal/LinkLocalService.h index f7e9e3c..8ae593c 100644 --- a/Swiften/LinkLocal/LinkLocalService.h +++ b/Swiften/LinkLocal/LinkLocalService.h @@ -1,6 +1,7 @@ #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" @@ -36,6 +37,8 @@ namespace Swift { String getDescription() const; + JID getJID() const; + private: DNSSDServiceID id; DNSSDResolveServiceQuery::Result info; diff --git a/Swiften/LinkLocal/LinkLocalServiceBrowser.h b/Swiften/LinkLocal/LinkLocalServiceBrowser.h index 7ef661c..66973d5 100644 --- a/Swiften/LinkLocal/LinkLocalServiceBrowser.h +++ b/Swiften/LinkLocal/LinkLocalServiceBrowser.h @@ -36,6 +36,11 @@ namespace Swift { 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; 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/Makefile.inc b/Swiften/LinkLocal/UnitTest/Makefile.inc index e5f1bf0..330808a 100644 --- a/Swiften/LinkLocal/UnitTest/Makefile.inc +++ b/Swiften/LinkLocal/UnitTest/Makefile.inc @@ -1,4 +1,5 @@ UNITTEST_SOURCES += \ Swiften/LinkLocal/UnitTest/LinkLocalServiceTest.cpp \ Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp \ - Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp + Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp \ + Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp diff --git a/Swiften/Network/BoostConnectionServer.cpp b/Swiften/Network/BoostConnectionServer.cpp index 18a3ca4..4e83ad5 100644 --- a/Swiften/Network/BoostConnectionServer.cpp +++ b/Swiften/Network/BoostConnectionServer.cpp @@ -1,28 +1,64 @@ #include "Swiften/Network/BoostConnectionServer.h" #include <boost/bind.hpp> +#include <boost/system/system_error.hpp> #include "Swiften/EventLoop/MainEventLoop.h" namespace Swift { -BoostConnectionServer::BoostConnectionServer(int port, boost::asio::io_service* ioService) : acceptor_(*ioService, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) { +BoostConnectionServer::BoostConnectionServer(int port, boost::asio::io_service* ioService) : port_(port), ioService_(ioService), acceptor_(NULL) { } void BoostConnectionServer::start() { - acceptNextConnection(); + try { + assert(!acceptor_); + acceptor_ = new boost::asio::ip::tcp::acceptor( + *ioService_, + boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port_)); + acceptNextConnection(); + } + catch (const boost::system::system_error& e) { + if (e.code() == boost::asio::error::address_in_use) { + MainEventLoop::postEvent(boost::bind(boost::ref(onStopped), Conflict), shared_from_this()); + } + else { + MainEventLoop::postEvent(boost::bind(boost::ref(onStopped), UnknownError), shared_from_this()); + } + } +} + + +void BoostConnectionServer::stop() { + stop(boost::optional<Error>()); +} + +void BoostConnectionServer::stop(boost::optional<Error> e) { + if (acceptor_) { + acceptor_->close(); + acceptor_ = NULL; + } + MainEventLoop::postEvent(boost::bind(boost::ref(onStopped), e), shared_from_this()); } void BoostConnectionServer::acceptNextConnection() { - boost::shared_ptr<BoostConnection> newConnection(new BoostConnection(&acceptor_.io_service())); - acceptor_.async_accept(newConnection->getSocket(), + boost::shared_ptr<BoostConnection> newConnection(new BoostConnection(&acceptor_->io_service())); + acceptor_->async_accept(newConnection->getSocket(), boost::bind(&BoostConnectionServer::handleAccept, shared_from_this(), newConnection, boost::asio::placeholders::error)); } void BoostConnectionServer::handleAccept(boost::shared_ptr<BoostConnection> newConnection, const boost::system::error_code& error) { - if (!error) { - MainEventLoop::postEvent(boost::bind(boost::ref(onNewConnection), newConnection), shared_from_this()); + if (error) { + MainEventLoop::postEvent( + boost::bind( + &BoostConnectionServer::stop, shared_from_this(), UnknownError), + shared_from_this()); + } + else { + MainEventLoop::postEvent( + boost::bind(boost::ref(onNewConnection), newConnection), + shared_from_this()); newConnection->listen(); acceptNextConnection(); } diff --git a/Swiften/Network/BoostConnectionServer.h b/Swiften/Network/BoostConnectionServer.h index c92318e..d8e5eb4 100644 --- a/Swiften/Network/BoostConnectionServer.h +++ b/Swiften/Network/BoostConnectionServer.h @@ -3,6 +3,7 @@ #include <boost/shared_ptr.hpp> #include <boost/enable_shared_from_this.hpp> #include <boost/asio.hpp> +#include <boost/signal.hpp> #include "Swiften/Network/BoostConnection.h" #include "Swiften/Network/ConnectionServer.h" @@ -11,15 +12,25 @@ namespace Swift { class BoostConnectionServer : public ConnectionServer, public EventOwner, public boost::enable_shared_from_this<BoostConnectionServer> { public: + enum Error { + Conflict, + UnknownError + }; BoostConnectionServer(int port, boost::asio::io_service* ioService); void start(); + void stop(); + + boost::signal<void (boost::optional<Error>)> onStopped; private: + void stop(boost::optional<Error> e); void acceptNextConnection(); void handleAccept(boost::shared_ptr<BoostConnection> newConnection, const boost::system::error_code& error); private: - boost::asio::ip::tcp::acceptor acceptor_; + int port_; + boost::asio::io_service* ioService_; + boost::asio::ip::tcp::acceptor* acceptor_; }; } diff --git a/Swiften/Network/FakeConnection.h b/Swiften/Network/FakeConnection.h new file mode 100644 index 0000000..92a03c3 --- /dev/null +++ b/Swiften/Network/FakeConnection.h @@ -0,0 +1,88 @@ +#pragma once + +#include <boost/optional.hpp> +#include <boost/bind.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <vector> + +#include "Swiften/Network/Connection.h" +#include "Swiften/Network/HostAddressPort.h" +#include "Swiften/EventLoop/EventOwner.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + class FakeConnection : + public Connection, + public EventOwner, + public boost::enable_shared_from_this<FakeConnection> { + public: + enum State { + Initial, + Connecting, + Connected, + Disconnected, + DisconnectedWithError + }; + + FakeConnection() : state(Initial), delayConnect(false) {} + + virtual void listen() { + assert(false); + } + + void setError(const Error& e) { + error = boost::optional<Error>(e); + state = DisconnectedWithError; + if (connectedTo) { + MainEventLoop::postEvent( + boost::bind(boost::ref(onDisconnected), error), + shared_from_this()); + } + } + + virtual void connect(const HostAddressPort& address) { + if (delayConnect) { + state = Connecting; + } + else { + if (!error) { + connectedTo = address; + state = Connected; + } + else { + state = DisconnectedWithError; + } + MainEventLoop::postEvent( + boost::bind(boost::ref(onConnectFinished), error), + shared_from_this()); + } + } + + virtual void disconnect() { + if (!error) { + state = Disconnected; + } + else { + state = DisconnectedWithError; + } + connectedTo.reset(); + MainEventLoop::postEvent( + boost::bind(boost::ref(onDisconnected), error), + shared_from_this()); + } + + virtual void write(const ByteArray& data) { + dataWritten.push_back(data); + } + + void setDelayConnect() { + delayConnect = true; + } + + boost::optional<HostAddressPort> connectedTo; + std::vector<ByteArray> dataWritten; + boost::optional<Error> error; + State state; + bool delayConnect; + }; +} diff --git a/Swiften/Network/HostAddress.cpp b/Swiften/Network/HostAddress.cpp index 84a0012..ea324cb 100644 --- a/Swiften/Network/HostAddress.cpp +++ b/Swiften/Network/HostAddress.cpp @@ -1,10 +1,14 @@ #include "Swiften/Network/HostAddress.h" #include <boost/numeric/conversion/cast.hpp> +#include <boost/lexical_cast.hpp> #include <cassert> #include <sstream> #include <iomanip> +#include "Swiften/Base/foreach.h" +#include "Swiften/Base/String.h" + namespace Swift { HostAddress::HostAddress() { @@ -13,6 +17,14 @@ HostAddress::HostAddress() { } } +HostAddress::HostAddress(const String& address) { + std::vector<String> components = address.split('.'); + assert(components.size() == 4); + foreach(const String& component, components) { + address_.push_back(boost::lexical_cast<int>(component.getUTF8String())); + } +} + HostAddress::HostAddress(const unsigned char* address, int length) { assert(length == 4 || length == 16); address_.reserve(length); diff --git a/Swiften/Network/HostAddress.h b/Swiften/Network/HostAddress.h index 2c9760d..fa34df4 100644 --- a/Swiften/Network/HostAddress.h +++ b/Swiften/Network/HostAddress.h @@ -1,13 +1,15 @@ -#ifndef SWIFTEN_HOSTADDRESS -#define SWIFTEN_HOSTADDRESS +#pragma once #include <string> #include <vector> namespace Swift { + class String; + class HostAddress { public: HostAddress(); + HostAddress(const String&); HostAddress(const unsigned char* address, int length); const std::vector<unsigned char>& getRawAddress() const { @@ -20,5 +22,3 @@ namespace Swift { std::vector<unsigned char> address_; }; } - -#endif diff --git a/Swiften/Network/UnitTest/HostAddressTest.cpp b/Swiften/Network/UnitTest/HostAddressTest.cpp index b805647..50e9198 100644 --- a/Swiften/Network/UnitTest/HostAddressTest.cpp +++ b/Swiften/Network/UnitTest/HostAddressTest.cpp @@ -2,18 +2,23 @@ #include <cppunit/extensions/TestFactoryRegistry.h> #include "Swiften/Network/HostAddress.h" +#include "Swiften/Base/String.h" using namespace Swift; -class HostAddressTest : public CppUnit::TestFixture -{ +class HostAddressTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(HostAddressTest); + CPPUNIT_TEST(testConstructor); CPPUNIT_TEST(testToString); CPPUNIT_TEST(testToString_IPv6); CPPUNIT_TEST_SUITE_END(); public: - HostAddressTest() {} + void testConstructor() { + HostAddress testling("192.168.1.254"); + + CPPUNIT_ASSERT_EQUAL(std::string("192.168.1.254"), testling.toString()); + } void testToString() { unsigned char address[4] = {10, 0, 1, 253}; diff --git a/Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp b/Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp new file mode 100644 index 0000000..a5c51aa --- /dev/null +++ b/Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp @@ -0,0 +1,72 @@ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <boost/shared_ptr.hpp> + +#include "Swiften/Base/String.h" +#include "Swiften/Network/BoostConnectionServer.h" +#include "Swiften/Network/BoostIOServiceThread.h" +#include "Swiften/EventLoop/DummyEventLoop.h" + +using namespace Swift; + +class BoostConnectionServerTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(BoostConnectionServerTest); + CPPUNIT_TEST(testConstructor_TwoServersOnSamePort); + CPPUNIT_TEST(testStart_Conflict); + CPPUNIT_TEST(testStop); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + boostIOServiceThread_ = new BoostIOServiceThread(); + eventLoop_ = new DummyEventLoop(); + stopped = false; + stoppedError.reset(); + } + + void tearDown() { + delete eventLoop_; + delete boostIOServiceThread_; + } + + void testConstructor_TwoServersOnSamePort() { + boost::shared_ptr<BoostConnectionServer> testling(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService())); + boost::shared_ptr<BoostConnectionServer> testling2(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService())); + } + + void testStart_Conflict() { + boost::shared_ptr<BoostConnectionServer> testling(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService())); + testling->start(); + + boost::shared_ptr<BoostConnectionServer> testling2(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService())); + testling2->onStopped.connect( + boost::bind(&BoostConnectionServerTest::handleStopped, this, _1)); + + testling->stop(); + } + + void testStop() { + boost::shared_ptr<BoostConnectionServer> testling(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService())); + testling->start(); + + testling->stop(); + + boost::shared_ptr<BoostConnectionServer> testling2(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService())); + testling2->start(); + + testling2->stop(); + } + + void handleStopped(boost::optional<BoostConnectionServer::Error> e) { + stopped = true; + stoppedError = e; + } + + private: + BoostIOServiceThread* boostIOServiceThread_; + DummyEventLoop* eventLoop_; + bool stopped; + boost::optional<BoostConnectionServer::Error> stoppedError; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(BoostConnectionServerTest); diff --git a/Swiften/QA/NetworkTest/Makefile.inc b/Swiften/QA/NetworkTest/Makefile.inc index b263cdb..57c97a1 100644 --- a/Swiften/QA/NetworkTest/Makefile.inc +++ b/Swiften/QA/NetworkTest/Makefile.inc @@ -2,6 +2,7 @@ NETWORKTEST_TARGET += Swiften/QA/NetworkTest/checker NETWORKTEST_SOURCES += \ Swiften/QA/NetworkTest/DomainNameResolverTest.cpp \ Swiften/QA/NetworkTest/BoostConnectionTest.cpp \ + Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp \ QA/UnitTest/checker.cpp NETWORKTEST_OBJECTS = \ $(NETWORKTEST_SOURCES:.cpp=.o) |