summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Slimber/Cocoa/CocoaAction.h20
-rw-r--r--Slimber/Cocoa/CocoaAction.mm22
-rw-r--r--Slimber/Cocoa/CocoaController.h11
-rw-r--r--Slimber/Cocoa/CocoaController.mm19
-rw-r--r--Slimber/Cocoa/CocoaMenulet.h26
-rw-r--r--Slimber/Cocoa/CocoaMenulet.mm79
-rw-r--r--Slimber/Cocoa/Info.plist5
-rw-r--r--Slimber/Cocoa/MainController.h9
-rw-r--r--Slimber/Cocoa/MainController.mm15
-rw-r--r--Slimber/Cocoa/MainMenu.xib6
-rw-r--r--Slimber/Cocoa/Makefile.inc13
-rw-r--r--Slimber/Cocoa/Menulet.h16
-rw-r--r--Slimber/Cocoa/Menulet.m104
-rw-r--r--Slimber/Cocoa/Slimber.h28
-rw-r--r--Slimber/Cocoa/Slimber.mm61
-rw-r--r--Slimber/LinkLocalPresenceManager.cpp19
-rw-r--r--Slimber/LinkLocalPresenceManager.h5
-rw-r--r--Slimber/MainController.cpp123
-rw-r--r--Slimber/MainController.h39
-rw-r--r--Slimber/Makefile.inc5
-rw-r--r--Slimber/Menulet.cpp4
-rw-r--r--Slimber/Menulet.h20
-rw-r--r--Slimber/MenuletController.cpp51
-rw-r--r--Slimber/MenuletController.h33
-rw-r--r--Slimber/Qt/QtMenulet.h65
-rw-r--r--Slimber/Qt/Slimber.pro24
-rw-r--r--Slimber/Qt/Slimber.qrc9
-rw-r--r--Slimber/Qt/main.cpp19
-rw-r--r--Slimber/Resources/Icon.svg2
-rw-r--r--Slimber/Resources/Slimber.icnsbin0 -> 77315 bytes
-rw-r--r--Slimber/Server.cpp170
-rw-r--r--Slimber/Server.h38
-rw-r--r--Slimber/ServerError.h31
-rw-r--r--Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp23
-rw-r--r--Slimber/UnitTest/Makefile.inc3
-rw-r--r--Slimber/UnitTest/MenuletControllerTest.cpp151
-rw-r--r--Swiften/Base/String.cpp17
-rw-r--r--Swiften/Base/String.h2
-rw-r--r--Swiften/Base/UnitTest/StringTest.cpp10
-rw-r--r--Swiften/EventLoop/DummyEventLoop.h20
-rw-r--r--Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h2
-rw-r--r--Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h2
-rw-r--r--Swiften/LinkLocal/DNSSD/DNSSDQuerier.h3
-rw-r--r--Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp25
-rw-r--r--Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h8
-rw-r--r--Swiften/LinkLocal/DNSSD/Makefile.inc3
-rw-r--r--Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp25
-rw-r--r--Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h12
-rw-r--r--Swiften/LinkLocal/LinkLocalConnector.cpp34
-rw-r--r--Swiften/LinkLocal/LinkLocalConnector.h19
-rw-r--r--Swiften/LinkLocal/LinkLocalService.cpp4
-rw-r--r--Swiften/LinkLocal/LinkLocalService.h3
-rw-r--r--Swiften/LinkLocal/LinkLocalServiceBrowser.h5
-rw-r--r--Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp135
-rw-r--r--Swiften/LinkLocal/UnitTest/Makefile.inc3
-rw-r--r--Swiften/Network/BoostConnectionServer.cpp48
-rw-r--r--Swiften/Network/BoostConnectionServer.h13
-rw-r--r--Swiften/Network/FakeConnection.h88
-rw-r--r--Swiften/Network/HostAddress.cpp12
-rw-r--r--Swiften/Network/HostAddress.h8
-rw-r--r--Swiften/Network/UnitTest/HostAddressTest.cpp11
-rw-r--r--Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp72
-rw-r--r--Swiften/QA/NetworkTest/Makefile.inc1
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
new file mode 100644
index 0000000..5deeb40
--- /dev/null
+++ b/Slimber/Resources/Slimber.icns
Binary files differ
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)