summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--BuildTools/SCons/Tools/AppBundle.py14
-rw-r--r--Slimber/Cocoa/CocoaMenulet.h2
-rw-r--r--Slimber/Cocoa/SConscript1
-rw-r--r--SwifTools/Cocoa/CocoaAction.h (renamed from Slimber/Cocoa/CocoaAction.h)0
-rw-r--r--SwifTools/Cocoa/CocoaAction.mm (renamed from Slimber/Cocoa/CocoaAction.mm)8
-rw-r--r--SwifTools/Cocoa/SConscript8
-rw-r--r--SwifTools/Linkify.cpp2
-rw-r--r--SwifTools/SConscript2
-rw-r--r--SwifTools/URIHandler/MacOSXURIHandler.h24
-rw-r--r--SwifTools/URIHandler/MacOSXURIHandler.mm59
-rw-r--r--SwifTools/URIHandler/MacOSXURIHandlerHelpers.h11
-rw-r--r--SwifTools/URIHandler/MacOSXURIHandlerHelpers.mm16
-rw-r--r--SwifTools/URIHandler/NullURIHandler.h20
-rw-r--r--SwifTools/URIHandler/SConscript23
-rw-r--r--SwifTools/URIHandler/URIHandler.cpp15
-rw-r--r--SwifTools/URIHandler/URIHandler.h20
-rw-r--r--SwifTools/URIHandler/UnitTest/XMPPURITest.cpp191
-rw-r--r--SwifTools/URIHandler/XMPPURI.cpp144
-rw-r--r--SwifTools/URIHandler/XMPPURI.h72
-rw-r--r--SwifTools/UnitTest/SConscript2
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp4
-rw-r--r--Swift/Controllers/MainController.cpp6
-rw-r--r--Swift/Controllers/MainController.h5
-rw-r--r--Swift/Controllers/SConscript1
-rw-r--r--Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h13
-rw-r--r--Swift/Controllers/XMPPURIController.cpp38
-rw-r--r--Swift/Controllers/XMPPURIController.h32
-rw-r--r--Swift/QtUI/QtSwift.cpp22
-rw-r--r--Swift/QtUI/QtSwift.h2
-rw-r--r--Swift/QtUI/QtURIHandler.cpp36
-rw-r--r--Swift/QtUI/QtURIHandler.h28
-rw-r--r--Swift/QtUI/SConscript3
32 files changed, 809 insertions, 15 deletions
diff --git a/BuildTools/SCons/Tools/AppBundle.py b/BuildTools/SCons/Tools/AppBundle.py
index c271575..6a343f6 100644
--- a/BuildTools/SCons/Tools/AppBundle.py
+++ b/BuildTools/SCons/Tools/AppBundle.py
@@ -1,7 +1,7 @@
import SCons.Util, os.path
def generate(env) :
- def createAppBundle(env, bundle, version = "1.0", resources = [], frameworks = [], info = {}) :
+ def createAppBundle(env, bundle, version = "1.0", resources = [], frameworks = [], info = {}, handlesXMPPURIs = False) :
bundleDir = bundle + ".app"
bundleContentsDir = bundleDir + "/Contents"
resourcesDir = bundleContentsDir + "/Resources"
@@ -32,6 +32,18 @@ def generate(env) :
for key, value in infoDict.items() :
plist += "<key>" + key + "</key>\n"
plist += "<string>" + value.encode("utf-8") + "</string>\n"
+ if handlesXMPPURIs :
+ plist += """<key>CFBundleURLTypes</key>
+<array>
+ <dict>
+ <key>CFBundleURLName</key>
+ <string>XMPP URL</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>xmpp</string>
+ </array>
+ </dict>
+</array>\n"""
plist += """</dict>
</plist>
"""
diff --git a/Slimber/Cocoa/CocoaMenulet.h b/Slimber/Cocoa/CocoaMenulet.h
index 7f2758b..5c7c33e 100644
--- a/Slimber/Cocoa/CocoaMenulet.h
+++ b/Slimber/Cocoa/CocoaMenulet.h
@@ -9,7 +9,7 @@
#include <Cocoa/Cocoa.h>
#include "Slimber/Menulet.h"
-#include "Slimber/Cocoa/CocoaAction.h"
+#include <SwifTools/Cocoa/CocoaAction.h>
class CocoaMenulet : public Menulet {
public:
diff --git a/Slimber/Cocoa/SConscript b/Slimber/Cocoa/SConscript
index e2d8221..d664846 100644
--- a/Slimber/Cocoa/SConscript
+++ b/Slimber/Cocoa/SConscript
@@ -16,7 +16,6 @@ myenv.Program("Slimber", [
"main.mm",
"CocoaController.mm",
"CocoaMenulet.mm",
- "CocoaAction.mm"
])
myenv.Nib("MainMenu")
diff --git a/Slimber/Cocoa/CocoaAction.h b/SwifTools/Cocoa/CocoaAction.h
index a46ef7c..a46ef7c 100644
--- a/Slimber/Cocoa/CocoaAction.h
+++ b/SwifTools/Cocoa/CocoaAction.h
diff --git a/Slimber/Cocoa/CocoaAction.mm b/SwifTools/Cocoa/CocoaAction.mm
index 15498a1..d560787 100644
--- a/Slimber/Cocoa/CocoaAction.mm
+++ b/SwifTools/Cocoa/CocoaAction.mm
@@ -1,4 +1,10 @@
-#include "Slimber/Cocoa/CocoaAction.h"
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <SwifTools/Cocoa/CocoaAction.h>
@implementation CocoaAction
diff --git a/SwifTools/Cocoa/SConscript b/SwifTools/Cocoa/SConscript
new file mode 100644
index 0000000..4ae4a07
--- /dev/null
+++ b/SwifTools/Cocoa/SConscript
@@ -0,0 +1,8 @@
+Import("swiftools_env", "env")
+
+sources = []
+if swiftools_env["PLATFORM"] == "darwin" and swiftools_env["target"] == "native" :
+ sources += ["CocoaAction.mm"]
+
+objects = swiftools_env.StaticObject(sources)
+swiftools_env.Append(SWIFTOOLS_OBJECTS = [objects])
diff --git a/SwifTools/Linkify.cpp b/SwifTools/Linkify.cpp
index 91c713f..a5deccb 100644
--- a/SwifTools/Linkify.cpp
+++ b/SwifTools/Linkify.cpp
@@ -12,7 +12,7 @@
namespace Swift {
-static boost::regex linkifyRegexp("^https?://.*");
+static boost::regex linkifyRegexp("^(https?://|xmpp:).*");
std::string Linkify::linkify(const std::string& input) {
std::ostringstream result;
diff --git a/SwifTools/SConscript b/SwifTools/SConscript
index d4747db..8d00418 100644
--- a/SwifTools/SConscript
+++ b/SwifTools/SConscript
@@ -50,8 +50,10 @@ if env["SCONS_STAGE"] == "build" :
"Application",
"Dock",
"Notifier",
+ "URIHandler",
"Idle/IdleQuerierTest",
"Idle/UnitTest",
+ "Cocoa",
"UnitTest"
])
diff --git a/SwifTools/URIHandler/MacOSXURIHandler.h b/SwifTools/URIHandler/MacOSXURIHandler.h
new file mode 100644
index 0000000..f803420
--- /dev/null
+++ b/SwifTools/URIHandler/MacOSXURIHandler.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <SwifTools/URIHandler/URIHandler.h>
+
+namespace Swift {
+ class MacOSXURIHandler : public URIHandler {
+ public:
+ MacOSXURIHandler();
+ virtual ~MacOSXURIHandler();
+
+ virtual void start();
+ virtual void stop();
+
+ private:
+ class Private;
+ Private* p;
+ };
+}
diff --git a/SwifTools/URIHandler/MacOSXURIHandler.mm b/SwifTools/URIHandler/MacOSXURIHandler.mm
new file mode 100644
index 0000000..3542e2f
--- /dev/null
+++ b/SwifTools/URIHandler/MacOSXURIHandler.mm
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <SwifTools/URIHandler/MacOSXURIHandler.h>
+
+#include <Cocoa/Cocoa.h>
+#include <iostream>
+
+using namespace Swift;
+
+@interface MacOSXURIEventHandler : NSObject {
+ URIHandler* handler;
+}
+- (id) initWithHandler: (URIHandler*) handler;
+- (void) getUrl: (NSAppleEventDescriptor*) event withReplyEvent: (NSAppleEventDescriptor*) replyEvent;
+
+@end
+@implementation MacOSXURIEventHandler
+ - (id) initWithHandler: (URIHandler*) h {
+ if ([super init]) {
+ handler = h;
+ }
+ return self;
+ }
+
+ - (void) getUrl: (NSAppleEventDescriptor*) event withReplyEvent: (NSAppleEventDescriptor*) replyEvent {
+ (void) replyEvent;
+ NSString* url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+ handler->onURI(std::string([url UTF8String]));
+ }
+@end
+
+class MacOSXURIHandler::Private {
+ public:
+ MacOSXURIEventHandler* eventHandler;
+};
+
+MacOSXURIHandler::MacOSXURIHandler() {
+ p = new Private();
+ p->eventHandler = [[MacOSXURIEventHandler alloc] initWithHandler: this];
+}
+
+MacOSXURIHandler::~MacOSXURIHandler() {
+ [p->eventHandler release];
+ delete p;
+}
+
+void MacOSXURIHandler::start() {
+ [[NSAppleEventManager sharedAppleEventManager] setEventHandler:p->eventHandler andSelector:@selector(getUrl:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
+ NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier];
+ LSSetDefaultHandlerForURLScheme((CFStringRef)@"xmpp", (CFStringRef)bundleID);
+}
+
+void MacOSXURIHandler::stop() {
+ [[NSAppleEventManager sharedAppleEventManager] removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
+}
diff --git a/SwifTools/URIHandler/MacOSXURIHandlerHelpers.h b/SwifTools/URIHandler/MacOSXURIHandlerHelpers.h
new file mode 100644
index 0000000..5a2db7a
--- /dev/null
+++ b/SwifTools/URIHandler/MacOSXURIHandlerHelpers.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+namespace Swift {
+ void registerAppAsDefaultXMPPURIHandler();
+}
diff --git a/SwifTools/URIHandler/MacOSXURIHandlerHelpers.mm b/SwifTools/URIHandler/MacOSXURIHandlerHelpers.mm
new file mode 100644
index 0000000..dca91b8
--- /dev/null
+++ b/SwifTools/URIHandler/MacOSXURIHandlerHelpers.mm
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <SwifTools/URIHandler/MacOSXURIHandlerHelpers.h>
+
+#include <Cocoa/Cocoa.h>
+
+namespace Swift {
+ void registerAppAsDefaultXMPPURIHandler() {
+ NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier];
+ LSSetDefaultHandlerForURLScheme((CFStringRef)@"xmpp", (CFStringRef)bundleID);
+ }
+}
diff --git a/SwifTools/URIHandler/NullURIHandler.h b/SwifTools/URIHandler/NullURIHandler.h
new file mode 100644
index 0000000..28c35bb
--- /dev/null
+++ b/SwifTools/URIHandler/NullURIHandler.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <SwifTools/URIHandler/URIHandler.h>
+
+namespace Swift {
+ class NullURIHandler : public URIHandler {
+ public:
+ virtual void start() {
+ }
+
+ virtual void stop() {
+ }
+ };
+}
diff --git a/SwifTools/URIHandler/SConscript b/SwifTools/URIHandler/SConscript
new file mode 100644
index 0000000..42c6ca8
--- /dev/null
+++ b/SwifTools/URIHandler/SConscript
@@ -0,0 +1,23 @@
+Import("swiftools_env", "env")
+
+sources = [
+ "XMPPURI.cpp",
+ "URIHandler.cpp",
+ ]
+
+if swiftools_env["PLATFORM"] == "darwin" and swiftools_env["target"] == "native" :
+ sources += [
+ "MacOSXURIHandler.mm",
+ "MacOSXURIHandlerHelpers.mm",
+ ]
+elif swiftools_env["PLATFORM"] == "win32" :
+ sources += []
+else :
+ sources += []
+
+objects = swiftools_env.StaticObject(sources)
+swiftools_env.Append(SWIFTOOLS_OBJECTS = [objects])
+
+env.Append(UNITTEST_SOURCES = [
+ File("UnitTest/XMPPURITest.cpp"),
+ ])
diff --git a/SwifTools/URIHandler/URIHandler.cpp b/SwifTools/URIHandler/URIHandler.cpp
new file mode 100644
index 0000000..91e54e5
--- /dev/null
+++ b/SwifTools/URIHandler/URIHandler.cpp
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <SwifTools/URIHandler/URIHandler.h>
+
+using namespace Swift;
+
+URIHandler::URIHandler() {
+}
+
+URIHandler::~URIHandler() {
+}
diff --git a/SwifTools/URIHandler/URIHandler.h b/SwifTools/URIHandler/URIHandler.h
new file mode 100644
index 0000000..9dd13a8
--- /dev/null
+++ b/SwifTools/URIHandler/URIHandler.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <Swiften/Base/boost_bsignals.h>
+
+namespace Swift {
+ class URIHandler {
+ public:
+ URIHandler();
+ virtual ~URIHandler();
+
+ boost::signal<void (const std::string&)> onURI;
+ };
+}
diff --git a/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp b/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp
new file mode 100644
index 0000000..8d03b60
--- /dev/null
+++ b/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <SwifTools/URIHandler/XMPPURI.h>
+
+using namespace Swift;
+
+class XMPPURITest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(XMPPURITest);
+ CPPUNIT_TEST(testFromString_Authority);
+ CPPUNIT_TEST(testFromString_AuthorityWithPath);
+ CPPUNIT_TEST(testFromString_AuthorityWithFragment);
+ CPPUNIT_TEST(testFromString_AuthorityWithPathAndFragment);
+ CPPUNIT_TEST(testFromString_AuthorityWithIntlChars);
+ CPPUNIT_TEST(testFromString_AuthorityWithQueryWithoutParameters);
+ CPPUNIT_TEST(testFromString_AuthorityWithQueryWithParameters);
+ CPPUNIT_TEST(testFromString_AuthorityWithQueryWithoutParametersWithFragment);
+ CPPUNIT_TEST(testFromString_AuthorityWithQueryWithParametersWithFragment);
+ CPPUNIT_TEST(testFromString_Path);
+ CPPUNIT_TEST(testFromString_PathWithFragment);
+ CPPUNIT_TEST(testFromString_PathWithIntlChars);
+ CPPUNIT_TEST(testFromString_PathWithInvalidEscapedChar);
+ CPPUNIT_TEST(testFromString_PathWithIncompleteEscapedChar);
+ CPPUNIT_TEST(testFromString_PathWithIncompleteEscapedChar2);
+ CPPUNIT_TEST(testFromString_PathWithQueryWithoutParameters);
+ CPPUNIT_TEST(testFromString_PathWithQueryWithParameters);
+ CPPUNIT_TEST(testFromString_PathWithQueryWithoutParametersWithFragment);
+ CPPUNIT_TEST(testFromString_PathWithQueryWithParametersWithFragment);
+ CPPUNIT_TEST(testFromString_NoPrefix);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testFromString_Authority() {
+ XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com");
+
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority());
+ }
+
+ void testFromString_AuthorityWithPath() {
+ XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com/baz@example.com");
+
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority());
+ CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath());
+ }
+
+ void testFromString_AuthorityWithFragment() {
+ XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com#myfragment");
+
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority());
+ CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment());
+ }
+
+ void testFromString_AuthorityWithPathAndFragment() {
+ XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com/baz@example.com#myfragment");
+
+ CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority());
+ CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath());
+ CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment());
+ }
+
+ void testFromString_AuthorityWithIntlChars() {
+ XMPPURI testling = XMPPURI::fromString("xmpp://nasty!%23$%25()*+,-.;=\%3F\%5B\%5C\%5D\%5E_\%60\%7B\%7C\%7D~node@example.com");
+
+ CPPUNIT_ASSERT_EQUAL(JID("nasty!#$\%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getAuthority());
+ }
+
+ void testFromString_AuthorityWithQueryWithoutParameters() {
+ XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message");
+
+ CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority());
+ CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType());
+ }
+
+ void testFromString_AuthorityWithQueryWithParameters() {
+ XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message");
+
+ CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority());
+ CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType());
+ CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body"));
+ }
+
+ void testFromString_AuthorityWithQueryWithoutParametersWithFragment() {
+ XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message#myfragment");
+
+ CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority());
+ CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType());
+ CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment());
+ }
+
+ void testFromString_AuthorityWithQueryWithParametersWithFragment() {
+ XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message#myfragment");
+
+ CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority());
+ CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType());
+ CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body"));
+ CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment());
+ }
+
+ void testFromString_Path() {
+ XMPPURI testling = XMPPURI::fromString("xmpp:baz@example.com");
+
+ CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath());
+ }
+
+ void testFromString_PathWithFragment() {
+ XMPPURI testling = XMPPURI::fromString("xmpp:baz@example.com#myfragment");
+
+ CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath());
+ CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment());
+ }
+
+ void testFromString_PathWithIntlChars() {
+ XMPPURI testling = XMPPURI::fromString("xmpp:nasty!%23$%25()*+,-.;=\%3F\%5B\%5C\%5D\%5E_\%60\%7B\%7C\%7D~node@example.com");
+
+ CPPUNIT_ASSERT_EQUAL(JID("nasty!#$\%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getPath());
+ }
+
+ void testFromString_PathWithInvalidEscapedChar() {
+ XMPPURI testling = XMPPURI::fromString("xmpp:test%%@example.com");
+
+ CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath());
+ }
+
+ void testFromString_PathWithIncompleteEscapedChar() {
+ XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com%");
+
+ CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath());
+ }
+
+ void testFromString_PathWithIncompleteEscapedChar2() {
+ XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com%1");
+
+ CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath());
+ }
+
+ void testFromString_PathWithQueryWithoutParameters() {
+ XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message");
+
+ CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath());
+ CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType());
+ }
+
+ void testFromString_PathWithQueryWithParameters() {
+ XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message");
+
+ CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath());
+ CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType());
+ CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body"));
+ }
+
+ void testFromString_PathWithQueryWithoutParametersWithFragment() {
+ XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message#myfragment");
+
+ CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath());
+ CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType());
+ CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment());
+ }
+
+ void testFromString_PathWithQueryWithParametersWithFragment() {
+ XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message#myfragment");
+
+ CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath());
+ CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType());
+ CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject"));
+ CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body"));
+ CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment());
+ }
+
+ void testFromString_NoPrefix() {
+ XMPPURI testling = XMPPURI::fromString("baz@example.com");
+
+ CPPUNIT_ASSERT(testling.isNull());
+ }
+
+ private:
+ std::string get(const std::map<std::string, std::string>& m, const std::string& k) {
+ std::map<std::string, std::string>::const_iterator i = m.find(k);
+ return i == m.end() ? "" : i->second;
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(XMPPURITest);
diff --git a/SwifTools/URIHandler/XMPPURI.cpp b/SwifTools/URIHandler/XMPPURI.cpp
new file mode 100644
index 0000000..d9fde07
--- /dev/null
+++ b/SwifTools/URIHandler/XMPPURI.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <SwifTools/URIHandler/XMPPURI.h>
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/find_format.hpp>
+#include <boost/algorithm/string/formatter.hpp>
+#include <boost/algorithm/string/find_iterator.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
+#include <sstream>
+#include <stdexcept>
+#include <vector>
+
+using namespace Swift;
+
+namespace {
+
+ struct PercentEncodedCharacterFinder {
+ template<typename Iterator>
+ boost::iterator_range<Iterator> operator()(Iterator begin, Iterator end) {
+ boost::iterator_range<Iterator> r = boost::first_finder("%")(begin, end);
+ if (r.end() == end) {
+ return r;
+ }
+ else {
+ if (r.end() + 1 == end || r.end() + 2 == end) {
+ throw std::runtime_error("Incomplete escape character");
+ }
+ else {
+ r.advance_end(2);
+ return r;
+ }
+ }
+ }
+ };
+
+ struct PercentUnencodeFormatter {
+ template<typename FindResult>
+ std::string operator()(const FindResult& match) const {
+ std::stringstream s;
+ s << std::hex << std::string(match.begin() + 1, match.end());
+ unsigned int value;
+ s >> value;
+ if (s.fail() || s.bad()) {
+ throw std::runtime_error("Invalid escape character");
+ }
+ return std::string(reinterpret_cast<const char*>(&value), 1);
+ }
+ };
+
+
+ std::string unescape(const std::string& s) {
+ try {
+ return boost::find_format_all_copy(s, PercentEncodedCharacterFinder(), PercentUnencodeFormatter());
+ }
+ catch (const std::exception&) {
+ return "";
+ }
+ }
+}
+
+XMPPURI::XMPPURI() {
+}
+
+XMPPURI XMPPURI::fromString(const std::string& s) {
+ XMPPURI result;
+ if (boost::starts_with(s, "xmpp:")) {
+ std::string uri = s.substr(5, s.npos);
+ bool parsePath = true;
+ bool parseQuery = true;
+ bool parseFragment = true;
+
+ // Parse authority
+ if (boost::starts_with(uri, "//")) {
+ size_t i = uri.find_first_of("/#?", 2);
+ result.setAuthority(JID(unescape(uri.substr(2, i - 2))));
+ if (i == uri.npos) {
+ uri = "";
+ parsePath = parseQuery = parseFragment = false;
+ }
+ else {
+ if (uri[i] == '?') {
+ parsePath = false;
+ }
+ else if (uri[i] == '#') {
+ parseQuery = parsePath = false;
+ }
+ uri = uri.substr(i + 1, uri.npos);
+ }
+ }
+
+ // Parse path
+ if (parsePath) {
+ size_t i = uri.find_first_of("#?");
+ result.setPath(JID(unescape(uri.substr(0, i))));
+ if (i == uri.npos) {
+ uri = "";
+ parseQuery = parseFragment = false;
+ }
+ else {
+ if (uri[i] == '#') {
+ parseQuery = false;
+ }
+ uri = uri.substr(i + 1, uri.npos);
+ }
+ }
+
+ // Parse query
+ if (parseQuery) {
+ size_t end = uri.find_first_of("#");
+ std::string query = uri.substr(0, end);
+ bool haveType = false;
+ typedef boost::split_iterator<std::string::iterator> split_iterator;
+ for (split_iterator it = boost::make_split_iterator(query, boost::first_finder(";")); it != split_iterator(); ++it) {
+ if (haveType) {
+ std::vector<std::string> keyValue;
+ boost::split(keyValue, *it, boost::is_any_of("="));
+ if (keyValue.size() == 1) {
+ result.addQueryParameter(unescape(keyValue[0]), "");
+ }
+ else if (keyValue.size() >= 2) {
+ result.addQueryParameter(unescape(keyValue[0]), unescape(keyValue[1]));
+ }
+ }
+ else {
+ result.setQueryType(unescape(boost::copy_range<std::string>(*it)));
+ haveType = true;
+ }
+ }
+ uri = (end == uri.npos ? "" : uri.substr(end + 1, uri.npos));
+ }
+
+ // Parse fragment
+ if (parseFragment) {
+ result.setFragment(unescape(uri));
+ }
+ }
+ return result;
+}
diff --git a/SwifTools/URIHandler/XMPPURI.h b/SwifTools/URIHandler/XMPPURI.h
new file mode 100644
index 0000000..266b79b
--- /dev/null
+++ b/SwifTools/URIHandler/XMPPURI.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <map>
+
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+ class XMPPURI {
+ public:
+ XMPPURI();
+
+ const JID& getAuthority() const {
+ return authority;
+ }
+
+ void setAuthority(const JID& j) {
+ authority = j;
+ }
+
+ const JID& getPath() const {
+ return path;
+ }
+
+ void setPath(const JID& j) {
+ path = j;
+ }
+
+ const std::string& getQueryType() const {
+ return queryType;
+ }
+
+ void setQueryType(const std::string& q) {
+ queryType = q;
+ }
+
+ const std::map<std::string, std::string>& getQueryParameters() const {
+ return queryParameters;
+ }
+
+ void addQueryParameter(const std::string& key, const std::string& path) {
+ queryParameters[key] = path;
+ }
+
+ const std::string& getFragment() const {
+ return fragment;
+ }
+
+ void setFragment(const std::string& f) {
+ fragment = f;
+ }
+
+ bool isNull() const {
+ return !authority.isValid() && !path.isValid();
+ }
+
+ static XMPPURI fromString(const std::string&);
+
+ private:
+ JID authority;
+ JID path;
+ std::string fragment;
+ std::string queryType;
+ std::map<std::string, std::string> queryParameters;
+ };
+}
diff --git a/SwifTools/UnitTest/SConscript b/SwifTools/UnitTest/SConscript
index 8034905..9fd1375 100644
--- a/SwifTools/UnitTest/SConscript
+++ b/SwifTools/UnitTest/SConscript
@@ -2,5 +2,5 @@ Import("env")
env.Append(UNITTEST_SOURCES = [
File("LinkifyTest.cpp"),
- File("TabCompleteTest.cpp")
+ File("TabCompleteTest.cpp"),
])
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 5f51ac6..972501f 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -158,13 +158,13 @@ void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> event) {
else if (JoinMUCUIEvent::ref joinEvent = boost::dynamic_pointer_cast<JoinMUCUIEvent>(event)) {
handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getNick(), false);
}
- else if (boost::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) {
+ else if (boost::shared_ptr<RequestJoinMUCUIEvent> joinEvent = boost::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) {
if (!joinMUCWindow_) {
joinMUCWindow_ = joinMUCWindowFactory_->createJoinMUCWindow();
joinMUCWindow_->onJoinMUC.connect(boost::bind(&ChatsManager::handleJoinMUCRequest, this, _1, _2, _3));
joinMUCWindow_->onSearchMUC.connect(boost::bind(&ChatsManager::handleSearchMUCRequest, this));
}
- joinMUCWindow_->setMUC("");
+ joinMUCWindow_->setMUC(joinEvent->getRoom());
joinMUCWindow_->setNick(nickResolver_->jidToNick(jid_));
joinMUCWindow_->show();
}
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 9a35cc1..3f86b43 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -65,6 +65,7 @@
#include "Swiften/Network/NetworkFactories.h"
#include <Swift/Controllers/ProfileController.h>
#include <Swift/Controllers/ContactEditController.h>
+#include <Swift/Controllers/XMPPURIController.h>
namespace Swift {
@@ -84,6 +85,7 @@ MainController::MainController(
CertificateStorageFactory* certificateStorageFactory,
Dock* dock,
Notifier* notifier,
+ URIHandler* uriHandler,
bool useDelayForLatency) :
eventLoop_(eventLoop),
networkFactories_(networkFactories),
@@ -92,6 +94,7 @@ MainController::MainController(
storagesFactory_(storagesFactory),
certificateStorageFactory_(certificateStorageFactory),
settings_(settings),
+ uriHandler_(uriHandler),
loginWindow_(NULL) ,
useDelayForLatency_(useDelayForLatency) {
storages_ = NULL;
@@ -121,6 +124,8 @@ MainController::MainController(
loginWindow_ = uiFactory_->createLoginWindow(uiEventStream_);
soundEventController_ = new SoundEventController(eventController_, soundPlayer, settings, uiEventStream_);
+ xmppURIController_ = new XMPPURIController(uriHandler_, uiEventStream_);
+
std::string selectedLoginJID = settings_->getStringSetting("lastLoginJID");
bool loginAutomatically = settings_->getBoolSetting("loginAutomatically", false);
std::string cachedPassword;
@@ -167,6 +172,7 @@ MainController::~MainController() {
resetClient();
delete xmlConsoleController_;
+ delete xmppURIController_;
delete soundEventController_;
delete systemTrayController_;
delete eventController_;
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index f402f8f..f9722de 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -62,6 +62,8 @@ namespace Swift {
class Storages;
class StoragesFactory;
class NetworkFactories;
+ class URIHandler;
+ class XMPPURIController;
class MainController {
public:
@@ -76,6 +78,7 @@ namespace Swift {
CertificateStorageFactory* certificateStorageFactory,
Dock* dock,
Notifier* notifier,
+ URIHandler* uriHandler,
bool useDelayForLatency);
~MainController();
@@ -123,6 +126,7 @@ namespace Swift {
SettingsProvider *settings_;
ProfileSettingsProvider* profileSettings_;
Dock* dock_;
+ URIHandler* uriHandler_;
TogglableNotifier* notifier_;
PresenceNotifier* presenceNotifier_;
EventNotifier* eventNotifier_;
@@ -139,6 +143,7 @@ namespace Swift {
JID boundJID_;
SystemTrayController* systemTrayController_;
SoundEventController* soundEventController_;
+ XMPPURIController* xmppURIController_;
std::string vCardPhotoHash_;
std::string password_;
std::string certificateFile_;
diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript
index 61da9fb..c523419 100644
--- a/Swift/Controllers/SConscript
+++ b/Swift/Controllers/SConscript
@@ -54,6 +54,7 @@ if env["SCONS_STAGE"] == "build" :
"CertificateFileStorage.cpp",
"StatusUtil.cpp",
"Translator.cpp",
+ "XMPPURIController.cpp",
])
env.Append(UNITTEST_SOURCES = [
diff --git a/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h b/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h
index dd2ff6c..2c7b105 100644
--- a/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h
+++ b/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h
@@ -6,18 +6,25 @@
#pragma once
-#include <boost/optional.hpp>
#include <boost/shared_ptr.hpp>
-
#include <string>
+
#include <Swift/Controllers/UIEvents/UIEvent.h>
+#include <Swiften/JID/JID.h>
namespace Swift {
class RequestJoinMUCUIEvent : public UIEvent {
public:
typedef boost::shared_ptr<RequestJoinMUCUIEvent> ref;
- RequestJoinMUCUIEvent() {
+ RequestJoinMUCUIEvent(const JID& room = JID()) : room(room) {
}
+
+ const JID& getRoom() const {
+ return room;
+ }
+
+ private:
+ JID room;
};
}
diff --git a/Swift/Controllers/XMPPURIController.cpp b/Swift/Controllers/XMPPURIController.cpp
new file mode 100644
index 0000000..00759b8
--- /dev/null
+++ b/Swift/Controllers/XMPPURIController.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swift/Controllers/XMPPURIController.h>
+
+#include <boost/bind.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <SwifTools/URIHandler/URIHandler.h>
+#include <SwifTools/URIHandler/XMPPURI.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
+
+using namespace Swift;
+
+XMPPURIController::XMPPURIController(URIHandler* uriHandler, UIEventStream* uiEventStream) : uriHandler(uriHandler), uiEventStream(uiEventStream) {
+ uriHandler->onURI.connect(boost::bind(&XMPPURIController::handleURI, this, _1));
+}
+
+XMPPURIController::~XMPPURIController() {
+ uriHandler->onURI.disconnect(boost::bind(&XMPPURIController::handleURI, this, _1));
+}
+
+void XMPPURIController::handleURI(const std::string& s) {
+ XMPPURI uri = XMPPURI::fromString(s);
+ if (!uri.isNull()) {
+ if (uri.getQueryType() == "join") {
+ uiEventStream->send(boost::make_shared<RequestJoinMUCUIEvent>(uri.getPath()));
+ }
+ else {
+ uiEventStream->send(boost::make_shared<RequestChatUIEvent>(uri.getPath()));
+ }
+ }
+}
diff --git a/Swift/Controllers/XMPPURIController.h b/Swift/Controllers/XMPPURIController.h
new file mode 100644
index 0000000..54534d4
--- /dev/null
+++ b/Swift/Controllers/XMPPURIController.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <Swiften/Base/boost_bsignals.h>
+
+namespace Swift {
+ class URIHandler;
+ class JID;
+ class UIEventStream;
+
+ class XMPPURIController {
+ public:
+ XMPPURIController(URIHandler* uriHandler, UIEventStream* uiEventStream);
+ ~XMPPURIController();
+
+ boost::signal<void (const JID&)> onStartChat;
+ boost::signal<void (const JID&)> onJoinMUC;
+
+ private:
+ void handleURI(const std::string&);
+
+ private:
+ URIHandler* uriHandler;
+ UIEventStream* uiEventStream;
+ };
+}
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
index d4c306f..d977637 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -32,20 +32,28 @@
#include "Swift/Controllers/BuildVersion.h"
#include "SwifTools/AutoUpdater/AutoUpdater.h"
#include "SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h"
+
#if defined(SWIFTEN_PLATFORM_WINDOWS)
#include "WindowsNotifier.h"
-#endif
-#if defined(HAVE_GROWL)
+#elif defined(HAVE_GROWL)
#include "SwifTools/Notifier/GrowlNotifier.h"
#elif defined(SWIFTEN_PLATFORM_LINUX)
#include "FreeDesktopNotifier.h"
#else
#include "SwifTools/Notifier/NullNotifier.h"
#endif
+
#if defined(SWIFTEN_PLATFORM_MACOSX)
#include "SwifTools/Dock/MacOSXDock.h"
-#endif
+#else
#include "SwifTools/Dock/NullDock.h"
+#endif
+
+#if defined(SWIFTEN_PLATFORM_MACOSX)
+#include "QtURIHandler.h"
+#else
+#include <SwifTools/URIHandler/NullURIHandler.h>
+#endif
namespace Swift{
@@ -123,6 +131,12 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
dock_ = new NullDock();
#endif
+#if defined(SWIFTEN_PLATFORM_MACOSX)
+ uriHandler_ = new QtURIHandler();
+#else
+ uriHandler_ = new NullURIHandler();
+#endif
+
if (splitter_) {
splitter_->show();
}
@@ -145,6 +159,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
certificateStorageFactory_,
dock_,
notifier_,
+ uriHandler_,
options.count("latency-debug") > 0);
mainControllers_.push_back(mainController);
}
@@ -172,6 +187,7 @@ QtSwift::~QtSwift() {
}
delete tabs_;
delete splitter_;
+ delete uriHandler_;
delete dock_;
delete soundPlayer_;
delete chatWindowFactory_;
diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h
index 978fa14..4bf5c97 100644
--- a/Swift/QtUI/QtSwift.h
+++ b/Swift/QtUI/QtSwift.h
@@ -44,6 +44,7 @@ namespace Swift {
class QtMUCSearchWindowFactory;
class QtUserSearchWindowFactory;
class EventLoop;
+ class URIHandler;
class QtSwift : public QObject {
Q_OBJECT
@@ -63,6 +64,7 @@ namespace Swift {
QSplitter* splitter_;
QtSoundPlayer* soundPlayer_;
Dock* dock_;
+ URIHandler* uriHandler_;
QtChatTabs* tabs_;
ApplicationPathProvider* applicationPathProvider_;
StoragesFactory* storagesFactory_;
diff --git a/Swift/QtUI/QtURIHandler.cpp b/Swift/QtUI/QtURIHandler.cpp
new file mode 100644
index 0000000..43f3ed1
--- /dev/null
+++ b/Swift/QtUI/QtURIHandler.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "QtURIHandler.h"
+
+#include <QCoreApplication>
+#include <QFileOpenEvent>
+#include <QUrl>
+
+#include "QtSwiftUtil.h"
+#ifdef Q_WS_MAC
+#include <SwifTools/URIHandler/MacOSXURIHandlerHelpers.h>
+#endif
+
+using namespace Swift;
+
+QtURIHandler::QtURIHandler() {
+ qApp->installEventFilter(this);
+#ifdef Q_WS_MAC
+ registerAppAsDefaultXMPPURIHandler();
+#endif
+}
+
+bool QtURIHandler::eventFilter(QObject*, QEvent* event) {
+ if (event->type() == QEvent::FileOpen) {
+ QFileOpenEvent* fileOpenEvent = static_cast<QFileOpenEvent*>(event);
+ if (fileOpenEvent->url().scheme() == "xmpp") {
+ onURI(Q2PSTRING(fileOpenEvent->url().toString()));
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/Swift/QtUI/QtURIHandler.h b/Swift/QtUI/QtURIHandler.h
new file mode 100644
index 0000000..a4b56f8
--- /dev/null
+++ b/Swift/QtUI/QtURIHandler.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2011 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <SwifTools/URIHandler/URIHandler.h>
+
+class QUrl;
+
+namespace Swift {
+ class QtURIHandler : public QObject, public URIHandler {
+ public:
+ QtURIHandler();
+
+ virtual void start() {
+ }
+
+ virtual void stop() {
+ }
+
+ private:
+ bool eventFilter(QObject* obj, QEvent* event);
+ };
+}
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index d8d9abd..e2775a6 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -77,6 +77,7 @@ sources = [
"QtStatusWidget.cpp",
"QtScaledAvatarCache.cpp",
"QtSwift.cpp",
+ "QtURIHandler.cpp",
"QtChatView.cpp",
"QtChatTheme.cpp",
"QtChatTabs.cpp",
@@ -215,7 +216,7 @@ if env["PLATFORM"] == "darwin" :
if env["HAVE_GROWL"] :
frameworks.append(env["GROWL_FRAMEWORK"])
commonResources[""] = commonResources.get("", []) + ["../resources/MacOSX/Swift.icns"]
- app = myenv.AppBundle("Swift", version = myenv["SWIFT_VERSION"], resources = commonResources, frameworks = frameworks)
+ app = myenv.AppBundle("Swift", version = myenv["SWIFT_VERSION"], resources = commonResources, frameworks = frameworks, handlesXMPPURIs = True)
if env["DIST"] :
myenv.Command(["Swift-${SWIFT_VERSION}.dmg"], [app], ["Swift/Packaging/MacOSX/package.sh " + app.path + " Swift/Packaging/MacOSX/Swift.dmg.gz $TARGET $QTDIR"])