diff --git a/.gitignore b/.gitignore
index 84676ed5f330d..60bb5156d5a30 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,6 +45,7 @@ debian/coolwsd.postinst
common/support-public-key.hpp
compile_commands.json
**/gdb.txt
+macos/coolwsd/gmake-wrapper.sh
# Test stuff
systemplate
@@ -97,14 +98,14 @@ browser/compilets
browser/typescript_js
browser/src/layer/tile/CanvasTileWorker.js
-coolforkit
-coolforkitns
-coolforkit-caps
-coolforkit-nocaps
-coolforkit-ns
+/coolforkit
+/coolforkitns
+/coolforkit-caps
+/coolforkit-nocaps
+/coolforkit-ns
connect
lokitclient
-coolwsd
+/coolwsd
loolwsd
coolmount
coolmap
@@ -207,3 +208,12 @@ autogen.input
# SBOM
collabora-online-sbom.spdx.json
+
+# Visual Studio projects for CODA-W
+windows/coda/.vs
+windows/coda/CODA/bin
+windows/coda/CODA/obj
+windows/coda/CODALib/x64
+
+# CODA-W generated files
+windows/coda/config.props
diff --git a/Makefile.am b/Makefile.am
index df41ae1782e24..e17f68d328b29 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -106,7 +106,10 @@ if !ENABLE_DEBUG
AM_CPPFLAGS += -DNDEBUG
endif
-AM_LDFLAGS = -Wl,-E -lpam $(ZLIB_LIBS) $(ZSTD_LIBS) ${PNG_LIBS}
+AM_LDFLAGS = -lpam $(ZLIB_LIBS) $(ZSTD_LIBS) ${PNG_LIBS}
+if !ENABLE_MACOS
+AM_LDFLAGS += -Wl,-E
+endif
# Clang's linker doesn't like -pthread.
if !HAVE_CLANG
@@ -123,6 +126,12 @@ coolwsd_LDADD += ${OPENSSL_LIBS}
coolconfig_LDADD += ${OPENSSL_LIBS}
endif
+if ENABLE_MACOS
+util_platform_cpp = common/Util-macos.cpp
+else
+util_platform_cpp = common/Util-linux.cpp
+endif
+
AM_ETAGSFLAGS = --c++-kinds=+p --fields=+iaS --extra=+q -R --totals=yes --exclude=browser/node_modules --exclude=browser/dist *
AM_CTAGSFLAGS = $(AM_ETAGSFLAGS)
@@ -138,12 +147,14 @@ shared_sources = common/FileUtil.cpp \
common/RegexUtil.cpp \
common/SigUtil-server.cpp \
common/SpookyV2.cpp \
+ common/Syscall.cpp \
common/TraceEvent.cpp \
common/Unit.cpp \
common/Unit-server.cpp \
common/Uri.cpp \
common/Util.cpp \
common/Util-server.cpp \
+ $(util_platform_cpp) \
common/Util-unix.cpp \
common/ConfigUtil.cpp \
common/Authorization.cpp \
@@ -220,6 +231,7 @@ connect_SOURCES = tools/Connect.cpp \
common/StringVector.cpp \
common/Util.cpp \
common/Util-server.cpp \
+ $(util_platform_cpp) \
common/Util-unix.cpp
connect_LDADD = libglobals.a
@@ -232,6 +244,7 @@ lokitclient_SOURCES = common/Log.cpp \
common/TraceEvent.cpp \
common/Util.cpp \
common/Util-server.cpp \
+ $(util_platform_cpp) \
common/Util-unix.cpp
lokitclient_LDADD = libglobals.a
@@ -338,6 +351,7 @@ clientnb_SOURCES = net/clientnb.cpp \
common/StringVector.cpp \
common/Util.cpp \
common/Util-server.cpp \
+ $(util_platform_cpp) \
common/Util-unix.cpp
clientnb_LDADD = libsimd.a libglobals.a
@@ -353,6 +367,7 @@ coolbench_SOURCES = tools/Benchmark.cpp \
common/StringVector.cpp \
common/Util.cpp \
common/Util-server.cpp \
+ $(util_platform_cpp) \
common/Util-unix.cpp \
common/Simd.cpp
@@ -447,6 +462,7 @@ shared_headers = common/Anonymizer.hpp \
common/StringVector.hpp \
common/Seccomp.hpp \
common/Session.hpp \
+ common/Syscall.hpp \
common/Unit.hpp \
common/Uri.hpp \
common/Util.hpp \
diff --git a/android/lib/src/main/cpp/androidapp.cpp b/android/lib/src/main/cpp/androidapp.cpp
index 6133bba8bf8a3..395814d727a40 100644
--- a/android/lib/src/main/cpp/androidapp.cpp
+++ b/android/lib/src/main/cpp/androidapp.cpp
@@ -54,9 +54,9 @@ JNI_OnLoad(JavaVM* vm, void*) {
// Uncomment the following to see the logs from the core too
//setenv("SAL_LOG", "+WARN+INFO", 0);
#if ENABLE_DEBUG
- Log::initialize("Mobile", "debug", false, false, {}, false, {});
+ Log::initialize("Mobile", "debug");
#else
- Log::initialize("Mobile", "information", false, false, {}, false, {});
+ Log::initialize("Mobile", "information");
#endif
return JNI_VERSION_1_6;
}
diff --git a/browser/Makefile.am b/browser/Makefile.am
index d2362b2103601..6d17be179ad8d 100644
--- a/browser/Makefile.am
+++ b/browser/Makefile.am
@@ -690,7 +690,9 @@ define bundle_cool
$(if $(IS_SEPARATE),\
@touch $@,
$(QUIET_M4) m4 -PE -DIOSAPP=$(ENABLE_IOSAPP) \
+ -DMACOSAPP=$(ENABLE_MACOSAPP) \
-DGTKAPP=$(ENABLE_GTKAPP) \
+ -DWINDOWSAPP=$(ENABLE_WINDOWSAPP) \
-DANDROIDAPP=$(ENABLE_ANDROIDAPP) \
-DMOBILEAPPNAME="$(APP_NAME)" \
-DVERSION=$(COOL_VERSION) \
@@ -923,8 +925,10 @@ $(DIST_FOLDER)/cool.html: $(srcdir)/html/cool.html.m4 \
$(QUIET_M4) m4 -PE -I $(INTERMEDIATE_DIR) \
-DBUNDLE=$(IS_BUNDLE) \
-DIOSAPP=$(ENABLE_IOSAPP) \
+ -DMACOSAPP=$(ENABLE_MACOSAPP) \
-DGTKAPP=$(ENABLE_GTKAPP) \
-DDEBUG=$(ENABLE_DEBUG) \
+ -DWINDOWSAPP=$(ENABLE_WINDOWSAPP) \
-DANDROIDAPP=$(ENABLE_ANDROIDAPP) \
-DEMSCRIPTENAPP=$(ENABLE_EMSCRIPTENAPP) \
-DMOBILEAPPNAME="$(APP_NAME)" \
diff --git a/browser/html/cool.html.m4 b/browser/html/cool.html.m4
index 29165b303b21d..a9fc1e89e26c5 100644
--- a/browser/html/cool.html.m4
+++ b/browser/html/cool.html.m4
@@ -9,7 +9,9 @@ m4_dnl------------------------------------------------------------------------
m4_dnl# Define MOBILEAPP as true if this is either for the iOS/Android app or for the gtk+ "app" testbed
m4_define([MOBILEAPP],[])m4_dnl
m4_ifelse(IOSAPP,[true],[m4_define([MOBILEAPP],[true])])m4_dnl
+m4_ifelse(MACOSAPP,[true],[m4_define([MOBILEAPP],[true])])m4_dnl
m4_ifelse(GTKAPP,[true],[m4_define([MOBILEAPP],[true])])m4_dnl
+m4_ifelse(WINDOWSAPP,[true],[m4_define([MOBILEAPP],[true])])m4_dnl
m4_ifelse(ANDROIDAPP,[true],[m4_define([MOBILEAPP],[true])])m4_dnl
m4_dnl
m4_dnl# FIXME: This is temporary and not what we actually eventually want.
@@ -63,7 +65,9 @@ m4_ifelse(MOBILEAPP, [true],
m4_dnl# For use in conditionals in JS:
m4_ifelse(IOSAPP, [true], [])
+m4_ifelse(MACOSAPP, [true], [])
m4_ifelse(GTKAPP, [true], [])
+m4_ifelse(WINDOWSAPP, [true], [])
m4_ifelse(ANDROIDAPP, [true], [])
m4_ifelse(EMSCRIPTENAPP, [true], [])
@@ -74,15 +78,11 @@ m4_ifelse(BUNDLE,[],
m4_foreachq([fileCSS],[COOL_CSS],[
]),
[])
-
-
-m4_ifelse(IOSAPP,[true],
- [])
-m4_ifelse(ANDROIDAPP,[true],
- [])
-m4_ifelse(EMSCRIPTENAPP,[true],
- [])
-
+m4_dnl
+m4_dnl Add branding.css for mobile apps, or the placeholder for server processing
+m4_ifelse(MOBILEAPP, [true], [],
+ [ ])
+m4_dnl
m4_dnl Handle localization
m4_ifelse(MOBILEAPP,[true],
[
diff --git a/browser/js/global.js b/browser/js/global.js
index 2baf05ea5f455..7dc5d1e1a7f5d 100644
--- a/browser/js/global.js
+++ b/browser/js/global.js
@@ -1,4 +1,4 @@
-/* -*- js-indent-level: 8 -*- */
+/* -*- js-indent-level: 8; fill-column: 100 -*- */
/* global Module ArrayBuffer Uint8Array _ */
@@ -477,6 +477,19 @@ class IOSAppInitializer extends MobileAppInitializer {
}
}
+class MacOSAppInitializer extends MobileAppInitializer {
+ constructor() {
+ super();
+
+ window.ThisIsTheMacOSApp = true;
+ window.postMobileMessage = function(msg) { window.webkit.messageHandlers.lok.postMessage(msg); };
+ window.postMobileError = function(msg) { window.webkit.messageHandlers.error.postMessage(msg); };
+ window.postMobileDebug = function(msg) { window.webkit.messageHandlers.debug.postMessage(msg); };
+
+ window.userInterfaceMode = window.coolParams.get('userinterfacemode');
+ }
+}
+
class GTKAppInitializer extends MobileAppInitializer {
constructor() {
super();
@@ -488,6 +501,24 @@ class GTKAppInitializer extends MobileAppInitializer {
}
}
+class WindowsAppInitializer extends MobileAppInitializer {
+ constructor() {
+ super();
+
+ window.ThisIsTheWindowsApp = true;
+ window.postMobileMessage = function(msg) { window.chrome.webview.postMessage('MSG ' + msg); };
+
+ // FIXME: No registration of separate handlers in Windows WebView2, so just log
+ // errors and debug messages? Maybe instead send a JSON object with separate name
+ // and body? But then we would have to parse that JSON object from the string in C#
+ // anyway.
+ window.postMobileError = function(msg) { window.chrome.webview.postMessage('ERR ' + msg); };
+ window.postMobileDebug = function(msg) { window.chrome.webview.postMessage('DBG ' + msg); };
+
+ window.userInterfaceMode = window.coolParams.get('userinterfacemode');
+ }
+}
+
class AndroidAppInitializer extends MobileAppInitializer {
constructor() {
super();
@@ -528,8 +559,12 @@ function getInitializerClass() {
if (osType === "IOS")
return new IOSAppInitializer();
+ else if (osType === "MACOS")
+ return new MacOSAppInitializer();
else if (osType === "GTK")
return new GTKAppInitializer();
+ else if (osType === "WINDOWS")
+ return new WindowsAppInitializer();
else if (osType === "ANDROID")
return new AndroidAppInitializer();
else if (osType === "EMSCRIPTEN")
diff --git a/browser/src/app/Socket.ts b/browser/src/app/Socket.ts
index b8910bf9ce239..8d26e4f650dfd 100644
--- a/browser/src/app/Socket.ts
+++ b/browser/src/app/Socket.ts
@@ -1144,7 +1144,10 @@ class Socket {
private _extractTextImg(e: SlurpMessageEvent): void {
if (
- (window.ThisIsTheiOSApp || window.ThisIsTheEmscriptenApp) &&
+ (window.ThisIsTheiOSApp ||
+ window.ThisIsTheWindowsApp ||
+ window.ThisIsTheMacOSApp ||
+ window.ThisIsTheEmscriptenApp) &&
typeof e.data === 'string'
) {
// Another fix for issue #5843 limit splitting on the first newline
diff --git a/browser/src/control/Toolbar.js b/browser/src/control/Toolbar.js
index 3b514a173c537..539719ec02b43 100644
--- a/browser/src/control/Toolbar.js
+++ b/browser/src/control/Toolbar.js
@@ -260,7 +260,7 @@ window.L.Map.include({
},
print: function (options) {
- if (window.ThisIsTheiOSApp || window.ThisIsTheAndroidApp) {
+ if (window.ThisIsTheiOSApp || window.ThisIsTheAndroidApp || window.ThisIsTheMacOSApp || window.ThisIsTheWindowsApp) {
window.postMobileMessage('PRINT');
} else {
this.showBusy(_('Downloading...'), false);
diff --git a/browser/src/global.d.ts b/browser/src/global.d.ts
index f803842b0e06c..90af6316ea78a 100644
--- a/browser/src/global.d.ts
+++ b/browser/src/global.d.ts
@@ -357,6 +357,9 @@ interface Window {
ThisIsAMobileApp: boolean;
ThisIsTheEmscriptenApp: boolean;
ThisIsTheGtkApp: boolean;
+ ThisIsTheiOSApp: boolean;
+ ThisIsTheMacOSApp: boolean;
+ ThisIsTheWindowsApp: boolean;
wopiSrc: string;
zoteroEnabled: boolean;
accessToken: string;
diff --git a/browser/src/layer/tile/CanvasTileLayer.js b/browser/src/layer/tile/CanvasTileLayer.js
index 749387b5d2592..8e97ac0993cba 100644
--- a/browser/src/layer/tile/CanvasTileLayer.js
+++ b/browser/src/layer/tile/CanvasTileLayer.js
@@ -2108,6 +2108,9 @@ window.L.CanvasTileLayer = window.L.Layer.extend({
// when json.commandName is '.uno:RowColSelCount'.
if (json.commandName && json.state !== undefined) {
this._map.fire('commandstatechanged', json);
+ if (window.ThisIsTheMacOSApp) {
+ window.postMobileMessage('COMMANDSTATECHANGED ' + JSON.stringify(json));
+ }
}
}
else if (textMsg.startsWith('.uno:Context=') && this._docType === 'presentation') {
@@ -2117,7 +2120,11 @@ window.L.CanvasTileLayer = window.L.Layer.extend({
var index = textMsg.indexOf('=');
var commandName = index !== -1 ? textMsg.substr(0, index) : '';
var state = index !== -1 ? textMsg.substr(index + 1) : '';
- this._map.fire('commandstatechanged', {commandName : commandName, state : state});
+ const json = {commandName : commandName, state : state};
+ this._map.fire('commandstatechanged', json);
+ if (window.ThisIsTheMacOSApp) {
+ window.postMobileMessage('COMMANDSTATECHANGED ' + JSON.stringify(json));
+ }
}
},
diff --git a/browser/src/map/Clipboard.js b/browser/src/map/Clipboard.js
index 5145f646313eb..1600b51791693 100644
--- a/browser/src/map/Clipboard.js
+++ b/browser/src/map/Clipboard.js
@@ -449,8 +449,10 @@ window.L.Clipboard = window.L.Class.extend({
},
_sendToInternalClipboard: async function (content) {
- if (window.ThisIsTheiOSApp) {
+ if (window.ThisIsTheiOSApp || window.ThisIsTheMacOSApp) {
await window.webkit.messageHandlers.clipboard.postMessage(`sendToInternal ${await content.text()}`); // no need to base64 in this direction...
+ } else if (window.ThisIsTheWindowsApp) {
+ await window.postMobileMessage(`CLIPBOARDJS sendToInternal ${await content.text()}`);
} else {
var formData = new FormData();
formData.append('file', content);
@@ -710,6 +712,8 @@ window.L.Clipboard = window.L.Class.extend({
}
if (!window.ThisIsTheiOSApp && // in mobile apps, we want to drop straight to navigatorClipboardRead as execCommand will require user interaction...
+ !window.ThisIsTheMacOSApp &&
+ !window.ThisIsTheWindowsApp &&
document.execCommand(operation) &&
serial !== this._clipboardSerial) {
window.app.console.log('copied successfully');
@@ -867,7 +871,7 @@ window.L.Clipboard = window.L.Class.extend({
// Executes the navigator.clipboard.write() call, if it's available.
_navigatorClipboardWrite: function(params) {
- if (!window.L.Browser.clipboardApiAvailable && !window.ThisIsTheiOSApp) {
+ if (!window.L.Browser.clipboardApiAvailable && !window.ThisIsTheiOSApp && !window.ThisIsTheMacOSApp && !window.ThisIsTheWindowsApp) {
return false;
}
@@ -888,7 +892,7 @@ window.L.Clipboard = window.L.Class.extend({
// Deferring like this is kinda horrible - it certainly looks gross in places - but it's absolutely necessary to avoid errors on the clipboard.write line
// I don't like it either :). If you change this make sure to thoroughly test cross-browser and cross-device!
- if (window.ThisIsTheiOSApp) {
+ if (window.ThisIsTheiOSApp || window.ThisIsTheMacOSApp) {
// This is sent down the fakewebsocket which can race with the
// native message - so first step is to wait for the result of
// that command so we are sure the clipboard is set before
@@ -897,6 +901,8 @@ window.L.Clipboard = window.L.Class.extend({
return; // Either wrong command or a pending event.
await window.webkit.messageHandlers.clipboard.postMessage(`write`);
+ } else if (window.ThisIsTheWindowsApp) {
+ await window.postMobileMessage(`CLIPBOARDWRITE`);
} else {
const url = this.getMetaURL() + '&MimeType=text/html,text/plain;charset=utf-8';
@@ -976,7 +982,7 @@ window.L.Clipboard = window.L.Class.extend({
// Executes the navigator.clipboard.read() call, if it's available.
_navigatorClipboardRead: function(isSpecial) {
- if (!window.L.Browser.clipboardApiAvailable && !window.ThisIsTheiOSApp) {
+ if (!window.L.Browser.clipboardApiAvailable && !window.ThisIsTheiOSApp && !window.ThisIsTheMacOSApp && !window.ThisIsTheWindowsApp) {
return false;
}
@@ -984,9 +990,7 @@ window.L.Clipboard = window.L.Class.extend({
return true;
},
- _iOSReadClipboard: async function() {
- const encodedClipboardData = await window.webkit.messageHandlers.clipboard.postMessage('read');
-
+ _MobileAppReadClipboard: function(encodedClipboardData) {
if (encodedClipboardData === "(internal)") {
return null;
}
@@ -1014,6 +1018,17 @@ window.L.Clipboard = window.L.Class.extend({
return [new ClipboardItem(dataByMimeType)];
},
+ _iOSReadClipboard: async function() {
+ const encodedClipboardData = await window.webkit.messageHandlers.clipboard.postMessage('read');
+ return this._MobileAppReadClipboard(encodedClipboardData);
+ },
+
+ _WindowsReadClipboard: async function() {
+ const encodedClipboardData = await window.postMobileMessage('CLIBOARDREAD');
+ // FIXME: Is the same code as for iOS OK? Will see.
+ return this._MobileAppReadClipboard(encodedClipboardData);
+ },
+
_asyncAttemptNavigatorClipboardRead: async function(isSpecial) {
var clipboard = navigator.clipboard;
if (window.L.Browser.cypressTest) {
@@ -1021,9 +1036,12 @@ window.L.Clipboard = window.L.Class.extend({
}
let clipboardContents;
try {
- clipboardContents = window.ThisIsTheiOSApp
- ? await this._iOSReadClipboard()
- : await clipboard.read();
+ if (window.ThisIsTheiOSApp || window.ThisIsTheMacOSApp)
+ clipboardContents = await this._iOSReadClipboard();
+ else if (window.ThisIsTheWindowsApp)
+ clipboardContents = await this._WindowsReadClipboard();
+ else
+ clipboardContents = await clipboard.read();
if (clipboardContents === null) {
this._doInternalPaste(this._map, false);
diff --git a/common/Common.hpp b/common/Common.hpp
index 04bd04ffa1950..196fa68cfc80e 100644
--- a/common/Common.hpp
+++ b/common/Common.hpp
@@ -86,7 +86,8 @@ constexpr const char UPLOADING_SUFFIX[] = "ing";
/// The client port number, both coolwsd and the kits have this.
extern int ClientPortNumber;
-extern std::string MasterLocation;
+class UnxSocketPath;
+extern UnxSocketPath MasterLocation;
/// Controls whether experimental features/behavior is enabled or not.
extern bool EnableExperimental;
diff --git a/common/CoolMount.cpp b/common/CoolMount.cpp
index 0dcdfd3311ec1..1d61621a02f3f 100644
--- a/common/CoolMount.cpp
+++ b/common/CoolMount.cpp
@@ -36,7 +36,19 @@
#define MS_REMOUNT 4
#define MS_NOSUID 16
#define MS_RDONLY 32
+#elif defined(__APPLE__)
+#define MOUNT mount_wrapper
+#define MS_MGC_VAL 0 // ignored, no mapping to macOS
+#define MS_NODEV MNT_NODEV
+#define MS_UNBINDABLE 0 // ignored, no mapping to macOS
+#define MS_BIND 0 // ignored, no mapping to macOS
+#define MS_REC 0 // ignored, no mapping to macOS
+#define MS_REMOUNT 0 // ignored, no mapping to macOS
+#define MS_NOSUID MNT_NOSUID
+#define MS_RDONLY MNT_RDONLY
+#endif
+#ifdef __FreeBSD__
void
build_iovec(struct iovec **iov, int *iovlen, const char *name, const void *val,
size_t len)
@@ -94,7 +106,32 @@ int mount_wrapper(const char *source, const char *target,
return nmount(iov, iovlen, freebsd_flags);
}
+#elif defined(__APPLE__)
+int mount_wrapper(const char *source, const char *target,
+ const char *filesystemtype, unsigned long mountflags,
+ const void *data)
+{
+ // Build some "data" for mount(2) if it's suspected that the FS needs a device=SOURCE string:
+ char fallback_data[1024];
+ if (source && *source) {
+ // e.g. "device=/dev/disk2s1" or "device=/path/to/something"
+ snprintf(fallback_data, sizeof(fallback_data), "device=%s", source);
+ } else {
+ fallback_data[0] = '\0';
+ }
+
+ const char *fs = (filesystemtype ? filesystemtype : "hfs");
+
+ // It's expected that the mountflags are built using the above defines,
+ // ie. no translation is needed
+ return mount(fs, target, mountflags,
+ fallback_data[0] ? (void*)fallback_data : (void*)data);
+}
+#else
+#define MOUNT mount
+#endif
+#ifndef __linux__
#define MNT_DETACH 1
int umount2(const char *target, int flags)
@@ -123,8 +160,6 @@ int umount2(const char *target, int flags)
return unmount(target, flags);
}
-#else
-#define MOUNT mount
#endif
void usage(const char* program)
diff --git a/common/FileUtil-unix.cpp b/common/FileUtil-unix.cpp
index 3eec3050339d8..305ac8e6c01e0 100644
--- a/common/FileUtil-unix.cpp
+++ b/common/FileUtil-unix.cpp
@@ -13,6 +13,7 @@
#include
#include
+#include
#include
#include
#include
@@ -60,7 +61,7 @@ namespace FileUtil
return path;
}
-#ifndef IOS // iOS-specific implementation in FileUtil-apple.cpp
+#if !defined(__APPLE__) // iOS-specific implementation in FileUtil-apple.cpp
bool platformDependentCheckDiskSpace(const std::string& path, int64_t enoughSpace)
{
@@ -88,6 +89,13 @@ namespace FileUtil
return false;
#endif
+ return true;
+ }
+#elif defined(MACOS) && !MOBILEAPP
+
+ bool platformDependentCheckDiskSpace(const std::string&, int64_t)
+ {
+ // FIXME Use the FileUtil-apple.mm instead
return true;
}
#endif
@@ -460,14 +468,14 @@ namespace FileUtil
{
{
tsAccess.tv_sec,
-#ifdef IOS
+#if defined(IOS) || defined(MACOS)
(__darwin_suseconds_t)
#endif
(tsAccess.tv_nsec / 1000)
},
{
tsModified.tv_sec,
-#ifdef IOS
+#if defined(IOS) || defined(MACOS)
(__darwin_suseconds_t)
#endif
(tsModified.tv_nsec / 1000)
diff --git a/common/FileUtil-windows.cpp b/common/FileUtil-windows.cpp
new file mode 100644
index 0000000000000..43bbde09753fa
--- /dev/null
+++ b/common/FileUtil-windows.cpp
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * Copyright the Collabora Online contributors.
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include
+
+#include
+#include
+
+#include
+#include
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+#include
+#include
+
+namespace FileUtil
+{
+ void removeFile(const std::string& path, const bool recursive)
+ {
+ LOG_DBG("Removing [" << path << "] " << (recursive ? "recursively." : "only."));
+
+ try
+ {
+ if (recursive)
+ std::filesystem::remove_all(Util::string_to_wide_string(path));
+ else
+ std::filesystem::remove(Util::string_to_wide_string(path));
+ }
+ catch (const std::filesystem::filesystem_error& e)
+ {
+ // Don't complain if already non-existant.
+ if (FileUtil::Stat(path).exists())
+ {
+ // Error only if it still exists.
+ LOG_ERR("Failed to remove ["
+ << path << "] " << (recursive ? "recursively: " : "only: ") << e.what());
+ }
+ }
+ }
+
+ /// Remove directories only, which must be empty for this to work.
+ static void removeEmptyDirTreeTakingPath(const std::filesystem::path& path)
+ {
+ for (auto const& dirent :
+ std::filesystem::directory_iterator{path, std::filesystem::directory_options::skip_permission_denied})
+ {
+ if (dirent.is_directory())
+ {
+ std::error_code ec;
+ removeEmptyDirTreeTakingPath(dirent.path());
+ std::filesystem::remove(dirent.path(), ec);
+ }
+ }
+ }
+
+ void removeEmptyDirTree(const std::string& path)
+ {
+ LOG_DBG("Removing empty directories at [" << path << "] recursively");
+
+ removeEmptyDirTreeTakingPath(std::filesystem::path(Util::string_to_wide_string(path)));
+ }
+
+ bool isEmptyDirectory(const char* path)
+ {
+ bool empty = true;
+ for (auto const& dirent :
+ std::filesystem::directory_iterator{std::filesystem::path(Util::string_to_wide_string(path)),
+ std::filesystem::directory_options::skip_permission_denied})
+ {
+ (void) dirent;
+ empty = false;
+ break;
+ }
+ return empty;
+ }
+
+ bool linkOrCopyFile(const std::string& source, const std::string& newPath)
+ {
+ return FileUtil::copy(source, newPath, /*log=*/true, /*throw_on_error=*/false);
+ }
+
+ std::string realpath(const char* path)
+ {
+ return path;
+ }
+
+ bool platformDependentCheckDiskSpace(const std::string& path, int64_t enoughSpace)
+ {
+ // FIXME
+ return true;
+ }
+
+ int openFileAsFD(const std::string& file, int oflag, int mode)
+ {
+ return _wopen(Util::string_to_wide_string(file).c_str(), oflag | O_BINARY, mode);
+ }
+
+ int readFromFD(int fd, void *buf, size_t nbytes)
+ {
+ return _read(fd, buf, nbytes);
+ }
+
+ int writeToFD(int fd, const void *buf, size_t nbytes)
+ {
+ return _write(fd, buf, nbytes);
+ }
+
+ int closeFD(int fd)
+ {
+ return _close(fd);
+ }
+
+ void openFileToIFStream(const std::string& file, std::ifstream& stream, std::ios_base::openmode mode)
+ {
+ stream.open(Util::string_to_wide_string(file), mode | std::ios_base::binary);
+ }
+
+ int getStatOfFile(const std::string& file, struct stat& sb)
+ {
+ return _wstat64i32(Util::string_to_wide_string(file).c_str(), (struct _stat64i32*) &sb);
+ }
+
+ int getLStatOfFile(const std::string& file, struct stat& sb)
+ {
+ return getStatOfFile(file, sb);
+ }
+
+ int unlinkFile(const std::string& file)
+ {
+ return _wunlink(Util::string_to_wide_string(file).c_str());
+ }
+
+ int makeDirectory(const std::string& dir)
+ {
+ return _wmkdir(Util::string_to_wide_string(dir).c_str());
+ }
+
+ void createDirectory(const std::string& dir)
+ {
+ std::filesystem::create_directory(Util::string_to_wide_string(dir));
+ }
+
+ std::string getSysTempDirectoryPath()
+ {
+ std::wstring path = std::filesystem::temp_directory_path().wstring();
+
+ if (!path.empty() && path.back() == L'\\')
+ path.pop_back();
+
+ if (!path.empty())
+ return Util::wide_string_to_string(path);
+
+ // Try some fallbacks
+ wchar_t *tmp = _wgetenv(L"TEMP");
+ if (!tmp)
+ tmp = _wgetenv(L"TMP");
+
+ // We don't want to modify the environment string directly.
+ if (tmp)
+ {
+ tmp = _wcsdup(tmp);
+ if (tmp[wcslen(tmp)-1] == L'\\')
+ tmp[wcslen(tmp)-1] = L'\0';
+ }
+
+ // This folder seems to be protected somehow on modern Windows, but oh well.
+ // Duplicate here, too, so we can free() below.
+ if (!tmp)
+ tmp = _wcsdup(L"C:/Windows/Temp");
+
+ auto result = Util::wide_string_to_string(tmp);
+ free(tmp);
+ return result;
+ }
+
+ bool isWritable(const char* path)
+ {
+ if (_waccess(Util::string_to_wide_string(path).c_str(), 0) == 0)
+ return true;
+
+ LOG_INF("No write access to path [" << path << "]: " << strerror(errno));
+ return false;
+ }
+} // namespace FileUtil
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/FileUtil.cpp b/common/FileUtil.cpp
index fe3a4a73f229d..2e1b80bdf6b02 100644
--- a/common/FileUtil.cpp
+++ b/common/FileUtil.cpp
@@ -176,13 +176,15 @@ namespace FileUtil
if (preserveTimestamps)
{
const Stat st(fromPath);
+#ifndef _WIN32
updateTimestamps(randFilename,
-#ifdef IOS
+#if defined(IOS) || defined(MACOS)
st.sb().st_atimespec, st.sb().st_mtimespec
#else
st.sb().st_atim, st.sb().st_mtim
#endif
);
+#endif
}
// Now rename atomically, replacing any existing files with the same name.
diff --git a/common/FileUtil.hpp b/common/FileUtil.hpp
index 0e66b4f960616..a3aed84913cf6 100644
--- a/common/FileUtil.hpp
+++ b/common/FileUtil.hpp
@@ -40,12 +40,15 @@
namespace FileUtil
{
- // Wrappers for actual file handling library API.
+ // Wrappers for actual file handling library API. Needed because the file names we handle are in
+ // UTF-8, and on Windows we can't pass such to C and C++ library APIs. We need to convert to
+ // UTF-16 strings and call the proper wide character APIs.
// Also needed because Visual Studio insists on claiming that some POSIXy functions are "deprecated" and
// wants you to call the variant prefixed with an underscore instead, for example _close().
- // As open(). Returns the file descriptor. On error returns -1 and sets errno.
+ // As open(). Returns the file descriptor (which on Windows is just a thing the C library knows
+ // about, not the OS). On error returns -1 and sets errno.
int openFileAsFD(const std::string& file, int oflag, int mode = 0);
// As read() and write().
@@ -74,12 +77,15 @@ namespace FileUtil
void createDirectory(const std::string& dir);
// Wraps std::filesystem::temp_directory_path(), and if that fails, uses obvious fallbacks.
+ // Returns as UTF-8 on Windows. (And surely also on any sane Unix?)
std::string getSysTempDirectoryPath();
/// Returns true iff the path given is writable by our *real* UID.
+ /// On Windows "real UID" is meaningless.
bool isWritable(const char* path);
/// Update the access-time and modified-time metadata for the given file.
+ /// Not implemented on Windows.
bool updateTimestamps(const std::string& filename, timespec tsAccess, timespec tsModified);
// End of wrappers for platform-dependent API.
@@ -187,7 +193,8 @@ namespace FileUtil
/// Create a temporary directory in the root provided
std::string createTmpDir(const std::string& dirName, std::string root = std::string());
- /// Returns the realpath(3) of the provided path.
+ /// Returns the realpath(3) of the provided path. This also has a separate implementation for
+ /// Windows.
std::string realpath(const char* path);
inline std::string realpath(const std::string& path)
@@ -267,8 +274,11 @@ namespace FileUtil
/// nanosecond precision, if/when the filesystem supports it.
timespec modifiedTime() const
{
-#ifdef IOS
+#if defined(IOS) || defined(MACOS)
return _sb.st_mtimespec;
+#elif defined(_WIN32)
+ timespec result{ _sb.st_mtime, 0 };
+ return result;
#else
return _sb.st_mtim;
#endif
diff --git a/common/JailUtil.cpp b/common/JailUtil.cpp
index 0938ab9820832..923487c7be4eb 100644
--- a/common/JailUtil.cpp
+++ b/common/JailUtil.cpp
@@ -43,6 +43,7 @@ namespace
static const std::string CoolTestMountpoint = "cool_test_mount";
+#ifdef __linux__
static void setdeny()
{
std::ofstream of("/proc/self/setgroups");
@@ -61,6 +62,8 @@ static void mapuser(uid_t origuid, uid_t newuid, gid_t origgid, gid_t newgid)
of << newgid << " " << origgid << " 1";
}
}
+#endif // __linux__
+
} // namespace
bool enterMountingNS(uid_t uid, gid_t gid)
@@ -238,12 +241,15 @@ constexpr const char* COPIED_JAIL_MARKER_FILE = "delete.me";
void markJailCopied(const std::string& root)
{
+#if ENABLE_CHILDROOTS
// The reason we should be able to create this file
// is because the jail must be writable.
// Failing this will cause an exception, signaling an error.
Poco::File(root + '/' + COPIED_JAIL_MARKER_FILE).createFile();
+#endif
}
+#if ENABLE_CHILDROOTS
bool isJailCopied(const std::string& root)
{
// If the marker file exists, the jail was copied.
@@ -288,6 +294,7 @@ void removeAuxFolders(const std::string &root)
FileUtil::removeFile(Poco::Path(root, "tmp").toString(), true);
FileUtil::removeFile(Poco::Path(root, "linkable").toString(), true);
}
+#endif
/*
The tmp dir of a path//tmp is mounted from (or linked to) a
@@ -298,6 +305,7 @@ void removeAuxFolders(const std::string &root)
*/
void removeAssocTmpOfJail(const std::string &root)
{
+#if ENABLE_CHILDROOTS
Poco::Path jailPath(root);
jailPath.makeDirectory();
const std::string jailId = jailPath[jailPath.depth() - 1];
@@ -307,12 +315,14 @@ void removeAssocTmpOfJail(const std::string &root)
jailPath.pushDirectory(std::string("cool-") + jailId);
FileUtil::removeFile(jailPath.toString(), true);
+#endif
}
} // namespace
bool tryRemoveJail(const std::string& root)
{
+#if ENABLE_CHILDROOTS
const bool emptyJail = FileUtil::isEmptyDirectory(root);
if (!emptyJail && !FileUtil::Stat(root + '/' + LO_JAIL_SUBPATH).exists())
return false; // not a jail.
@@ -343,6 +353,7 @@ bool tryRemoveJail(const std::string& root)
safeRemoveDir(root);
removeAssocTmpOfJail(root);
+#endif
return true;
}
@@ -354,6 +365,7 @@ bool tryRemoveJail(const std::string& root)
/// inadvertently delete the contents of the mount-points.
void cleanupJails(const std::string& root)
{
+#if ENABLE_CHILDROOTS
LOG_INF("Cleaning up childroot directory [" << root << "].");
FileUtil::Stat stRoot(root);
@@ -433,18 +445,22 @@ void cleanupJails(const std::string& root)
safeRemoveDir(root);
else
LOG_WRN("Jails root directory [" << root << "] is not empty. Will not remove it.");
+#endif
}
void createJailPath(const std::string& path)
{
+#if ENABLE_CHILDROOTS
LOG_INF("Creating jail path (if missing): " << path);
Poco::File(path).createDirectories();
if (chmod(path.c_str(), S_IXUSR | S_IWUSR | S_IRUSR) != 0)
LOG_WRN_SYS("chmod(\"" << path << "\") failed");
+#endif
}
void setupChildRoot(bool bindMount, const std::string& childRoot, const std::string& sysTemplate)
{
+#if ENABLE_CHILDROOTS
// Start with a clean slate.
cleanupJails(childRoot);
@@ -478,6 +494,7 @@ void setupChildRoot(bool bindMount, const std::string& childRoot, const std::str
else
LOG_INF("Disabling Bind-Mounting of jail contents per "
"mount_jail_tree config in coolwsd.xml.");
+#endif
}
/// The envar name used to control bind-mounting of systemplate/jails.
@@ -522,6 +539,7 @@ bool isMountNamespacesEnabled()
}
+#if ENABLE_CHILDROOTS
namespace SysTemplate
{
/// The network and other system files we need to keep up-to-date in jails.
@@ -729,6 +747,7 @@ void setupRandomDeviceLinks(const std::string& sysTemplate)
}
} // namespace SysTemplate
+#endif // ENABLE_CHILDROOTS
} // namespace JailUtil
diff --git a/common/JailUtil.hpp b/common/JailUtil.hpp
index bfc7214742753..e532a7983a6dc 100644
--- a/common/JailUtil.hpp
+++ b/common/JailUtil.hpp
@@ -90,6 +90,7 @@ void disableMountNamespaces();
/// Returns true iff namespace-mounting is enabled in this process.
bool isMountNamespacesEnabled();
+#if ENABLE_CHILDROOTS
namespace SysTemplate
{
/// Setup links for /dev/random and /dev/urandom in systemplate.
@@ -106,6 +107,7 @@ void setupDynamicFiles(const std::string& sysTemplate);
bool updateDynamicFiles(const std::string& sysTemplate);
} // namespace SysTemplate
+#endif // ENABLE_CHILDROOTS
} // end namespace JailUtil
diff --git a/common/Log.cpp b/common/Log.cpp
index 0d082b6cf0211..c5456af4a97c4 100644
--- a/common/Log.cpp
+++ b/common/Log.cpp
@@ -29,7 +29,13 @@
#include
#include
#include
+#ifndef _WIN32
#include
+#else
+#define WIN32_LEAN_AND_MEAN
+#include
+#include
+#endif
#include
namespace
@@ -134,6 +140,7 @@ namespace Log
/// Write the given buffer to stderr directly.
static inline std::size_t writeRaw(const char* data, std::size_t count)
{
+#ifndef _WIN32
#if WASMAPP
// In WASM, stdout works best.
constexpr int LOG_FILE_FD = STDOUT_FILENO;
@@ -159,6 +166,19 @@ namespace Log
count -= wrote;
}
return ptr - data;
+#else // _WIN32
+ if (!IsDebuggerPresent())
+ fwrite(data, count, 1, stderr);
+ else
+ {
+ char *s = (char *)malloc(count + 1);
+ memcpy(s, data, count);
+ s[count] = 0;
+ OutputDebugStringA(s);
+ free(s);
+ }
+ return count;
+#endif
}
template inline void writeRaw(const char (&data)[N])
@@ -474,7 +494,7 @@ namespace Log
char* prefix(const std::chrono::time_point& tp, char* buffer,
const std::string_view level)
{
-#if defined(IOS) || defined(__FreeBSD__)
+#if defined(IOS) || defined(__FreeBSD__) || defined(_WIN32)
// Don't bother with the "Source" which would be just "Mobile" always (or whatever the app
// process is called depending on platform and configuration) and non-informative as there
// is just one process in the app anyway.
@@ -610,13 +630,13 @@ namespace Log
const auto it = config.find("flush");
if (it == config.end() || Util::toLower(it->second) != "false")
{
- // Buffered logging, reduces number of write(2) syscalls.
- channel = static_cast(new Log::BufferedConsoleChannel());
+ // Unbuffered (flushed) logging, directly writes each entry (to stderr).
+ channel = static_cast(new Log::ConsoleChannel());
}
else
{
- // Unbuffered logging, directly writes each entry (to stderr).
- channel = static_cast(new Log::ConsoleChannel());
+ // Buffered logging, reduces number of write(2) syscalls.
+ channel = static_cast(new Log::BufferedConsoleChannel());
}
}
diff --git a/common/Log.hpp b/common/Log.hpp
index 1f9b19fab8688..4fc91d5f38eca 100644
--- a/common/Log.hpp
+++ b/common/Log.hpp
@@ -68,11 +68,11 @@ namespace Log
/// Initialize the logging system.
void initialize(const std::string& name,
const std::string& logLevel,
- bool withColor,
- bool logToFile,
- const std::map& config,
- bool logToFileUICmd,
- const std::map& configUICmd);
+ bool withColor = false,
+ bool logToFile = false,
+ const std::map& config = {},
+ bool logToFileUICmd = false,
+ const std::map& configUICmd = {});
/// Shutdown and release the logging system.
void shutdown();
diff --git a/common/MobileApp.hpp b/common/MobileApp.hpp
index b86aaa75ea1f9..4ec1bd11e0181 100644
--- a/common/MobileApp.hpp
+++ b/common/MobileApp.hpp
@@ -59,8 +59,8 @@ class DocumentData
#ifdef IOS
CODocument *coDocument;
- std::weak_ptr docBroker;
#endif
+ std::weak_ptr docBroker;
};
/// Stub/Dummy WOPI types/interface.
diff --git a/common/SigUtil-server.cpp b/common/SigUtil-server.cpp
index bc0157446d72a..c86e782d59c21 100644
--- a/common/SigUtil-server.cpp
+++ b/common/SigUtil-server.cpp
@@ -45,7 +45,7 @@
# include
#endif
-#if !defined(ANDROID) && !defined(IOS) && !defined(__FreeBSD__)
+#if !defined(ANDROID) && !defined(IOS) && !defined(MACOS) && !defined(__FreeBSD__)
# include
#endif
#if defined(__FreeBSD__)
@@ -588,7 +588,7 @@ void resetTerminationFlags()
void dieOnParentDeath()
{
-#if !defined(ANDROID) && !defined(__FreeBSD__)
+#if defined(__linux__) && !defined(ANDROID)
prctl(PR_SET_PDEATHSIG, SIGKILL);
#endif
#if defined(__FreeBSD__)
diff --git a/common/Syscall.cpp b/common/Syscall.cpp
new file mode 100644
index 0000000000000..b676d1bfae833
--- /dev/null
+++ b/common/Syscall.cpp
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * Copyright the Collabora Online contributors.
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include
+
+#if MOBILEAPP
+#error This file is not supposed to be compiled in the MOBILEAPP case
+#endif
+
+#include "Syscall.hpp"
+
+#include
+
+#include
+#include
+#include
+#include
+
+
+/**
+ * Internal helper functions.
+ */
+#if !defined(__linux__)
+namespace {
+
+
+/**
+ * Set FD_CLOEXEC and O_NONBLOCK on one file descriptor.
+ *
+ * Called "unsafe" because it keeps the file descriptor open on error, possibly leading to leaks.
+ */
+bool unsafe_set_fd_cloexec_nonblock(int fd, bool cloexec, bool nonblock) {
+ // Set FD_CLOEXEC if the user wants it
+ if (cloexec)
+ {
+ int fd_flags = fcntl(fd, F_GETFD);
+ if (fd_flags == -1)
+ return false;
+
+ fd_flags |= FD_CLOEXEC;
+ if (fcntl(fd, F_SETFD, fd_flags) == -1)
+ return false;
+ }
+
+ // Set O_NONBLOCK if the user wants it
+ if (nonblock)
+ {
+ int fl_flags = fcntl(fd, F_GETFL);
+ if (fl_flags == -1)
+ return false;
+
+ fl_flags |= O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, fl_flags) == -1)
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Set FD_CLOEXEC and O_NONBLOCK on one file descriptor.
+ */
+bool set_fd_cloexec_nonblock(int fd, bool cloexec, bool nonblock) {
+ bool ret = unsafe_set_fd_cloexec_nonblock(fd, cloexec, nonblock);
+ if (!ret) {
+ int saved_errno = errno;
+ close(fd);
+ errno = saved_errno;
+ }
+
+ return ret;
+}
+
+/**
+ * Set CLOEXEC or NONBLOCK on both sides of the pipe/socket/...
+ */
+bool set_fds_cloexec_nonblock(int fds[2], bool cloexec, bool nonblock) {
+ for (int i = 0; i < 2; i++) {
+ bool ret = unsafe_set_fd_cloexec_nonblock(fds[i], cloexec, nonblock);
+ if (!ret) {
+ int saved_errno = errno;
+ close(fds[0]);
+ close(fds[1]);
+ errno = saved_errno;
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+}
+#endif
+
+int Syscall::accept_cloexec_nonblock(int socket, struct sockaddr *address, socklen_t *address_len)
+{
+#if defined(__linux__)
+ return accept4(socket, address, address_len, SOCK_CLOEXEC | SOCK_NONBLOCK);
+#else
+ int fd = ::accept(socket, address, address_len);
+ if (fd < 0)
+ return fd;
+
+ return set_fd_cloexec_nonblock(fd, true, true)? fd: -1;
+#endif
+}
+
+int Syscall::get_peer_pid(int socket) {
+#ifdef __linux__
+ struct ucred creds;
+ socklen_t credSize = sizeof(struct ucred);
+ if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &creds, &credSize) < 0)
+ return -1;
+
+ return creds.pid;
+#elif defined(__FreeBSD__)
+ struct xucred creds;
+ socklen_t credSize = sizeof(struct xucred);
+ if (getsockopt(socket, SOL_LOCAL, LOCAL_PEERCRED, &creds, &credSize) < 0)
+ return -1;
+
+ return creds.cr_pid;
+#elif defined(__APPLE__)
+ int pid = -1;
+ socklen_t pidLen = sizeof(pid);
+
+ // Retrieve the PID of the peer connected on this Unix-domain socket
+ if (getsockopt(socket, SOL_LOCAL, LOCAL_PEERPID, &pid, &pidLen) < 0)
+ return -1;
+
+ return pid;
+#else
+#error Implement for your platform
+#endif
+}
+
+/// Implementation of pipe2() for platforms that don't have it (like macOS)
+int Syscall::pipe2(int pipefd[2], int flags)
+{
+#if HAVE_PIPE2
+ return ::pipe2(pipefd, flags);
+#else
+ if (pipe(pipefd) < 0)
+ return -1;
+
+ return set_fds_cloexec_nonblock(pipefd, flags & O_CLOEXEC, flags & O_NONBLOCK)? 0: -1;
+#endif
+}
+
+int Syscall::socket_cloexec_nonblock(int domain, int type, int protocol) {
+#ifdef __linux__
+ return ::socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol);
+#else
+ int fd = ::socket(domain, type, protocol);
+ if (fd < 0)
+ return fd;
+
+ return set_fd_cloexec_nonblock(fd, true, true)? fd: -1;
+#endif
+}
+
+int Syscall::socketpair_cloexec_nonblock(int domain, int type, int protocol, int socket_vector[2])
+{
+#ifdef __linux__
+ return ::socketpair(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol, socket_vector);
+#else
+ int rc = ::socketpair(domain, type, protocol, socket_vector);
+ if (rc < 0)
+ return rc;
+
+ return set_fds_cloexec_nonblock(socket_vector, true, true)? 0: -1;
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/Syscall.hpp b/common/Syscall.hpp
new file mode 100644
index 0000000000000..d8a98d7e0d626
--- /dev/null
+++ b/common/Syscall.hpp
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * Copyright the Collabora Online contributors.
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#if MOBILEAPP
+#error This file is not supposed to be compiled in the MOBILEAPP case
+#endif
+
+#include
+
+/**
+ * This is a place for platform-dependent syscalls, to be able to extend them conveniently.
+ */
+namespace Syscall {
+
+ /**
+ * Implement an equivalent of accept4() with CLOEXEC and NONBLOCK set.
+ *
+ * @return the accepted socket, or -1 on error
+ */
+ int accept_cloexec_nonblock(int socket, struct sockaddr *address, socklen_t *address_len);
+
+ /**
+ * Retrieve the PID of the peer connected on this Unix-domain socket.
+ *
+ * @return peer's PID, or -1 on error
+ */
+ int get_peer_pid(int socket);
+
+ /**
+ * Implement pipe2() on platforms that don't have it.
+ *
+ * @return 0 on success, -1 on error
+ */
+ int pipe2(int pipefd[2], int flags);
+
+ /**
+ * Implement socket() with CLOEXEC and NONBLOCK on platforms that don't have those flags.
+ *
+ * @return file descriptor of the socket, or -1 on error
+ */
+ int socket_cloexec_nonblock(int domain, int type, int protocol);
+
+ /**
+ * Implement socket_pair() with CLOEXEC and NONBLOCK on platforms that don't have those flags.
+ *
+ * @return 0 on success, -1 on error
+ */
+ int socketpair_cloexec_nonblock(int domain, int type, int protocol, int socket_vector[2]);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/Unit.cpp b/common/Unit.cpp
index 58d0f1c0e5bf4..cc60f68a60a80 100644
--- a/common/Unit.cpp
+++ b/common/Unit.cpp
@@ -25,7 +25,9 @@
#include
#include
#include
+#ifndef _WIN32
#include
+#endif
#include
std::atomicGlobalKit = nullptr;
diff --git a/common/Util-freebsd.cpp b/common/Util-freebsd.cpp
new file mode 100644
index 0000000000000..280e4a5498a63
--- /dev/null
+++ b/common/Util-freebsd.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * Copyright the Collabora Online contributors.
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#include
+
+#include "Util.hpp"
+
+#ifdef __linux__
+#include
+#include
+#elif defined __FreeBSD__
+#include
+#endif
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include "Log.hpp"
+
+namespace Util
+{
+
+// TODO FIXME This should be shared with common/Util-linux.cpp, not a copy of that
+class CounterImpl {
+private:
+ DIR* _dir = nullptr;
+
+public:
+ CounterImpl(const char* procPath)
+ : _dir(opendir(procPath))
+ {
+ if (!_dir)
+ LOG_ERR("No proc mounted for procPath " << procPath << ", can't count threads");
+ }
+
+ ~CounterImpl() { closedir(_dir); }
+
+ int count()
+ {
+ if (!_dir)
+ return -1;
+
+ rewinddir(_dir);
+
+ int tasks = 0;
+ struct dirent* i;
+ while ((i = readdir(_dir)))
+ {
+ if (i->d_name[0] != '.')
+ tasks++;
+ }
+
+ return tasks;
+ }
+};
+
+// TODO FIXME This is just a copy from a3334b96cdb25e1b6e90bb1a7313222b59658cc9, should be made to work
+ThreadCounter::ThreadCounter() { pid = getpid(); }
+
+ThreadCounter::~ThreadCounter() {}
+
+int ThreadCounter::count()
+{
+ size_t len = 0, olen = 0;
+ struct kinfo_proc* kipp = NULL;
+ int name[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD, pid };
+ int error = sysctl(name, 4, NULL, &len, NULL, 0);
+ if (len == 0 || (error < 0 && errno != EPERM)) {
+ goto fail;
+ }
+ do
+ {
+ len += len / 10;
+ kipp = (struct kinfo_proc *) reallocf(kipp, len);
+ if (kipp == NULL)
+ {
+ goto fail;
+ }
+ olen = len;
+ error = sysctl(name, 4, kipp, &len, NULL, 0);
+ } while (error < 0 && errno == ENOMEM && olen == len);
+
+ if (error < 0 && errno != EPERM) {
+ goto fail;
+ }
+ return len / sizeof(*kipp);
+
+fail:
+ if (kipp)
+ free(kipp);
+ return 0;
+}
+
+FDCounter::FDCounter() : _impl(new CounterImpl("/proc/self/fd")) {}
+
+FDCounter::~FDCounter() = default;
+
+int FDCounter::count() { return _impl->count(); }
+
+} // namespace Util
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/Util-linux.cpp b/common/Util-linux.cpp
new file mode 100644
index 0000000000000..34878d307b207
--- /dev/null
+++ b/common/Util-linux.cpp
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * Copyright the Collabora Online contributors.
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#include
+
+#include "Util.hpp"
+
+#ifdef __linux__
+#include
+#include
+#elif defined __FreeBSD__
+#include
+#endif
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include "Log.hpp"
+
+namespace Util
+{
+
+class CounterImpl {
+private:
+ DIR* _dir = nullptr;
+
+public:
+ CounterImpl(const char* procPath)
+ : _dir(opendir(procPath))
+ {
+ if (!_dir)
+ LOG_ERR("No proc mounted for procPath " << procPath << ", can't count threads");
+ }
+
+ ~CounterImpl() { closedir(_dir); }
+
+ int count()
+ {
+ if (!_dir)
+ return -1;
+
+ rewinddir(_dir);
+
+ int tasks = 0;
+ struct dirent* i;
+ while ((i = readdir(_dir)))
+ {
+ if (i->d_name[0] != '.')
+ tasks++;
+ }
+
+ return tasks;
+ }
+};
+
+ThreadCounter::ThreadCounter() : _impl(new CounterImpl("/proc/self/task")) {}
+
+ThreadCounter::~ThreadCounter() = default;
+
+int ThreadCounter::count() { return _impl->count(); }
+
+FDCounter::FDCounter() : _impl(new CounterImpl("/proc/self/fd")) {}
+
+FDCounter::~FDCounter() = default;
+
+int FDCounter::count() { return _impl->count(); }
+
+} // namespace Util
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/Util-macos.cpp b/common/Util-macos.cpp
new file mode 100644
index 0000000000000..894d7dcbf1e11
--- /dev/null
+++ b/common/Util-macos.cpp
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * Copyright the Collabora Online contributors.
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#include
+
+#include "Util.hpp"
+
+#include
+#include
+#include
+
+#include
+#include "Log.hpp"
+
+namespace Util
+{
+
+/** Nothing to do for macOS, everything is implemented directly in the count() methods. */
+class CounterImpl {};
+
+ThreadCounter::ThreadCounter() : _impl(new CounterImpl()) {}
+
+ThreadCounter::~ThreadCounter() {}
+
+int ThreadCounter::count() {
+ // use the Mach task_threads approach:
+ mach_msg_type_number_t threadCount = 0;
+ thread_act_array_t threadList;
+
+ kern_return_t kr = task_threads(mach_task_self(), &threadList, &threadCount);
+ if (kr != KERN_SUCCESS) {
+ return -1;
+ }
+
+ // deallocate the array not to leak memory
+ vm_deallocate(mach_task_self(), (vm_address_t)threadList, threadCount * sizeof(thread_t));
+
+ return (int)threadCount;
+}
+
+FDCounter::FDCounter() : _impl(new CounterImpl()) {}
+
+FDCounter::~FDCounter() {}
+
+int FDCounter::count() {
+ // there's no /proc/self/fd, let's use the naive approach:
+ // Iterate from 0..getdtablesize()-1 and call fcntl(fd, F_GETFD).
+ // NB. a bit slower but hopefully workable for a typical range.
+ int maxFD = getdtablesize();
+ if (maxFD < 0) {
+ return -1;
+ }
+
+ int count = 0;
+ for (int fd = 0; fd < maxFD; ++fd) {
+ // If fcntl works, the FD is in use
+ if (fcntl(fd, F_GETFD) != -1) {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+} // namespace Util
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/Util-mobile.cpp b/common/Util-mobile.cpp
index e0c4c98fcebd3..57c6b8897e190 100644
--- a/common/Util-mobile.cpp
+++ b/common/Util-mobile.cpp
@@ -15,9 +15,6 @@
namespace Util
{
/// No-op implementation of desktop only functions
-DirectoryCounter::DirectoryCounter(const char*) { (void)_tasks; }
-DirectoryCounter::~DirectoryCounter() {}
-int DirectoryCounter::count() { return 0; }
int spawnProcess(const std::string&, const StringVector&) { return 0; }
std::string getHumanizedBytes(unsigned long) { return std::string(); }
diff --git a/common/Util-server.cpp b/common/Util-server.cpp
index 7bca210bed1b4..ee00a0a90068f 100644
--- a/common/Util-server.cpp
+++ b/common/Util-server.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#ifdef __linux__
#include
@@ -27,10 +28,15 @@
#elif defined __FreeBSD__
#include
#include
-#include
extern char** environ;
#endif
+// 'environ' is not directly available on macOS, but using _NSGetEnviron() should be good enough
+#ifdef __APPLE__
+# include
+# define environ (*_NSGetEnviron())
+#endif
+
namespace
{
const char* startsWith(const char* line, const char* tag, std::size_t tagLen)
@@ -138,71 +144,6 @@ std::size_t getFromCGroupV2(const std::string& key)
namespace Util
{
-DirectoryCounter::DirectoryCounter(const char* procPath)
- : _tasks(opendir(procPath))
-{
- if (!_tasks)
- LOG_ERR("No proc mounted, can't count threads");
-}
-
-DirectoryCounter::~DirectoryCounter() { closedir(reinterpret_cast(_tasks)); }
-
-int DirectoryCounter::count()
-{
- auto dir = reinterpret_cast(_tasks);
-
- if (!dir)
- return -1;
-
- rewinddir(dir);
-
- int tasks = 0;
- struct dirent* i;
- while ((i = readdir(dir)))
- {
- if (i->d_name[0] != '.')
- tasks++;
- }
-
- return tasks;
-}
-
-#ifdef __FreeBSD__
-ThreadCounter::ThreadCounter() { pid = getpid(); }
-
-ThreadCounter::~ThreadCounter() {}
-
-int ThreadCounter::count()
-{
- size_t len = 0, olen = 0;
- struct kinfo_proc* kipp = NULL;
- int name[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD, pid };
- int error = sysctl(name, 4, NULL, &len, NULL, 0);
- if (len == 0 || (error < 0 && errno != EPERM)) {
- goto fail;
- }
- do
- {
- len += len / 10;
- kipp = (struct kinfo_proc *) reallocf(kipp, len);
- if (kipp == NULL)
- {
- goto fail;
- }
- olen = len;
- error = sysctl(name, 4, kipp, &len, NULL, 0);
- } while (error < 0 && errno == ENOMEM && olen == len);
-
- if (error < 0 && errno != EPERM) {
- goto fail;
- }
- return len / sizeof(*kipp);
-
-fail:
- if (kipp)
- free(kipp);
- return 0;}
-#endif
int spawnProcess(const std::string& cmd, const StringVector& args)
{
diff --git a/common/Util-windows.cpp b/common/Util-windows.cpp
new file mode 100644
index 0000000000000..3c6be365ad572
--- /dev/null
+++ b/common/Util-windows.cpp
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * Copyright the Collabora Online contributors.
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include
+
+#include
+
+#include
+
+#include
+
+namespace Util
+{
+ namespace rng
+ {
+ std::vector getBytes(const std::size_t length)
+ {
+ std::vector v(length);
+
+ static std::random_device rd;
+ size_t offset;
+ size_t byteoffset = 0;
+ std::random_device::result_type buffer;
+ for (offset = 0; offset < length; offset++)
+ {
+ if (offset % sizeof(std::random_device::result_type) == 0)
+ buffer = rd();
+ v[offset] = (buffer >> byteoffset) & 0xFF;
+ byteoffset = (byteoffset + 8) % (8 * sizeof(std::random_device::result_type));
+ }
+
+ return v;
+ }
+ } // namespace rng
+
+ long getProcessId()
+ {
+ return _getpid();
+ }
+
+ std::tm *time_t_to_localtime(std::time_t t, std::tm& tm)
+ {
+ if (localtime_s(&tm, &t) != 0)
+ return nullptr;
+ return &tm;
+ }
+
+ std::tm *time_t_to_gmtime(std::time_t t, std::tm& tm)
+ {
+ if (gmtime_s(&tm, &t) != 0)
+ return nullptr;
+ return &tm;
+ }
+
+ std::wstring string_to_wide_string(const std::string& string)
+ {
+ if (string.empty())
+ {
+ return L"";
+ }
+
+ const auto size_needed = MultiByteToWideChar(CP_UTF8, 0, string.data(), (int)string.size(), nullptr, 0);
+ if (size_needed <= 0)
+ {
+ throw std::runtime_error("MultiByteToWideChar() failed: " + std::to_string(size_needed));
+ }
+
+ std::wstring result(size_needed, 0);
+ MultiByteToWideChar(CP_UTF8, 0, string.data(), (int)string.size(), result.data(), size_needed);
+
+ return result;
+ }
+
+ std::string wide_string_to_string(const std::wstring& wide_string)
+ {
+ if (wide_string.empty())
+ {
+ return "";
+ }
+
+ const auto size_needed = WideCharToMultiByte(CP_UTF8, 0, wide_string.data(), (int)wide_string.size(), nullptr, 0, nullptr, nullptr);
+ if (size_needed <= 0)
+ {
+ throw std::runtime_error("WideCharToMultiByte() failed: " + std::to_string(size_needed));
+ }
+
+ std::string result(size_needed, 0);
+ WideCharToMultiByte(CP_UTF8, 0, wide_string.data(), (int)wide_string.size(), result.data(), size_needed, nullptr, nullptr);
+
+ return result;
+ }
+} // namespace Util
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/Util.cpp b/common/Util.cpp
index 3c43a4f93e3d0..aea8f3df645b5 100644
--- a/common/Util.cpp
+++ b/common/Util.cpp
@@ -37,21 +37,16 @@
#include
#include
#include
-#include
-#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
#include
-#include
#include
-#include
#ifndef COOLWSD_BUILDCONFIG
#define COOLWSD_BUILDCONFIG
@@ -61,7 +56,7 @@
#include "SigHandlerTrap.hpp"
#endif
-#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
+#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(_WIN32)
# include
# include
#endif
@@ -77,6 +72,14 @@
#import
#endif
+#ifndef _WIN32
+#include
+#include
+#include
+#include
+#include
+#endif
+
#if defined __GLIBC__
#include
#if defined(M_TRIM_THRESHOLD)
@@ -88,6 +91,10 @@
#include
#endif
+#ifdef _WIN32
+#include
+#endif
+
// for version info
#include
#if ENABLE_SSL
@@ -264,6 +271,14 @@ namespace Util
if (!ThreadTid)
thr_self(&ThreadTid);
return ThreadTid;
+#elif defined __APPLE__
+ if (!ThreadTid)
+ {
+ uint64_t tid;
+ if (pthread_threadid_np(NULL, &tid) == 0)
+ ThreadTid = tid;
+ }
+ return ThreadTid;
#else
static long threadCounter = 1;
if (!ThreadTid)
@@ -277,6 +292,7 @@ namespace Util
#if defined __linux__
::syscall(SYS_tgkill, getpid(), tid, signal);
#else
+ (void) signal;
LOG_WRN("No tgkill for thread " << tid);
#endif
}
@@ -313,6 +329,9 @@ namespace Util
LOG_INF("Thread " << getThreadId() << ") is now called [" << s << ']');
#elif defined __EMSCRIPTEN__
emscripten_console_logf("COOL thread name: \"%s\"", s.c_str());
+#elif defined _WIN32
+ SetThreadDescription(GetCurrentThread(), string_to_wide_string(s).c_str());
+ LOG_INF("Thread " << getThreadId() << ") is now called [" << s << ']');
#endif
// Emit a metadata Trace Event identifying this thread. This will invoke a different function
@@ -945,6 +964,8 @@ namespace Util
free(rawSymbols);
}
}
+#else
+ (void) maxFrames;
#endif
if (0 == _frames.size())
{
diff --git a/common/Util.hpp b/common/Util.hpp
index d057a79953285..981bc0cddeffc 100644
--- a/common/Util.hpp
+++ b/common/Util.hpp
@@ -27,6 +27,7 @@
#include
#include
#include
+
#include
#include
@@ -172,41 +173,28 @@ namespace Util
uint64_t _startSys;
};
- class DirectoryCounter
- {
- void *_tasks;
- public:
- DirectoryCounter(const char *procPath);
- ~DirectoryCounter();
- /// Get number of items in this directory or -1 on error
- int count();
- };
+ class CounterImpl;
- #ifdef __FreeBSD__
/// Needs to open dirent before forking in Kit process
class ThreadCounter
{
- pid_t pid;
+ std::unique_ptr _impl;
public:
ThreadCounter();
~ThreadCounter();
/// Get number of items in this directory or -1 on error
int count();
};
- #else
- /// Needs to open dirent before forking in Kit process
- class ThreadCounter : public DirectoryCounter
- {
- public:
- ThreadCounter() : DirectoryCounter("/proc/self/task") {}
- };
- #endif
/// Needs to open dirent before forking in Kit process
- class FDCounter : public DirectoryCounter
+ class FDCounter
{
+ std::unique_ptr _impl;
public:
- FDCounter() : DirectoryCounter("/proc/self/fd") {}
+ FDCounter();
+ ~FDCounter();
+ /// Get number of items in this directory or -1 on error
+ int count();
};
/// Spawn a process.
@@ -1461,6 +1449,10 @@ int main(int argc, char**argv)
/// Base-64 decode the given input.
std::string base64Decode(const std::string& input);
+#ifdef _WIN32
+ std::wstring string_to_wide_string(const std::string& string);
+ std::string wide_string_to_string(const std::wstring& wide_string);
+#endif
} // end namespace Util
inline std::ostream& operator<<(std::ostream& os, const std::chrono::system_clock::time_point& ts)
diff --git a/config.h.in b/config.h.in
index 47072a7f8fe97..84815c7454c71 100644
--- a/config.h.in
+++ b/config.h.in
@@ -81,6 +81,9 @@
/* Define to 1 if the `memrchr' function is available, otherwise 0. */
#define HAVE_MEMRCHR 0
+/* Define to 1 if the `pipe2' function is available, otherwise 0. */
+#define HAVE_PIPE2 0
+
/* Default value of help root URL */
#undef HELP_URL
@@ -111,6 +114,12 @@
/* Define to 1 if this is a mobileapp (eg. Android) build. */
#undef MOBILEAPP
+/* Define to 1 when we should use systemplate and jails (eg. normal coolwsd server), otherwise 0 (eg. CODA). */
+#undef ENABLE_CHILDROOTS
+
+/* Define to 1 when the compiled code is supposed to be part of CODA (the entry point is from the app, not from main() etc.), otherwise 0 (ie. normal server). */
+#undef ENABLE_CODA
+
/* Default value of feature_lock.unlock_description */
#undef UNLOCK_DESCRIPTION
@@ -135,9 +144,20 @@
/* Define to 1 if the "fallback to Wasm" capability is enabled: */
#define ENABLE_WASM_SUPPORT 0
+/* Define to 1 if building for macOS */
+#undef MACOS
+
/* Makes config variables conditionally static, only in non-debug builds, to allow for overriding them in unit-tests. */
#undef CONFIG_STATIC_TYPE
+/* This top-level config.h is used only in "normal" COOL, thus we
+ * don't need the Windows version of EXPORT here, and not EXTERNC
+ * either.
+ */
+
+#define EXTERNC /**/
+#define EXPORT /**/
+
/* Controls whether or not we allow loading documents from the local filesystem. */
#ifndef ENABLE_LOCAL_FILESYSTEM
#if MOBILEAPP || ENABLE_DEBUG
diff --git a/configure.ac b/configure.ac
index 8c822f4038d84..72495cfad03f5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,7 +11,9 @@ AM_INIT_AUTOMAKE([1.10 foreign subdir-objects tar-pax -Wno-portability])
AC_CONFIG_MACRO_DIR([m4])
# We don't want to require pkg-config and PKG_CHECK_MODULES on macOS
-m4_if(m4_esyscmd_s([uname -s]),Darwin,[m4_define([PKG_CHECK_MODULES],[])],[])
+# It may be missing, and then we'd generate broken shell code; but
+# let's guard against accidental use.
+m4_if(m4_esyscmd_s([uname -s]),Darwin,[m4_define([PKG_CHECK_MODULES],[AC_MSG_ERROR([It's not expected to use [PKG_CHECK_MODULES] on macOS, please fix configure.ac and make pkg-config a requirement if you really need it.])])],[])
COOLWSD_VERSION_MAJOR=`echo $VERSION | awk -F. '{print $1}'`
COOLWSD_VERSION_MINOR=`echo $VERSION | awk -F. '{print $2}'`
@@ -64,12 +66,6 @@ AC_DEFINE_UNQUOTED([COOLWSD_BUILDCONFIG],[["$ac_configure_args"]],[Options passe
# don't include config_unused.h anywhere!
AC_CONFIG_HEADERS([config_unused.h])
-# maintain config.h.in and config_version.h.in manually
-# config.h.in is for configurable variables that are stable if configure parameters are unchanged
-AC_CONFIG_HEADERS([config.h])
-# config_version.h.in is for version/hash related variables
-AC_CONFIG_HEADERS([config_version.h])
-
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
@@ -142,6 +138,22 @@ AC_ARG_ENABLE([iosapp],
AS_HELP_STRING([--enable-iosapp],
[Use on a Mac where you will build the iOS app.]))
+AC_ARG_ENABLE([macosapp],
+ AS_HELP_STRING([--enable-macosapp],
+ [Use on a Mac where you will build the macOS app.]))
+
+AC_ARG_ENABLE([windowsapp],
+ AS_HELP_STRING([--enable-windowsapp],
+ [Use in WSL2 on a Windows PC where you will build the Windows app that works
+ similarly to the iOS app, from the JavaScript and the pseudo WebSocket
+ message plumbing point of view.]))
+
+AC_ARG_ENABLE([coda],
+ AS_HELP_STRING([--enable-coda],
+ [Use to build a CODA app. This will build the entire coolwsd, so that it runs under the hood
+ inside the app. Do NOT use together with --enable-*app, this is supposed to be a
+ different approach that allows multiple documents open at the same time.]))
+
AC_ARG_WITH([wasm-fallback],
AS_HELP_STRING([--with-wasm-fallback=],
[Build a COOL where the client can fall back to a WASM implementation if the connection to the server fails.
@@ -253,7 +265,7 @@ AC_ARG_WITH([lokit-path],
AC_ARG_WITH([lo-path],
AS_HELP_STRING([--with-lo-path=],
- [Path to a working installation directory or instdir of LibreOffice]))
+ [Path to a working installation directory or instdir of LibreOffice, to be used at runtime]))
AC_ARG_WITH([lo-sourcedir],
AS_HELP_STRING([--with-lo-sourcedir=],
@@ -316,6 +328,16 @@ AC_ARG_WITH([libpng-libs],
[Path to the "lib" directory with the libpng
libraries. Not used on Android.]))
+AC_ARG_WITH([zlib-includes],
+ AS_HELP_STRING([--with-zlib-includes=],
+ [Path to the "include" directory with the libpng
+ headers. Only used on Windows.]))
+
+AC_ARG_WITH([zlib-libs],
+ AS_HELP_STRING([--with-zlib-libs=],
+ [Path to the "lib" directory with the zlib
+ libraries. Only used on Windows.]))
+
AC_ARG_WITH([cppunit-includes],
AS_HELP_STRING([--with-cppunit-includes=],
[Path to the "include" directory with the Cppunit headers]))
@@ -425,8 +447,9 @@ AC_ARG_ENABLE([experimental],
AS_HELP_STRING([--enable-experimental],
[Enable experimental features and behavior]))
-# Handle options
-AS_IF([test "$enable_debug" = yes -a -n "$with_poco_libs"],
+# Debug POCO libraries are named like 'PocoNetSSLd' instead of just 'PocoNetSSL',
+# but Homebrew does not have those, so avoid this suffix for macOS
+AS_IF([test "$enable_debug" = yes -a -n "$with_poco_libs" -a `uname -s` != Darwin],
[POCO_DEBUG_SUFFIX=d],
[POCO_DEBUG_SUFFIX=])
@@ -625,7 +648,7 @@ ZSTDLIB_X86=
ZSTDLIB_X86_64=
CORE_VERSION_HASH=""
-if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_androidapp" = "yes" \) -o "$host_os" = "emscripten" ; then
+if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_windowsapp" = "yes" \) -o \( "$enable_androidapp" = "yes" \) -o "$host_os" = "emscripten" ; then
if test "$enable_androidapp" = "yes" ; then
AC_MSG_CHECKING([for Android ABI to build for])
if test -n "$with_android_abi" ; then
@@ -649,7 +672,7 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
AC_MSG_CHECKING([for LibreOffice build tree to build against])
if test -z "$with_lo_builddir"; then
- AC_MSG_ERROR([You MUST use the --with-lo-builddir option when configuring the mobile app build tree.])
+ AC_MSG_ERROR([You MUST use the --with-lo-builddir option when configuring the mobile or Windows app build tree.])
fi
LOBUILDDIR="$with_lo_builddir"
@@ -678,16 +701,22 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
LOBUILDDIR=`readlink -f "$LOBUILDDIR"`
- # Get the git hash of the core build
- CORE_VERSION_HASH=`cd $LOBUILDDIR && grep buildid instdir/program/setuprc | sed -e 's/buildid=//' -e 's/............................$//'`
+ # Get the git hash of the core build.
+ # Also use the setuprc or setup.ini file existence as a sanity check.
+ if test "$enable_windowsapp" = "yes"; then
+ setuprc=setup.ini
+ else
+ setuprc=setuprc
+ fi
- # Sanity check, just a random object file in the LibreOffice build tree
- if test \( "$enable_iosapp" = "yes" -a -f "$LOBUILDDIR/workdir/CxxObject/vcl/ios/iosinst.o" \) -o \( "$enable_androidapp" = "yes" -a -f "$LOBUILDDIR/workdir/LinkTarget/StaticLibrary/liblibpng.a" \) -o \( "$host_os" = "emscripten" -a -f "${LOBUILDDIR}/workdir/LinkTarget/StaticLibrary/liblibpng.a" \); then
- AC_MSG_RESULT([$LOBUILDDIR])
+ if test -f "$LOBUILDDIR/instdir/program/$setuprc"; then
+ AC_MSG_RESULT([$LOBUILDDIR])
else
- AC_MSG_ERROR([This is not a LibreOffice core build directory: $LOBUILDDIR])
+ AC_MSG_ERROR([This is not a LibreOffice core build directory: $LOBUILDDIR])
fi
+ CORE_VERSION_HASH=`grep buildid $LOBUILDDIR/instdir/program/$setuprc | sed -e 's/buildid=//' -e 's/............................$//'`
+
if test -n "$LOBUILDDIR_ARM64_V8A" ; then
if test -f "$LOBUILDDIR_ARM64_V8A/workdir/LinkTarget/StaticLibrary/liblibpng.a" ; then
AC_MSG_RESULT([$LOBUILDDIR_ARM64_V8A])
@@ -712,9 +741,13 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
fi
fi
+ if test "$enable_windowsapp" = "yes" ; then
+ LOBUILDDIR_WINDOWS=`cd "$LOBUILDDIR" && cmd.exe /c cd | tr '\\\\' '/' | tr -d '\r'`
+ fi
+
AC_MSG_CHECKING([for Poco include directory to build against])
if test -z "$with_poco_includes"; then
- AC_MSG_ERROR([You MUST use the --with-poco-includes option when configuring the mobile app build tree.])
+ AC_MSG_ERROR([You MUST use the --with-poco-includes option when configuring the mobile or Windows app build tree.])
fi
POCOINCLUDE="$with_poco_includes"
@@ -743,6 +776,10 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
CHK_FILE_VAR(POCOINCLUDE,Poco/Poco.h,Poco include,_X86)
CHK_FILE_VAR(POCOINCLUDE,Poco/Poco.h,Poco include,_X86_64)
+ if test "$enable_windowsapp" = "yes" ; then
+ POCOINCLUDE_WINDOWS=`cd "$POCOINCLUDE" && cmd.exe /c cd | tr '\\\\' '/' | tr -d '\r'`
+ fi
+
AC_MSG_CHECKING([for Poco lib directory to build against])
if test -z "$with_poco_libs"; then
AC_MSG_ERROR([You MUST use the --with-poco-libs option when configuring the mobile app build tree.])
@@ -769,18 +806,31 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
fi
# Sanity checks
- CHK_FILE_VAR(POCOLIB,libPocoFoundation.a,Poco lib)
- CHK_FILE_VAR(POCOLIB,libPocoFoundation.a,Poco lib,_ARM64_V8A)
- CHK_FILE_VAR(POCOLIB,libPocoFoundation.a,Poco lib,_X86)
- CHK_FILE_VAR(POCOLIB,libPocoFoundation.a,Poco lib,_X86_64)
+ if test "$enable_windowsapp" = "yes"; then
+ CHK_FILE_VAR(POCOLIB,PocoFoundationmd.lib,Poco lib)
+ CHK_FILE_VAR(POCOLIB,PocoFoundationmdd.lib,Poco lib)
+ else
+ CHK_FILE_VAR(POCOLIB,libPocoFoundation.a,Poco lib)
+ CHK_FILE_VAR(POCOLIB,libPocoFoundation.a,Poco lib,_ARM64_V8A)
+ CHK_FILE_VAR(POCOLIB,libPocoFoundation.a,Poco lib,_X86)
+ CHK_FILE_VAR(POCOLIB,libPocoFoundation.a,Poco lib,_X86_64)
+ fi
+
+ if test "$enable_windowsapp" = "yes" ; then
+ POCOLIB_WINDOWS=`cd "$POCOLIB" && cmd.exe /c cd | tr '\\\\' '/' | tr -d '\r'`
+ fi
if test "$ENABLE_DEBUG" = "true" ; then
POCODEBUG=d
fi
- AC_MSG_CHECKING([for zstd lib directory to build against])
+ AC_MSG_CHECKING([for zstd include directory to build against])
+ if test -z "$with_zstd_includes"; then
+ AC_MSG_ERROR([You MUST use the --with-zstd-includes option when configuring the mobile or Windows app build tree.])
+ fi
+
if test -z "$with_zstd_libs"; then
- AC_MSG_ERROR([You MUST use the --with-zstd-libs option when configuring the mobile app build tree.])
+ AC_MSG_ERROR([You MUST use the --with-zstd-libs option when configuring the mobile or Windows app build tree.])
fi
ZSTDINCLUDE="$with_zstd_includes"
@@ -809,6 +859,12 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
CHK_FILE_VAR(ZSTDINCLUDE,zstd.h,Zstd include,_X86)
CHK_FILE_VAR(ZSTDINCLUDE,zstd.h,Zstd include,_X86_64)
+ if test "$enable_windowsapp" = "yes" ; then
+ ZSTDINCLUDE_WINDOWS=`cd "$ZSTDINCLUDE" && cmd.exe /c cd | tr '\\\\' '/' | tr -d '\r'`
+ fi
+
+ AC_MSG_CHECKING([for zstd lib directory to build against])
+
ZSTDLIB="$with_zstd_libs"
if test "$enable_androidapp" = "yes" ; then
if echo "$ZSTDLIB" | grep -qs ':' ; then
@@ -829,14 +885,23 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
fi
fi
- CHK_FILE_VAR(ZSTDLIB,libzstd.a,Zstd lib)
- CHK_FILE_VAR(ZSTDLIB,libzstd.a,Zstd lib,_ARM64_V8A)
- CHK_FILE_VAR(ZSTDLIB,libzstd.a,Zstd lib,_X86)
- CHK_FILE_VAR(ZSTDLIB,libzstd.a,Zstd lib,_X86_64)
+ if test "$enable_windowsapp"; then
+ CHK_FILE_VAR(ZSTDLIB,libzstd_static.lib,Zstd lib)
+ else
+ CHK_FILE_VAR(ZSTDLIB,libzstd.a,Zstd lib)
+ CHK_FILE_VAR(ZSTDLIB,libzstd.a,Zstd lib,_ARM64_V8A)
+ CHK_FILE_VAR(ZSTDLIB,libzstd.a,Zstd lib,_X86)
+ CHK_FILE_VAR(ZSTDLIB,libzstd.a,Zstd lib,_X86_64)
+ fi
+
+ if test "$enable_windowsapp" = "yes" ; then
+ ZSTDLIB_WINDOWS=`cd "$ZSTDLIB" && cmd.exe /c cd | tr '\\\\' '/' | tr -d '\r'`
+ fi
fi
AC_SUBST(LOSOURCEDIR)
AC_SUBST(LOBUILDDIR)
+AC_SUBST(LOBUILDDIR_WINDOWS)
AC_SUBST(ANDROID_ABI)
AC_SUBST(ANDROID_ABI_SPLIT)
AC_SUBST(LOBUILDDIR_ARM64_V8A)
@@ -846,19 +911,23 @@ AC_SUBST(POCOINCLUDE)
AC_SUBST(POCOINCLUDE_ARM64_V8A)
AC_SUBST(POCOINCLUDE_X86)
AC_SUBST(POCOINCLUDE_X86_64)
+AC_SUBST(POCOINCLUDE_WINDOWS)
AC_SUBST(POCOLIB)
AC_SUBST(POCOLIB_ARM64_V8A)
AC_SUBST(POCOLIB_X86)
AC_SUBST(POCOLIB_X86_64)
+AC_SUBST(POCOLIB_WINDOWS)
AC_SUBST(POCODEBUG)
AC_SUBST(ZSTDINCLUDE)
AC_SUBST(ZSTDINCLUDE_ARM64_V8A)
AC_SUBST(ZSTDINCLUDE_X86)
AC_SUBST(ZSTDINCLUDE_X86_64)
+AC_SUBST(ZSTDINCLUDE_WINDOWS)
AC_SUBST(ZSTDLIB)
AC_SUBST(ZSTDLIB_ARM64_V8A)
AC_SUBST(ZSTDLIB_X86)
AC_SUBST(ZSTDLIB_X86_64)
+AC_SUBST(ZSTDLIB_WINDOWS)
AC_SUBST([CORE_VERSION_HASH])
AC_DEFINE_UNQUOTED([CORE_VERSION_HASH],[["$CORE_VERSION_HASH"]],[LibreOffice core git hash if present])
@@ -866,24 +935,44 @@ AC_DEFINE_UNQUOTED([CORE_VERSION_HASH],[["$CORE_VERSION_HASH"]],[LibreOffice cor
LIBPNG_INCLUDES="$with_libpng_includes"
LIBPNG_LIBS="$with_libpng_libs"
-if test "$enable_iosapp" != yes -a "$enable_androidapp" != yes; then
+if test "$enable_windowsapp" = "yes" ; then
+ LIBPNG_INCLUDES_WINDOWS=`cd "$LIBPNG_INCLUDES" && cmd.exe /c cd | tr '\\\\' '/' | tr -d '\r'`
+ LIBPNG_LIBS_WINDOWS=`cd "$LIBPNG_LIBS" && cmd.exe /c cd | tr '\\\\' '/' | tr -d '\r'`
+fi
+
+AC_SUBST(LIBPNG_INCLUDES)
+AC_SUBST(LIBPNG_INCLUDES_WINDOWS)
+AC_SUBST(LIBPNG_LIBS)
+AC_SUBST(LIBPNG_LIBS_WINDOWS)
+
+ZLIB_INCLUDES="$with_zlib_includes"
+ZLIB_LIBS="$with_zlib_libs"
+
+if test "$enable_windowsapp" = "yes" ; then
+ ZLIB_INCLUDES_WINDOWS=`cd "$ZLIB_INCLUDES" && cmd.exe /c cd | tr '\\\\' '/' | tr -d '\r'`
+ ZLIB_LIBS_WINDOWS=`cd "$ZLIB_LIBS" && cmd.exe /c cd | tr '\\\\' '/' | tr -d '\r'`
+fi
+
+AC_SUBST(ZLIB_INCLUDES_WINDOWS)
+AC_SUBST(ZLIB_LIBS_WINDOWS)
+
+if test "$enable_iosapp" != yes -a "$enable_windowsapp" != yes -a "$enable_androidapp" != yes; then
LOKIT_PATH=`readlink -f $with_lokit_path`
fi
if test "$enable_iosapp" = "yes" -o "$enable_androidapp" = "yes"; then
if test -n "$LIBPNG_INCLUDES" ; then
- AC_MSG_ERROR([--with-libpng-includes is ignored on Android, please remove the parameter.])
+ AC_MSG_ERROR([--with-libpng-includes is ignored on iOS / Android, please remove the parameter.])
fi
if test -n "$LIBPNG_LIBS" ; then
- AC_MSG_ERROR([--with-libpng-libs is ignored on Android, please remove the parameter.])
+ AC_MSG_ERROR([--with-libpng-libs is ignored on iOS / Android, please remove the parameter.])
fi
if test -n "$LOKIT_PATH" ; then
- AC_MSG_ERROR([--with-lokit-path is ignored on Android, please remove the parameter.])
+ AC_MSG_ERROR([--with-lokit-path is ignored on iOS / Android, please remove the parameter.])
LOKIT_PATH=`readlink -f $LOBUILDDIR/include`
fi
fi
-AC_SUBST(LIBPNG_INCLUDES)
-AC_SUBST(LIBPNG_LIBS)
+
AC_SUBST(LOKIT_PATH)
ENABLE_ANDROIDAPP=
@@ -913,8 +1002,8 @@ AC_SUBST(APP_NAME)
VENDOR=
AC_MSG_CHECKING([for vendor])
-if test "$enable_iosapp" = yes -a -z "$with_vendor"; then
- AC_MSG_ERROR([You must use --with-vendor when configuring for the iOS app])
+if test \( "$enable_iosapp" = yes -o "$enable_macosapp" = yes \) -a -z "$with_vendor"; then
+ AC_MSG_ERROR([You must use --with-vendor when configuring for the iOS / macOS app])
fi
if test -z "$with_vendor" -o "$with_vendor" = "no"; then
VENDOR="$USERNAME"
@@ -960,6 +1049,49 @@ AM_CONDITIONAL([ENABLE_IOSAPP], [test "$ENABLE_IOSAPP" = "true"])
AC_SUBST(IOSAPP_BUNDLE_SHORT_VERSION)
AC_SUBST(IOSAPP_BUNDLE_VERSION)
+# Check if we are on macOS (darwin) to define "MACOS" in config.h
+# We want this to be general, regardless of using --enable-macosapp, and it
+# could be potentially extended to handle the other defines like IOS, WASMAPP
+# or _WINDOWS (but at least iOS and Windows are using their own config.h)
+ENABLE_MACOS=
+case "$host_os" in
+ darwin*)
+ ENABLE_MACOS=true
+ AC_DEFINE([MACOS], [1], [Define to 1 if building for macOS])
+ ;;
+esac
+
+# Are we building on macOS at all, but eg. only the coolwsd?
+AC_SUBST(ENABLE_MACOS)
+AM_CONDITIONAL([ENABLE_MACOS], [test "$ENABLE_MACOS" = "true"])
+
+ENABLE_MACOSAPP=
+#MACOSAPP_BUNDLE_VERSION=
+
+if test "$enable_macosapp" = "yes"; then
+ ENABLE_MACOSAPP=true
+
+ #if test -f BUNDLE-VERSION; then
+ # MACOSAPP_BUNDLE_VERSION=$(cat BUNDLE-VERSION)
+ #else
+ # MACOSAPP_BUNDLE_VERSION=1
+ #fi
+ #echo $MACOSAPP_BUNDLE_VERSION >BUNDLE-VERSION
+fi
+
+# Are we building the macOS CODA?
+AC_SUBST(ENABLE_MACOSAPP)
+AM_CONDITIONAL([ENABLE_MACOSAPP], [test "$ENABLE_MACOSAPP" = "true"])
+#AC_SUBST(MACOSAPP_BUNDLE_VERSION)
+
+ENABLE_WINDOWSAPP=
+
+if test "$enable_windowsapp" = "yes"; then
+ ENABLE_WINDOWSAPP=true
+fi
+AC_SUBST(ENABLE_WINDOWSAPP)
+AM_CONDITIONAL([ENABLE_WINDOWSAPP], [test "$ENABLE_WINDOWSAPP" = "true"])
+
AC_MSG_CHECKING([for custom icon theme])
CUSTOM_ICONS_DIRECTORY=
if test -d "$with_icon_theme"; then
@@ -1273,7 +1405,16 @@ AS_IF([test "$ENABLE_IOSAPP" != "true" -a "$ENABLE_ANDROIDAPP" != "true"],
[AC_MSG_CHECKING([for LibreOffice path])
if test -n "$with_lo_path"; then
# strip trailing '/' from LO_PATH, 'ln -s' with such path will otherwise fail
- LO_PATH=`readlink -f ${with_lo_path%/}`
+ if test "$ENABLE_WINDOWSAPP" != "true"; then
+ LO_PATH=`readlink -f ${with_lo_path%/}`
+ else
+ # LO_PATH is the path used at run-time to find the
+ # LibreOffice shared libraries. When running configure
+ # for the CODA-W app it is not used at configure or
+ # build time. Thus it should be a Windows path and not a
+ # WSL one.
+ LO_PATH="$with_lo_path"
+ fi
AC_MSG_RESULT([found])
else
AC_MSG_RESULT([not found])
@@ -1287,6 +1428,7 @@ SYSTEMPLATE_PATH=not-set
have_lo_path=false
AC_MSG_CHECKING([whether to run tests against a LibreOffice])
version_file="$with_lo_path/program/versionrc"
+version_file_macos="$with_lo_path/Contents/Resources/versionrc"
if test -f $version_file; then
JAILS_PATH="\${abs_top_builddir}/jails"
CACHE_PATH="\${abs_top_builddir}/cache"
@@ -1294,6 +1436,12 @@ if test -f $version_file; then
have_lo_path=true
lo_msg="test against $LO_PATH"
AC_MSG_RESULT([yes])
+elif test `uname -s` = Darwin -a -f "$version_file_macos"; then
+ JAILS_PATH="\${abs_top_builddir}/jails"
+ SYSTEMPLATE_PATH="\${abs_top_builddir}/systemplate"
+ have_lo_path=true
+ lo_msg="test against $LO_PATH"
+ AC_MSG_RESULT([yes])
else
lo_msg="no integration tests"
AC_MSG_RESULT([no])
@@ -1385,8 +1533,8 @@ AM_CONDITIONAL([ENABLE_DEBUG], [test "$ENABLE_DEBUG" = "true"])
mobile_app=
ENABLE_MOBILEAPP=
MOBILEAPP=0
-AC_MSG_CHECKING([if this is a mobile app])
-if test "$enable_gtkapp" = "yes" -o "$enable_iosapp" = "yes" -o "$enable_androidapp" = "yes" -o "$host_os" = "emscripten" ; then
+AC_MSG_CHECKING([whether this is a mobile app])
+if test "$enable_gtkapp" = "yes" -o "$enable_iosapp" = "yes" -o "$enable_macosapp" = "yes" -o "$enable_windowsapp" = "yes" -o "$enable_androidapp" = "yes" -o "$host_os" = "emscripten" ; then
AC_MSG_RESULT([yes])
mobile_app=true;
MOBILEAPP=1
@@ -1398,6 +1546,23 @@ AC_DEFINE_UNQUOTED([MOBILEAPP],[$MOBILEAPP],[Define to 1 if this is a mobileapp
AC_SUBST(ENABLE_MOBILEAPP)
AM_CONDITIONAL([ENABLE_MOBILEAPP], [test "$ENABLE_MOBILEAPP" = "true"])
+AC_MSG_CHECKING([whether to enable the CODA build])
+if test "$enable_coda" = "yes"; then
+ AC_MSG_RESULT([yes])
+ if test "$ENABLE_MOBILEAPP" = "true" ; then
+ AC_MSG_ERROR([--enable-coda conflicts with --enable-(androidapp|gtkapp|iosapp|macosapp|windowsapp), please decide for one or the other.])
+ fi
+ if test "$ENABLE_DEBUG" != "true" ; then
+ AC_MSG_ERROR([--enable-coda currently depends on --enable-debug, please add it to ./configure. Many things haven't been updated to build a production CODA app yet.])
+ fi
+ AC_DEFINE([ENABLE_CHILDROOTS],0,[Disable systemplate and jails when building CODA.])
+ AC_DEFINE([ENABLE_CODA],1,[Enable the CODA build - disable the main() function in COOLWSD etc.])
+else
+ AC_MSG_RESULT([no])
+ AC_DEFINE([ENABLE_CHILDROOTS],1,[Normal server, enable systemplate and jails.])
+ AC_DEFINE([ENABLE_CODA],0,[Disable the CODA build - normal server, the main() function in COOLWSD is the entry point, etc.])
+fi
+
if test "$host_os" = "emscripten" ; then
ENABLE_EMSCRIPTENAPP=true
fi
@@ -1501,7 +1666,7 @@ fi
AS_IF([test "$ENABLE_SSL" = "true"],
[LIBS="$LIBS -lssl -lcrypto"])
-AS_IF([test "$ENABLE_IOSAPP" != "true" -a "$ENABLE_ANDROIDAPP" != "true" -a "$host_os" != "emscripten"],
+AS_IF([test "$ENABLE_IOSAPP" != "true" -a "$ENABLE_WINDOWSAPP" != "true" -a "$ENABLE_ANDROIDAPP" != "true" -a "$host_os" != "emscripten"],
[AC_CHECK_HEADERS([LibreOfficeKit/LibreOfficeKit.h],
[],
[AC_MSG_ERROR([header LibreOfficeKit/LibreOfficeKit.h not found, perhaps you want to use --with-lokit-path])])
@@ -1617,24 +1782,13 @@ AS_IF([test `uname -s` = "Linux" -o `uname -s` = "FreeBSD" -o `uname -s` = "Darw
])
AS_IF([test `uname -s` = "FreeBSD"],
- AC_CHECK_LIB(iconv, libiconv_open, [], [AC_MSG_ERROR([No. Install 3rd-party libiconv.])]))
+ [AC_CHECK_LIB(iconv, libiconv_open, [], [AC_MSG_ERROR([No. Install 3rd-party libiconv.])])],
+ [AS_IF([test `uname -s` = "Darwin" -a "$ENABLE_IOSAPP" != "true"],
+ [AC_CHECK_LIB(iconv, iconv_open, [], [AC_MSG_ERROR([No, please install libiconv.])])])])
AS_IF([test `uname -s` = "FreeBSD"],
[LIBS="$LIBS -lexecinfo -lz -liconv"])
-# need this after the other stuff that uses the compiler because we don't want to run configure-tests with the plugins enabled
-AS_IF([test -n "$with_compiler_plugins"],
- [CPPFLAGS="$CPPFLAGS -Xclang -load -Xclang ${with_compiler_plugins}/compilerplugins/obj/plugin.so -Xclang -add-plugin -Xclang loplugin -Xclang -plugin-arg-loplugin -Xclang --cool-base-path=\${abs_top_srcdir}"])
-
-# Clang plugin.
-AC_MSG_CHECKING([whether to build a clang plugin])
-if test "$enable_coplugin" = "yes"; then
- AC_MSG_RESULT([yes])
- CPPFLAGS="$CPPFLAGS -Xclang -load -Xclang \${abs_top_srcdir}/clang/plugin.so -Xclang -add-plugin -Xclang coplugin"
-else
- AC_MSG_RESULT([no])
-fi
-
if test "x${prefix}" = "xNONE"; then
prefix=/usr/local
fi
@@ -1727,8 +1881,11 @@ AS_IF([test "$ENABLE_IOSAPP" = "true"],
])
AC_SUBST(IOSAPP_FONTS)
+# TODO MACOSAPP_FONTS?
+
AC_CHECK_FUNCS(ppoll)
AC_CHECK_FUNCS([memrchr])
+AC_CHECK_FUNCS([pipe2])
ENABLE_CYPRESS=false
if test "$enable_cypress" = "yes"; then
@@ -1979,12 +2136,46 @@ AC_CONFIG_FILES([Makefile
coolwsd.xml
debian/coolwsd.postinst])
+# maintain config.h.in and config_version.h.in manually
+# config.h.in is for configurable variables that are stable if configure parameters are unchanged
+if test "$enable_iosapp" != "yes" -a "$enable_windowsapp" != "yes"; then
+ AC_CONFIG_HEADERS([config.h])
+fi
+
+# config_version.h.in is for version/hash related variables
+AC_CONFIG_HEADERS([config_version.h])
+
if test "$enable_iosapp" = "yes"; then
AC_CONFIG_FILES([ios/config.h
ios/Mobile/Info.plist
ios/Mobile/Resources/Settings.bundle/Root.plist])
fi
+if test `uname -s` = "Darwin"; then
+ # Homebrew uses different paths on Intel and Apple Silicon macOS.
+ # Having a separate /opt/homebrew makes more sense, but I guess
+ # backward compatibility keeps it as /usr/local on Intel Macs.
+ if test `uname -m` = "x86_64"; then
+ HOMEBREW_PATH=/usr/local
+ else
+ HOMEBREW_PATH=/opt/homebrew
+ fi
+ AC_SUBST(HOMEBREW_PATH)
+ AC_CONFIG_FILES([macos/coda/Config.xcconfig
+ macos/coolwsd/gmake-wrapper.sh],
+ [chmod +x macos/coolwsd/gmake-wrapper.sh])
+fi
+
+if test "$enable_windowsapp" = "yes"; then
+ # Don't use AC_CONFIG_HEADERS for windows/coda/config.h because we
+ # don't want to set random stuff that the configure script thinks
+ # it has detected, as that is very misleading because it is run on
+ # WSL. Ideally we shouldn't need to run the configure script when
+ # building CODA-W at all. Later.
+ AC_CONFIG_FILES([windows/coda/config.h
+ windows/coda/config.props])
+fi
+
if test "$enable_androidapp" = "yes"; then
AC_CONFIG_FILES([
$srcdir/android/variables.gradle:android/variables.gradle.in
@@ -2069,6 +2260,23 @@ AS_IF([test "$ENABLE_IOSAPP" = "true"],
[
])
+dnl !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+dnl Set plugins.so in CPPFLAGS as the last thing - not to interfere with other checks
+dnl !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+# need this after the other stuff that uses the compiler because we don't want to run configure-tests with the plugins enabled
+AS_IF([test -n "$with_compiler_plugins"],
+ [CPPFLAGS="$CPPFLAGS -Xclang -load -Xclang ${with_compiler_plugins}/compilerplugins/obj/plugin.so -Xclang -add-plugin -Xclang loplugin -Xclang -plugin-arg-loplugin -Xclang --cool-base-path=\${abs_top_srcdir}"])
+
+# Clang plugin.
+AC_MSG_CHECKING([whether to build a clang plugin])
+if test "$enable_coplugin" = "yes"; then
+ AC_MSG_RESULT([yes])
+ CPPFLAGS="$CPPFLAGS -Xclang -load -Xclang \${abs_top_srcdir}/clang/plugin.so -Xclang -add-plugin -Xclang coplugin"
+else
+ AC_MSG_RESULT([no])
+fi
+
echo "
Configuration:
LOKit path ${lokit_msg}
diff --git a/g b/g
index 4856ec591a21f..d15368f400538 100755
--- a/g
+++ b/g
@@ -81,7 +81,7 @@ if [ "$1" == "review" ]; then
exit 1
elif [ -n "$HAS_REMOTE_BRANCH" ] && [ -n "$CUSTOM_BRANCH" ]; then
# PR is open, same branch is explicitly specified, just update it.
- git push -f $REMOTE HEAD:$REMOTE_BRANCH
+ git push --force-with-lease $REMOTE HEAD:$REMOTE_BRANCH
else
# Open a new PR.
git push $REMOTE HEAD:$REMOTE_BRANCH
diff --git a/gtk/mobile.cpp b/gtk/mobile.cpp
index e0e25d55f0e22..607bd247d107f 100644
--- a/gtk/mobile.cpp
+++ b/gtk/mobile.cpp
@@ -292,7 +292,7 @@ int main(int argc, char* argv[])
_exit(1); // avoid log cleanup
}
- Log::initialize("Mobile", "trace", false, false, {}, false, {});
+ Log::initialize("Mobile", "trace");
Util::setThreadName("main");
fakeSocketSetLoggingCallback([](const std::string& line)
diff --git a/ios/Mobile.xcodeproj/project.pbxproj b/ios/Mobile.xcodeproj/project.pbxproj
index c75f13cb16663..5e815bcd2e87b 100644
--- a/ios/Mobile.xcodeproj/project.pbxproj
+++ b/ios/Mobile.xcodeproj/project.pbxproj
@@ -11,7 +11,6 @@
1FCFA28A2B2AF13F007EE2DF /* coolwsd-fork.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1FCFA2892B2AF13C007EE2DF /* coolwsd-fork.cpp */; };
3F3A77D22C59B04000947249 /* global.js in Resources */ = {isa = PBXBuildFile; fileRef = 3F3A77D12C59B04000947249 /* global.js */; };
3F3B54DD2A3928D100063C01 /* HttpRequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F3B54DB2A39288500063C01 /* HttpRequest.cpp */; };
- 3F3B54E02A392CCB00063C01 /* NetUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F3B54DE2A392C9C00063C01 /* NetUtil.cpp */; };
A5C2FA5A2AC1BB3900265946 /* Simd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5C2FA592AC1BB3800265946 /* Simd.cpp */; };
A5C2FA5D2AC1BEC500265946 /* DeltaSimd.c in Sources */ = {isa = PBXBuildFile; fileRef = A5C2FA5B2AC1BEC500265946 /* DeltaSimd.c */; };
BE00F8A021396585001CE2D4 /* cool.html in Resources */ = {isa = PBXBuildFile; fileRef = BE00F89621396585001CE2D4 /* cool.html */; };
@@ -40,9 +39,9 @@
BE5EB5CF213FE2D000E0826C /* ClientSession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5CC213FE2D000E0826C /* ClientSession.cpp */; };
BE5EB5D0213FE2D000E0826C /* TileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5CD213FE2D000E0826C /* TileCache.cpp */; };
BE5EB5D22140039100E0826C /* COOLWSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5D12140039100E0826C /* COOLWSD.cpp */; };
+ BE5EB5D22140039100E0826D /* ClientRequestDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5D12140039100E0826D /* ClientRequestDispatcher.cpp */; };
BE5EB5D22140039100E0836C /* KitWSDGlobals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5D12140039100E0836C /* KitWSDGlobals.cpp */; };
BE5EB5D22140039101E0927D /* dumpWsdState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5D12140039110E0836F /* dumpWsdState.cpp */; };
- BE5EB5D22140039100E0826D /* ClientRequestDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5D12140039100E0826D /* ClientRequestDispatcher.cpp */; };
BE5EB5D421400DC100E0826C /* DocumentBroker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5D321400DC100E0826C /* DocumentBroker.cpp */; };
BE5EB5D621401E0F00E0826C /* Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5D521401E0F00E0826C /* Storage.cpp */; };
BE5EB5DA2140363100E0826C /* ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5D92140363100E0826C /* ios.mm */; };
@@ -116,8 +115,6 @@
3F3A77D12C59B04000947249 /* global.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = global.js; path = ../../../browser/dist/global.js; sourceTree = ""; };
3F3B54DB2A39288500063C01 /* HttpRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HttpRequest.cpp; sourceTree = ""; };
3F3B54DC2A39288500063C01 /* HttpRequest.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = HttpRequest.hpp; sourceTree = ""; };
- 3F3B54DE2A392C9C00063C01 /* NetUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetUtil.cpp; sourceTree = ""; };
- 3F3B54DF2A392C9C00063C01 /* NetUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = NetUtil.hpp; sourceTree = ""; };
3F3B54DF2A392C9C00063C02 /* Uri.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Uri.hpp; sourceTree = ""; };
92414564BE942221FB823CF9 /* AsyncDNS.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AsyncDNS.hpp; sourceTree = ""; };
A5C2FA582AC1BB3800265946 /* Simd.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Simd.hpp; sourceTree = ""; };
@@ -619,9 +616,9 @@
BE5EB5CC213FE2D000E0826C /* ClientSession.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClientSession.cpp; sourceTree = ""; };
BE5EB5CD213FE2D000E0826C /* TileCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TileCache.cpp; sourceTree = ""; };
BE5EB5D12140039100E0826C /* COOLWSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = COOLWSD.cpp; sourceTree = ""; };
+ BE5EB5D12140039100E0826D /* ClientRequestDispatcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClientRequestDispatcher.cpp; sourceTree = ""; };
BE5EB5D12140039100E0836C /* KitWSDGlobals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KitWSDGlobals.cpp; sourceTree = ""; };
BE5EB5D12140039110E0836F /* dumpWsdState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dumpWsdState.cpp; sourceTree = ""; };
- BE5EB5D12140039100E0826D /* ClientRequestDispatcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClientRequestDispatcher.cpp; sourceTree = ""; };
BE5EB5D321400DC100E0826C /* DocumentBroker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DocumentBroker.cpp; sourceTree = ""; };
BE5EB5D521401E0F00E0826C /* Storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Storage.cpp; sourceTree = ""; };
BE5EB5D92140363100E0826C /* ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios.mm; path = ../../ios/ios.mm; sourceTree = ""; };
@@ -3012,8 +3009,6 @@
92414564BE942221FB823CF9 /* AsyncDNS.hpp */,
3F3B54DB2A39288500063C01 /* HttpRequest.cpp */,
3F3B54DC2A39288500063C01 /* HttpRequest.hpp */,
- 3F3B54DE2A392C9C00063C01 /* NetUtil.cpp */,
- 3F3B54DF2A392C9C00063C01 /* NetUtil.hpp */,
3F3B54DF2A392C9C00063C02 /* Uri.hpp */,
BEA2835C21498AD400848631 /* Socket.cpp */,
BEA2835E214A8E2000848631 /* Socket.hpp */,
@@ -3778,12 +3773,11 @@
BE980C592CEB557A00FED7BC /* FileUtil-apple.mm in Sources */,
BE5EB5D22140039100E0826C /* COOLWSD.cpp in Sources */,
BE5EB5D22140039100E0836C /* KitWSDGlobals.cpp in Sources */,
- BE5EB5D22140039101E0927D /* dumpWsdState.cpp in Sources */,
+ BE5EB5D22140039101E0927D /* dumpWsdState.cpp in Sources */,
BE5EB5D22140039100E0826D /* ClientRequestDispatcher.cpp in Sources */,
BEFB1EE121C29CC70081D757 /* L10n.mm in Sources */,
BEDCC8992456FFAD00FB02BD /* SceneDelegate.mm in Sources */,
BE5EB5C6213FE29900E0826C /* SigUtil-dummy.cpp in Sources */,
- 3F3B54E02A392CCB00063C01 /* NetUtil.cpp in Sources */,
BE5EB5C1213FE29900E0826C /* Log.cpp in Sources */,
A5C2FA5A2AC1BB3900265946 /* Simd.cpp in Sources */,
BEDCC84E2452F82800FB02BD /* MobileApp.cpp in Sources */,
diff --git a/ios/Mobile/AppDelegate.mm b/ios/Mobile/AppDelegate.mm
index c65b2c5f3a356..ec50e2fba1ca9 100644
--- a/ios/Mobile/AppDelegate.mm
+++ b/ios/Mobile/AppDelegate.mm
@@ -51,7 +51,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
else
setupKitEnvironment("");
- Log::initialize("Mobile", trace, false, false, {}, false, {});
+ Log::initialize("Mobile", trace);
Util::setThreadName("main");
// Clear the cache directory if it is for another build of the app
diff --git a/ios/Mobile/DocumentViewController.mm b/ios/Mobile/DocumentViewController.mm
index bd37341273102..72fe01811ff09 100644
--- a/ios/Mobile/DocumentViewController.mm
+++ b/ios/Mobile/DocumentViewController.mm
@@ -594,7 +594,7 @@ - (void)userContentController:(WKUserContentController *)userContentController d
p.events = POLLOUT;
fakeSocketPoll(&p, 1, -1);
- // This is read in the iOS-specific code in ClientRequestDispatcher::handleIncomingMessage() in COOLWSD.cpp
+ // This is read in the code in ClientRequestDispatcher::handleIncomingMessage()
std::string message(url + " " + std::to_string(self.document->appDocId));
fakeSocketWrite(self.document->fakeClientFd, message.c_str(), message.size());
diff --git a/ios/config.h.in b/ios/config.h.in
index dbb2c99ae0bfe..0b0198cbf16bc 100644
--- a/ios/config.h.in
+++ b/ios/config.h.in
@@ -138,3 +138,10 @@
/* Version number of package */
/* #undef VERSION */
+
+/* This config.h is used only for iOS and we don't need the Windows
+ * version of EXPORT here, and not EXTERNC either.
+ */
+
+#define EXTERNC /**/
+#define EXPORT /**/
diff --git a/kit/DeltaSimd.c b/kit/DeltaSimd.c
index 6ca34b0fd43a6..21179765a025f 100644
--- a/kit/DeltaSimd.c
+++ b/kit/DeltaSimd.c
@@ -20,7 +20,6 @@
#include
#include
#include
-#include
#include "DeltaSimd.h"
diff --git a/kit/ForKit.cpp b/kit/ForKit.cpp
index 2240400aadac3..ee2dd3bbe0562 100644
--- a/kit/ForKit.cpp
+++ b/kit/ForKit.cpp
@@ -51,6 +51,7 @@
#include
#include
#include
+#include
#include
namespace
@@ -346,6 +347,7 @@ void cleanupChildren(const std::string& childRoot)
}
else if (status == SIGKILL)
{
+#if !defined(MACOS)
// TODO differentiate with docker
if (info.si_code == SI_KERNEL)
{
@@ -354,6 +356,7 @@ void cleanupChildren(const std::string& childRoot)
<< status);
}
else
+#endif
{
++killedCount;
LOG_WRN("Child " << exitedChildPid << " was killed, with status "
@@ -529,7 +532,11 @@ int createLibreOfficeKit(const std::string& childRoot, const std::string& sysTem
std::string jailId = Util::rng::getFilename(16);
// Update the dynamic files as necessary.
+#if ENABLE_CHILDROOTS
const bool sysTemplateIncomplete = !JailUtil::SysTemplate::updateDynamicFiles(sysTemplate);
+#else
+ const bool sysTemplateIncomplete = false;
+#endif
// Used to label the spare kit instances
static size_t spareKitId = 0;
@@ -542,7 +549,11 @@ int createLibreOfficeKit(const std::string& childRoot, const std::string& sysTem
if (Util::isKitInProcess())
{
std::thread([childRoot, jailId = std::move(jailId), configId, sysTemplate,
- loTemplate, queryVersion, sysTemplateIncomplete] {
+ loTemplate, queryVersion
+#if ENABLE_CHILDROOTS
+ , sysTemplateIncomplete
+#endif
+ ] {
sleepForDebugger();
lokit_main(childRoot, jailId, configId, sysTemplate, loTemplate, true,
true, false, queryVersion, DisplayVersion,
@@ -906,7 +917,7 @@ int forkit_main(int argc, char** argv)
else if (std::strstr(cmd, "--masterport=") == cmd)
{
eq = std::strchr(cmd, '=');
- MasterLocation = std::string(eq+1);
+ MasterLocation = UnxSocketPath(std::string(eq+1));
}
else if (std::strstr(cmd, "--version") == cmd)
{
@@ -1019,11 +1030,13 @@ int forkit_main(int argc, char** argv)
if (Util::ThreadCounter().count() != 1)
LOG_ERR("forkit has more than a single thread after pre-init" << Util::ThreadCounter().count());
+#if ENABLE_CHILDROOTS
// Link the network and system files in sysTemplate, if possible.
JailUtil::SysTemplate::setupDynamicFiles(sysTemplate);
// Make dev/[u]random point to the writable devices in tmp/dev/.
JailUtil::SysTemplate::setupRandomDeviceLinks(sysTemplate);
+#endif
if (!Util::isKitInProcess())
{
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index cd1b8a5a2df5b..96d3859225632 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -36,7 +36,7 @@
#include
#endif
-#if defined(__FreeBSD__)
+#if defined(__FreeBSD__) || defined(MACOS)
#include
#define FTW_CONTINUE 0
#define FTW_STOP (-1)
@@ -44,12 +44,14 @@
#define FTW_ACTIONRETVAL 0
#endif
+#ifndef _WIN32
#include
#include
#include
#include
#include
#include
+#endif
#include
#include
@@ -98,6 +100,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -113,6 +116,13 @@
#ifdef IOS
#include "ios.h"
#include "DocumentBroker.hpp"
+#elif defined(MACOS) && MOBILEAPP
+#include "macos.h"
+#include "DocumentBroker.hpp"
+#endif
+
+#ifdef _WIN32
+#include "windows.hpp"
#endif
using Poco::Exception;
@@ -827,7 +837,7 @@ Document::~Document()
session.second->resetDocManager();
}
-#ifdef IOS
+#if defined(IOS) || defined(MACOS) || defined(_WIN32)
DocumentData::deallocate(_mobileAppDocId);
#endif
@@ -2075,7 +2085,7 @@ std::shared_ptr Document::load(const std::shared_ptr(duration);
LOG_DBG("Returned lokit::documentLoad(" << anonymizeUrl(url) << ") in " << elapsed);
-#ifdef IOS
+#if defined(IOS) || defined(MACOS) || defined(_WIN32)
DocumentData::get(_mobileAppDocId).loKitDocument = _loKitDocument.get();
{
std::unique_lock docBrokersLock(DocBrokersMutex);
@@ -3427,7 +3437,11 @@ void lokit_main(
= std::chrono::steady_clock::now();
userdir_url = "file:///tmp/user";
+#ifndef __APPLE__
instdir_path = '/' + std::string(JailUtil::LO_JAIL_SUBPATH) + "/program";
+#else
+ instdir_path = '/' + std::string(JailUtil::LO_JAIL_SUBPATH) + "/Contents/Frameworks";
+#endif
allowedPaths += ":r:/" + std::string(JailUtil::LO_JAIL_SUBPATH);
Poco::Path jailLOInstallation(jailPath, JailUtil::LO_JAIL_SUBPATH);
@@ -3453,6 +3467,7 @@ void lokit_main(
const std::string sysTemplateSubDir = Poco::Path(tempRoot, "systemplate-" + jailId).toString();
const std::string jailEtcDir = Poco::Path(jailPath, "etc").toString();
+#if ENABLE_CHILDROOTS
if (sysTemplateIncomplete && JailUtil::isBindMountingEnabled())
{
const std::string sysTemplateEtcDir = Poco::Path(sysTemplate, "etc").toString();
@@ -3471,6 +3486,7 @@ void lokit_main(
JailUtil::disableBindMounting(); // We can't mount from incomplete systemplate.
}
}
+#endif
// The bind-mount implementation: inlined here to mirror
// the fallback link/copy version bellow.
@@ -3669,6 +3685,7 @@ void lokit_main(
linkGCDAFiles(jailPathStr);
#endif
+#if ENABLE_CHILDROOTS
// Update the dynamic files inside the jail.
if (!JailUtil::SysTemplate::updateDynamicFiles(jailPathStr))
{
@@ -3679,6 +3696,7 @@ void lokit_main(
"read-only, running the installation scripts with the owner's account "
"should update these files. Some functionality may be missing.");
}
+#endif
if (usingMountNamespace)
{
@@ -3758,7 +3776,11 @@ void lokit_main(
LOG_INF("Using template ["
<< loTemplate << "] as install subpath directly, without chroot jail setup.");
userdir_url = "file://" + jailPathStr + "tmp/user";
+#ifndef __APPLE__
instdir_path = '/' + loTemplate + "/program";
+#else
+ instdir_path = '/' + loTemplate + "/Contents/Frameworks";
+#endif
allowedPaths += ":r:" + loTemplate;
JailRoot = jailPathStr;
@@ -3932,14 +3954,18 @@ void lokit_main(
#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__FreeBSD__)
Poco::URI userInstallationURI("file", LO_PATH);
LibreOfficeKit *kit = lok_init_2(LO_PATH "/program", userInstallationURI.toString().c_str());
-#else
-
-#ifdef IOS // In the iOS app we call lok_init_2() just once, when the app starts
+#elif defined(MACOS)
+ // this is the MACOS MOBILEAPP case
+ LibreOfficeKit *kit = lok_init_2((getBundlePath() + "/Contents/lokit/Frameworks").c_str(), getAppSupportURL().c_str());
+#elif defined(IOS) // In the iOS app we call lok_init_2() just once, when the app starts
static LibreOfficeKit *kit = lo_kit;
+#elif defined(_WIN32)
+ LibreOfficeKit *kit = lok_init_2
+ ((app_installation_path + "lo\\program").c_str(),
+ (app_installation_uri + "lo").c_str());
#else
+ // FIXME: I wonder for which platform this is supposed to be? Android?
static LibreOfficeKit *kit = lok_init_2(nullptr, nullptr);
-#endif
-
#endif
assert(kit);
@@ -3968,7 +3994,7 @@ void lokit_main(
if (isURPEnabled())
{
- if (pipe2(URPtoLoFDs, O_CLOEXEC) != 0 || pipe2(URPfromLoFDs, O_CLOEXEC | O_NONBLOCK) != 0)
+ if (Syscall::pipe2(URPtoLoFDs, O_CLOEXEC) != 0 || Syscall::pipe2(URPfromLoFDs, O_CLOEXEC | O_NONBLOCK) != 0)
LOG_ERR("Failed to create urp pipe " << strerror(errno));
else
{
@@ -4097,7 +4123,11 @@ void consistencyCheckJail()
if ((failedTmp = (!tmp.good() || !tmp.isDirectory())))
LOG_ERR("Fatal system error: Kit jail is missing its /tmp directory");
+#ifndef __APPLE__
FileUtil::Stat lo(InstDirPath + "/unorc");
+#else
+ FileUtil::Stat lo(InstDirPath + "/../Resources/ure/etc/unorc");
+#endif
if ((failedLo = (!lo.good() || !lo.isFile())))
LOG_ERR("Fatal system error: Kit jail is missing its LibreOfficeKit directory at '" << InstDirPath << "'");
@@ -4212,7 +4242,11 @@ bool globalPreinit(const std::string &loTemplate)
// we deliberately don't dlclose handle on success, make it
// static so static analysis doesn't see this as a leak
static void *handle;
+#ifndef __APPLE__
std::string libMerged = loTemplate + "/program/libmergedlo.so";
+#else
+ std::string libMerged = loTemplate + "/Contents/Frameworks/libmergedlo.dylib";
+#endif
if (File(libMerged).exists())
{
LOG_TRC("dlopen(" << libMerged << ", RTLD_GLOBAL|RTLD_NOW)");
@@ -4226,7 +4260,11 @@ bool globalPreinit(const std::string &loTemplate)
}
else
{
+#ifndef __APPLE__
std::string libSofficeapp = loTemplate + "/program/libsofficeapp.so";
+#else
+ std::string libSofficeapp = loTemplate + "/Contents/Frameworks/libsofficeapp.dylib";
+#endif
if (File(libSofficeapp).exists())
{
LOG_TRC("dlopen(" << libSofficeapp << ", RTLD_GLOBAL|RTLD_NOW)");
@@ -4271,9 +4309,15 @@ bool globalPreinit(const std::string &loTemplate)
"javaloader javavm jdbc rpt rptui rptxml ",
0 /* no overwrite */);
- LOG_TRC("Invoking lok_preinit_2(" << loTemplate << "/program\", \"file:///tmp/user\")");
+#ifndef __APPLE__
+ const std::string lokProgramDir = loTemplate + "/program";
+#else
+ const std::string lokProgramDir = loTemplate + "/Contents/Frameworks";
+#endif
+
+ LOG_TRC("Invoking lok_preinit_2(" << lokProgramDir << ", \"file:///tmp/user\")");
const auto start = std::chrono::steady_clock::now();
- if (preInit((loTemplate + "/program").c_str(), "file:///tmp/user", &loKitPtr) != 0)
+ if (preInit(lokProgramDir.c_str(), "file:///tmp/user", &loKitPtr) != 0)
{
LOG_FTL("lok_preinit() in " << loadedLibrary << " failed");
dlclose(handle);
@@ -4282,7 +4326,7 @@ bool globalPreinit(const std::string &loTemplate)
LOG_DBG("After lok_preinit_2: loKitPtr=" << loKitPtr);
- LOG_TRC("Finished lok_preinit(" << loTemplate << "/program\", \"file:///tmp/user\") in "
+ LOG_TRC("Finished lok_preinit(" << lokProgramDir << ", \"file:///tmp/user\") in "
<< std::chrono::duration_cast(
std::chrono::steady_clock::now() - start));
return true;
@@ -4303,15 +4347,15 @@ std::string anonymizeUsername(const std::string& username)
void dump_kit_state()
{
std::ostringstream oss(Util::makeDumpStateStream());
- oss << "Start Kit " << getpid() << " Dump State:\n";
+ oss << "Start Kit " << Util::getProcessId() << " Dump State:\n";
SigUtil::signalLogActivity();
KitSocketPoll::dumpGlobalState(oss);
- oss << "\nMalloc info [" << getpid() << "]: \n\t"
+ oss << "\nMalloc info [" << Util::getProcessId() << "]: \n\t"
<< Util::replace(Util::getMallocInfo(), "\n", "\n\t") << '\n';
- oss << "\nEnd Kit " << getpid() << " Dump State.\n";
+ oss << "\nEnd Kit " << Util::getProcessId() << " Dump State.\n";
const std::string msg = oss.str();
fprintf(stderr, "%s", msg.c_str()); // Log in the journal.
diff --git a/kit/KitWebSocket.cpp b/kit/KitWebSocket.cpp
index 97329c745cba0..97dad5b2a7e68 100644
--- a/kit/KitWebSocket.cpp
+++ b/kit/KitWebSocket.cpp
@@ -17,8 +17,10 @@
#include
+#ifndef _WIN32
#include
#include
+#endif
#include
diff --git a/kit/KitWebSocket.hpp b/kit/KitWebSocket.hpp
index fc4d59a558a18..459ec783a9ce7 100644
--- a/kit/KitWebSocket.hpp
+++ b/kit/KitWebSocket.hpp
@@ -51,8 +51,6 @@ class KitWebSocketHandler final : public WebSocketHandler
void shutdownForBackgroundSave();
- int getKitId() const { return _mobileAppDocId; }
-
protected:
virtual void handleMessage(const std::vector& data) override;
virtual void enableProcessInput(bool enable = true) override;
diff --git a/kit/SetupKitEnvironment.hpp b/kit/SetupKitEnvironment.hpp
index b265d3fe3b3fb..c64b42f79fefb 100644
--- a/kit/SetupKitEnvironment.hpp
+++ b/kit/SetupKitEnvironment.hpp
@@ -16,18 +16,29 @@
#include
+#if defined(MACOS) && MOBILEAPP
+#include
+#endif
+
inline void setupKitEnvironment(const std::string& userInterface)
{
// Setup & check environment
std::string layers(
+#if defined(MACOS)
+ "xcsxcu:${BRAND_BASE_DIR}/Resources/registry "
+ "res:${BRAND_BASE_DIR}/Resources/registry "
+#else
"xcsxcu:${BRAND_BASE_DIR}/share/registry "
"res:${BRAND_BASE_DIR}/share/registry "
+#endif
"bundledext:${${BRAND_BASE_DIR}/program/lounorc:BUNDLED_EXTENSIONS_USER}/registry/com.sun.star.comp.deployment.configuration.PackageRegistryBackend/configmgr.ini "
"sharedext:${${BRAND_BASE_DIR}/program/lounorc:SHARED_EXTENSIONS_USER}/registry/com.sun.star.comp.deployment.configuration.PackageRegistryBackend/configmgr.ini "
"userext:${${BRAND_BASE_DIR}/program/lounorc:UNO_USER_PACKAGES_CACHE}/registry/com.sun.star.comp.deployment.configuration.PackageRegistryBackend/configmgr.ini "
);
#ifdef IOS
layers += "user:*${BRAND_BASE_DIR}/coolkitconfig.xcu ";
+#elif defined(MACOS) && MOBILEAPP
+ layers += "user:*" + getResourceURL("coolkitconfig", "xcu");
#elif ENABLE_DEBUG && !defined(ANDROID) // '*' denotes non-writable.
layers += "user:*file://" DEBUG_ABSSRCDIR "/coolkitconfig.xcu ";
#else
@@ -53,6 +64,12 @@ inline void setupKitEnvironment(const std::string& userInterface)
options += ":sc_print_twips_msgs";
+#ifdef MACOS
+ // The "legacy way" (with repeating fonts) crashes CODA at the moment,
+ // enable the new behavior
+ options += ":compact_fonts";
+#endif
+
::setenv("SAL_LOK_OPTIONS", options.c_str(), 0);
}
diff --git a/kit/forkit-main.cpp b/kit/forkit-main.cpp
index c405253d6b8fb..6be3829b560fc 100644
--- a/kit/forkit-main.cpp
+++ b/kit/forkit-main.cpp
@@ -12,9 +12,10 @@
#include "config.h"
#include "Common.hpp"
#include "Kit.hpp"
+#include "ServerSocket.hpp"
int ClientPortNumber = DEFAULT_CLIENT_PORT_NUMBER;
-std::string MasterLocation;
+UnxSocketPath MasterLocation;
int main (int argc, char **argv)
{
diff --git a/macos/README.md b/macos/README.md
new file mode 100644
index 0000000000000..fc1a4b1b39b78
--- /dev/null
+++ b/macos/README.md
@@ -0,0 +1,122 @@
+# Building the CODA-M
+
+## Setup
+
+* Instal node.js
+ * brew install node
+* Install poco
+ * brew install poco
+* zstd
+ * brew install zstd
+* missing from the iOS build instructions (needed for online.git)
+ * brew install libtool
+* zlib
+ * brew install zlib
+* libpng
+ * brew install libpng
+* cppunit
+ * brew install cppunit
+
+* install dependencies for the canvas@next
+ * brew install cairo
+ * brew install pango
+ * /opt/homebrew/bin/pip3 install --break-system-packages lxml
+ * /opt/homebrew/bin/pip3 install --break-system-packages polib
+
+* install canvas to avoid error during build (complains about node-pre-gyp)
+ * NB. version 3.0 needed, it upgrades the API to fit the new node.js
+ * npm install canvas@next
+ * It might be that you should run the above in the browser subdirectory
+ of your online directory: (cd browser && npm install canvas@next)
+
+* Install and/or update the Command Line Tools for Xcode:
+ * xcode-select --install
+ * After that you might need to update them in System Settings > General > Software Updates
+ * For some reason for me it lists both 15.3 and 16.0 there. As I have Xcode 16.0, I choose just that one.
+
+## Build LO
+
+You need the 'coda' branch for that, and have to use the following
+autogen.input.
+
+NOTE: I build with stuff installed via 'brew', and not via 'lode'; if you have
+too many things installed via 'brew', compilation may fail for you due to
+incompatible stuff.
+
+autogen.input:
+
+ # Distro
+ --with-distro=CPMacOS-LOKit
+ --enable-headless
+ --disable-mergelibs
+
+ # Overrides for the debug builds
+ --enable-debug
+ #--enable-dbgutil
+
+ --enable-werror
+ --enable-symbols
+ --without-lang
+ --without-system-dicts
+ --without-myspell-dicts
+
+Configure Collabora Online
+
+ ./autogen.sh && ./configure \
+ --enable-macosapp \
+ --enable-experimental \
+ --with-app-name="Collabora Office" \
+ --with-vendor="Collabora Productivity" \
+ --with-poco-includes=/opt/homebrew/opt/poco/include \
+ --with-poco-libs=/opt/homebrew/opt/poco/lib \
+ --with-zstd-includes=/opt/homebrew/include \
+ --with-zstd-libs=/opt/homebrew/lib \
+ --with-lo-path=/Users/kendy/Projects/lo/core/instdir/CollaboraOffice.app \
+ --with-lokit-path=/Users/kendy/Projects/lo/core/include
+
+Obbiously you need to change the /Users/kendy/... above to match what
+you have. Also, on Intel Macs homebrew gets installed in /usr/local,
+not /opt/homebrew.
+
+If you find an instance of /Users/kendy hardcoded somewhere, please
+report that, it's a mistake and should be fixed.
+
+## Install branding
+
+Checkout online-branding, and install the branding like the following (please
+update the paths to the core instdir and online repo):
+
+./brand.sh ~/Projects/lo/core/instdir ../online/browser/dist 1
+
+## Then you can build CODA-M:
+
+* ( cd browser ; gmake )
+* open Xcode's project macos/coda/coda.xcodeproj & build from there
+
+# Building and debugging coolwsd directly in Xcode
+
+There is an additional Xcode project for easy building and debugging of
+coolwsd on macOS directly in Xcode. Configure everything as above, but use
+a slightly different ./configure (particularly notice the
+missing --enable-macosapp):
+
+ ./autogen.sh && ./configure \
+ --with-app-name="Collabora Office" \
+ --enable-experimental \
+ --enable-debug \
+ --with-vendor="Collabora Productivity" \
+ --with-poco-includes=/opt/homebrew/opt/poco/include \
+ --with-poco-libs=/opt/homebrew/opt/poco/lib \
+ --with-zstd-includes=/opt/homebrew/include \
+ --with-zstd-libs=/opt/homebrew/lib \
+ --with-lo-path=/Users/kendy/Projects/lo/core/instdir/CollaboraOffice.app \
+ --with-lokit-path=/Users/kendy/Projects/lo/core/include
+
+Then open the macos/coolwsd.xcodeproj project in Xcode and you can build, run
+and debug directly from Xcode.
+
+# TODO
+
+* configure.ac
+ * add sanity check for the lo builddir when configuring with —enable-macosapp
+ * MACOSAPP_FONTS
diff --git a/macos/coda/.gitignore b/macos/coda/.gitignore
new file mode 100644
index 0000000000000..437884a678219
--- /dev/null
+++ b/macos/coda/.gitignore
@@ -0,0 +1,3 @@
+/Config.xcconfig
+/coda.xcodeproj/project.xcworkspace/
+/coda.xcodeproj/xcuserdata/
diff --git a/macos/coda/Config.xcconfig.in b/macos/coda/Config.xcconfig.in
new file mode 100644
index 0000000000000..671b1ae7138f5
--- /dev/null
+++ b/macos/coda/Config.xcconfig.in
@@ -0,0 +1,16 @@
+//
+// Copyright the Collabora Online contributors.
+//
+// SPDX-License-Identifier: MPL-2.0
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// Configuration settings file format documentation can be found at:
+// https://help.apple.com/xcode/#/dev745c5c974
+
+HOMEBREW_PATH = "@HOMEBREW_PATH@"
+HEADER_SEARCH_PATHS = "@LOKIT_PATH@"
+GCC_PREPROCESSOR_DEFINITIONS = "COOLWSD_CONFIGDIR=\"@COOLWSD_CONFIGDIR@\"" "COOLWSD_LOGLEVEL=\"@COOLWSD_LOGLEVEL@\"" "NUM_PRESPAWN_CHILDREN=\"@NUM_PRESPAWN_CHILDREN@\""
+LO_PATH = @LO_PATH@
diff --git a/macos/coda/coda-Bridging-Header.h b/macos/coda/coda-Bridging-Header.h
new file mode 100644
index 0000000000000..739b135b2ec94
--- /dev/null
+++ b/macos/coda/coda-Bridging-Header.h
@@ -0,0 +1,5 @@
+//
+// Use this file to import your target's public headers that you would like to expose to Swift.
+//
+
+#import "COWrapper.h"
diff --git a/macos/coda/coda.xcodeproj/project.pbxproj b/macos/coda/coda.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000000..dd1545710ec2a
--- /dev/null
+++ b/macos/coda/coda.xcodeproj/project.pbxproj
@@ -0,0 +1,1029 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 77;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ BE16C9F02CD51F58005E5960 /* hello.odt in Resources */ = {isa = PBXBuildFile; fileRef = BE16C9EF2CD51F58005E5960 /* hello.odt */; };
+ BE22492C2CC79AD700385A9C /* cool.html in Resources */ = {isa = PBXBuildFile; fileRef = BE22492B2CC79AD700385A9C /* cool.html */; };
+ BE2252292CC79BAE00385A9C /* bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = BE22492E2CC79BAE00385A9C /* bundle.js */; };
+ BE22529E2CC79BAE00385A9C /* cool-help.html in Resources */ = {isa = PBXBuildFile; fileRef = BE22492F2CC79BAE00385A9C /* cool-help.html */; };
+ BE2253F32CC79BAE00385A9C /* device-desktop.css in Resources */ = {isa = PBXBuildFile; fileRef = BE2249302CC79BAE00385A9C /* device-desktop.css */; };
+ BE22557E2CC79BAE00385A9C /* global.js in Resources */ = {isa = PBXBuildFile; fileRef = BE2249312CC79BAE00385A9C /* global.js */; };
+ BE2257AD2CC79BAE00385A9C /* bundle.css in Resources */ = {isa = PBXBuildFile; fileRef = BE22492D2CC79BAE00385A9C /* bundle.css */; };
+ BE22AA9C2CC7AE7400385A9C /* images in Resources */ = {isa = PBXBuildFile; fileRef = BE22AA9B2CC7AE7400385A9C /* images */; };
+ BE44ACC02D7EFDE900BFA3B2 /* DummyTraceEventEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE44ACBF2D7EFDE900BFA3B2 /* DummyTraceEventEmitter.cpp */; };
+ BE44ACC22D7EFF1500BFA3B2 /* Util-mobile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE44ACC12D7EFF1500BFA3B2 /* Util-mobile.cpp */; };
+ BE44ACC52D7F00B000BFA3B2 /* ClientRequestDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE44ACC42D7F00B000BFA3B2 /* ClientRequestDispatcher.cpp */; };
+ BE44ACC72D7F011F00BFA3B2 /* FileUtil-apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = BE44ACC62D7F011F00BFA3B2 /* FileUtil-apple.mm */; };
+ BE44ACCA2D7F018700BFA3B2 /* SigUtil-mobile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE44ACC92D7F018700BFA3B2 /* SigUtil-mobile.cpp */; };
+ BE44ACCF2D7F067500BFA3B2 /* Crypto-stub.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE44ACCE2D7F067500BFA3B2 /* Crypto-stub.cpp */; };
+ BE44ACD12D7F073000BFA3B2 /* Unit-mobile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE44ACD02D7F073000BFA3B2 /* Unit-mobile.cpp */; };
+ BE44ACDA2D7F089200BFA3B2 /* RequestVettingStation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE44ACD92D7F089200BFA3B2 /* RequestVettingStation.cpp */; };
+ BE44ACFC2D7F1F6B00BFA3B2 /* branding-mobile.css in Resources */ = {isa = PBXBuildFile; fileRef = BE44ACF82D7F1F6B00BFA3B2 /* branding-mobile.css */; };
+ BE44ACFD2D7F1F6B00BFA3B2 /* branding-unsupported.js in Resources */ = {isa = PBXBuildFile; fileRef = BE44ACFB2D7F1F6B00BFA3B2 /* branding-unsupported.js */; };
+ BE44ACFE2D7F1F6B00BFA3B2 /* branding.js in Resources */ = {isa = PBXBuildFile; fileRef = BE44ACF62D7F1F6B00BFA3B2 /* branding.js */; };
+ BE44ACFF2D7F1F6B00BFA3B2 /* branding-desktop.css in Resources */ = {isa = PBXBuildFile; fileRef = BE44ACF72D7F1F6B00BFA3B2 /* branding-desktop.css */; };
+ BE44AD002D7F1F6B00BFA3B2 /* branding-tablet.css in Resources */ = {isa = PBXBuildFile; fileRef = BE44ACF92D7F1F6B00BFA3B2 /* branding-tablet.css */; };
+ BE44AD012D7F1F6B00BFA3B2 /* branding.css in Resources */ = {isa = PBXBuildFile; fileRef = BE44ACF52D7F1F6B00BFA3B2 /* branding.css */; };
+ BE44AD022D7F1F6B00BFA3B2 /* branding-unsupported.css in Resources */ = {isa = PBXBuildFile; fileRef = BE44ACFA2D7F1F6B00BFA3B2 /* branding-unsupported.css */; };
+ BE4C620E2D59E85500047DF2 /* MobileApp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE4C62062D59E85500047DF2 /* MobileApp.cpp */; };
+ BE4C620F2D59E85500047DF2 /* Util-macos.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE4C620C2D59E85500047DF2 /* Util-macos.cpp */; };
+ BE4C625E2D5A3DDA00047DF2 /* Kit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE4C625D2D5A3DDA00047DF2 /* Kit.cpp */; };
+ BE4C62612D5B736200047DF2 /* coolwsd.xml in Resources */ = {isa = PBXBuildFile; fileRef = BE4C62602D5B736200047DF2 /* coolwsd.xml */; };
+ BE5FDE372DAD3DCF00C48DB2 /* LogUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5FDE362DAD3DCF00C48DB2 /* LogUI.cpp */; };
+ BE9115AE2CECBD3100C597B2 /* FileUtil-unix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE9115AD2CECBD3100C597B2 /* FileUtil-unix.cpp */; };
+ BE9115B02CECBECF00C597B2 /* Util-unix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE9115AF2CECBECF00C597B2 /* Util-unix.cpp */; };
+ BEA2636D2CBE38E20007435A /* Uri.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA2636A2CBE38E20007435A /* Uri.cpp */; };
+ BEA2636E2CBE38E20007435A /* Util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA2636C2CBE38E20007435A /* Util.cpp */; };
+ BEA2636F2CBE38E20007435A /* Unit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA263682CBE38E20007435A /* Unit.cpp */; };
+ BEA263702CBE38E20007435A /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA263582CBE38E20007435A /* Log.cpp */; };
+ BEA263712CBE38E20007435A /* Simd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA263612CBE38E20007435A /* Simd.cpp */; };
+ BEA263722CBE38E20007435A /* FileUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA263562CBE38E20007435A /* FileUtil.cpp */; };
+ BEA263732CBE38E20007435A /* Session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA2635D2CBE38E20007435A /* Session.cpp */; };
+ BEA263742CBE38E20007435A /* TraceEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA263662CBE38E20007435A /* TraceEvent.cpp */; };
+ BEA263762CBE38E20007435A /* SpookyV2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA263622CBE38E20007435A /* SpookyV2.cpp */; };
+ BEA263772CBE38E20007435A /* ConfigUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA263552CBE38E20007435A /* ConfigUtil.cpp */; };
+ BEA263782CBE38E20007435A /* StringVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA263642CBE38E20007435A /* StringVector.cpp */; };
+ BEA263792CBE38E20007435A /* Protocol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA2635B2CBE38E20007435A /* Protocol.cpp */; };
+ BEA2637A2CBE38E20007435A /* Authorization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA263532CBE38E20007435A /* Authorization.cpp */; };
+ BEA264162CBE7A770007435A /* Config.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = BEA264152CBE7A770007435A /* Config.xcconfig */; };
+ BEA264922CBE986C0007435A /* KitQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA264902CBE986C0007435A /* KitQueue.cpp */; };
+ BEA264932CBE986C0007435A /* KitWebSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA264912CBE986C0007435A /* KitWebSocket.cpp */; };
+ BEA264942CBE986C0007435A /* ChildSession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA2648D2CBE986C0007435A /* ChildSession.cpp */; };
+ BEA264962CBE986C0007435A /* DeltaSimd.c in Sources */ = {isa = PBXBuildFile; fileRef = BEA2648C2CBE986C0007435A /* DeltaSimd.c */; };
+ BEA264A12CBE98CD0007435A /* Socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA2649E2CBE98CD0007435A /* Socket.cpp */; };
+ BEA264A32CBE98CD0007435A /* FakeSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA264982CBE98CD0007435A /* FakeSocket.cpp */; };
+ BEA264AD2CBE99340007435A /* Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA264AB2CBE99340007435A /* Storage.cpp */; };
+ BEA264AE2CBE99340007435A /* TileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA264AC2CBE99340007435A /* TileCache.cpp */; };
+ BEA264AF2CBE99340007435A /* COOLWSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA264A62CBE99340007435A /* COOLWSD.cpp */; };
+ BEA264B02CBE99340007435A /* ClientSession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA264A52CBE99340007435A /* ClientSession.cpp */; };
+ BEA264B12CBE99340007435A /* RequestDetails.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA264A92CBE99340007435A /* RequestDetails.cpp */; };
+ BEA264B32CBE99340007435A /* DocumentBroker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA264A72CBE99340007435A /* DocumentBroker.cpp */; };
+ BEEF3F862CE2139E00ABE785 /* coolkitconfig.xcu in Resources */ = {isa = PBXBuildFile; fileRef = BEEF3F852CE2139E00ABE785 /* coolkitconfig.xcu */; };
+ BEF5CE8F2D633CE60006FD77 /* discovery.xml in Resources */ = {isa = PBXBuildFile; fileRef = BEF5CE8E2D633CE60006FD77 /* discovery.xml */; };
+ BEF6F02D2DD1EC0200EA06BF /* RegexUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEF6F02C2DD1EC0200EA06BF /* RegexUtil.cpp */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ BECC4BBD2CBD3FA400A120B3 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = BECC4BA32CBD3FA300A120B3 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = BECC4BAA2CBD3FA300A120B3;
+ remoteInfo = coda;
+ };
+ BECC4BC72CBD3FA400A120B3 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = BECC4BA32CBD3FA300A120B3 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = BECC4BAA2CBD3FA300A120B3;
+ remoteInfo = coda;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ BE16C9EF2CD51F58005E5960 /* hello.odt */ = {isa = PBXFileReference; lastKnownFileType = file; name = hello.odt; path = ../../../test/data/hello.odt; sourceTree = ""; };
+ BE22492B2CC79AD700385A9C /* cool.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = cool.html; path = ../../../browser/dist/cool.html; sourceTree = ""; };
+ BE22492D2CC79BAE00385A9C /* bundle.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; name = bundle.css; path = ../../../browser/dist/bundle.css; sourceTree = ""; };
+ BE22492E2CC79BAE00385A9C /* bundle.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = bundle.js; path = ../../../browser/dist/bundle.js; sourceTree = ""; };
+ BE22492F2CC79BAE00385A9C /* cool-help.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = "cool-help.html"; path = "../../../browser/dist/cool-help.html"; sourceTree = ""; };
+ BE2249302CC79BAE00385A9C /* device-desktop.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; name = "device-desktop.css"; path = "../../../browser/dist/device-desktop.css"; sourceTree = ""; };
+ BE2249312CC79BAE00385A9C /* global.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = global.js; path = ../../../browser/dist/global.js; sourceTree = ""; };
+ BE22AA9B2CC7AE7400385A9C /* images */ = {isa = PBXFileReference; lastKnownFileType = folder; name = images; path = ../../../browser/dist/images; sourceTree = ""; };
+ BE44ACBF2D7EFDE900BFA3B2 /* DummyTraceEventEmitter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DummyTraceEventEmitter.cpp; path = ../../../../common/DummyTraceEventEmitter.cpp; sourceTree = ""; };
+ BE44ACC12D7EFF1500BFA3B2 /* Util-mobile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Util-mobile.cpp"; path = "../../../../common/Util-mobile.cpp"; sourceTree = ""; };
+ BE44ACC32D7F00B000BFA3B2 /* ClientRequestDispatcher.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ClientRequestDispatcher.hpp; path = ../../../../wsd/ClientRequestDispatcher.hpp; sourceTree = ""; };
+ BE44ACC42D7F00B000BFA3B2 /* ClientRequestDispatcher.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ClientRequestDispatcher.cpp; path = ../../../../wsd/ClientRequestDispatcher.cpp; sourceTree = ""; };
+ BE44ACC62D7F011F00BFA3B2 /* FileUtil-apple.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "FileUtil-apple.mm"; path = "../../../../common/FileUtil-apple.mm"; sourceTree = ""; };
+ BE44ACC82D7F018700BFA3B2 /* SigHandlerTrap.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = SigHandlerTrap.hpp; path = ../../../../common/SigHandlerTrap.hpp; sourceTree = ""; };
+ BE44ACC92D7F018700BFA3B2 /* SigUtil-mobile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "SigUtil-mobile.cpp"; path = "../../../../common/SigUtil-mobile.cpp"; sourceTree = ""; };
+ BE44ACCE2D7F067500BFA3B2 /* Crypto-stub.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Crypto-stub.cpp"; path = "../../../../common/Crypto-stub.cpp"; sourceTree = ""; };
+ BE44ACD02D7F073000BFA3B2 /* Unit-mobile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Unit-mobile.cpp"; path = "../../../../common/Unit-mobile.cpp"; sourceTree = ""; };
+ BE44ACD82D7F089200BFA3B2 /* RequestVettingStation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = RequestVettingStation.hpp; path = ../../../../wsd/RequestVettingStation.hpp; sourceTree = ""; };
+ BE44ACD92D7F089200BFA3B2 /* RequestVettingStation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = RequestVettingStation.cpp; path = ../../../../wsd/RequestVettingStation.cpp; sourceTree = ""; };
+ BE44ACF52D7F1F6B00BFA3B2 /* branding.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; name = branding.css; path = ../../../browser/dist/branding.css; sourceTree = ""; };
+ BE44ACF62D7F1F6B00BFA3B2 /* branding.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = branding.js; path = ../../../browser/dist/branding.js; sourceTree = ""; };
+ BE44ACF72D7F1F6B00BFA3B2 /* branding-desktop.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; name = "branding-desktop.css"; path = "../../../browser/dist/branding-desktop.css"; sourceTree = ""; };
+ BE44ACF82D7F1F6B00BFA3B2 /* branding-mobile.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; name = "branding-mobile.css"; path = "../../../browser/dist/branding-mobile.css"; sourceTree = ""; };
+ BE44ACF92D7F1F6B00BFA3B2 /* branding-tablet.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; name = "branding-tablet.css"; path = "../../../browser/dist/branding-tablet.css"; sourceTree = ""; };
+ BE44ACFA2D7F1F6B00BFA3B2 /* branding-unsupported.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; name = "branding-unsupported.css"; path = "../../../browser/dist/branding-unsupported.css"; sourceTree = ""; };
+ BE44ACFB2D7F1F6B00BFA3B2 /* branding-unsupported.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; name = "branding-unsupported.js"; path = "../../../browser/dist/branding-unsupported.js"; sourceTree = ""; };
+ BE4C61FF2D59E85500047DF2 /* Common.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Common.hpp; path = ../../../../common/Common.hpp; sourceTree = ""; };
+ BE4C62012D59E85500047DF2 /* Crypto.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Crypto.hpp; path = ../../../../common/Crypto.hpp; sourceTree = ""; };
+ BE4C62052D59E85500047DF2 /* MobileApp.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = MobileApp.hpp; path = ../../../../common/MobileApp.hpp; sourceTree = ""; };
+ BE4C62062D59E85500047DF2 /* MobileApp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MobileApp.cpp; path = ../../../../common/MobileApp.cpp; sourceTree = ""; };
+ BE4C620A2D59E85500047DF2 /* SpookyV2.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SpookyV2.h; path = ../../../../common/SpookyV2.h; sourceTree = ""; };
+ BE4C620C2D59E85500047DF2 /* Util-macos.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Util-macos.cpp"; path = "../../../../common/Util-macos.cpp"; sourceTree = ""; };
+ BE4C62252D59EBAC00047DF2 /* ClientSession.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ClientSession.hpp; path = ../../../../wsd/ClientSession.hpp; sourceTree = ""; };
+ BE4C62262D59EBAC00047DF2 /* COOLWSD.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = COOLWSD.hpp; path = ../../../../wsd/COOLWSD.hpp; sourceTree = ""; };
+ BE4C62272D59EBAC00047DF2 /* DocumentBroker.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DocumentBroker.hpp; path = ../../../../wsd/DocumentBroker.hpp; sourceTree = ""; };
+ BE4C623E2D59EBAC00047DF2 /* TileCache.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = TileCache.hpp; path = ../../../../wsd/TileCache.hpp; sourceTree = ""; };
+ BE4C625D2D5A3DDA00047DF2 /* Kit.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Kit.cpp; path = ../../../../kit/Kit.cpp; sourceTree = ""; };
+ BE4C62602D5B736200047DF2 /* coolwsd.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = coolwsd.xml; path = ../../../coolwsd.xml; sourceTree = ""; };
+ BE5A7A282D2BE28200FE7D60 /* FileUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = FileUtil.hpp; path = ../../../../common/FileUtil.hpp; sourceTree = ""; };
+ BE5FDE352DAD3DCF00C48DB2 /* LogUI.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = LogUI.hpp; path = ../../../../kit/LogUI.hpp; sourceTree = ""; };
+ BE5FDE362DAD3DCF00C48DB2 /* LogUI.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LogUI.cpp; path = ../../../../kit/LogUI.cpp; sourceTree = ""; };
+ BE9115AD2CECBD3100C597B2 /* FileUtil-unix.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "FileUtil-unix.cpp"; path = "../../../../common/FileUtil-unix.cpp"; sourceTree = ""; };
+ BE9115AF2CECBECF00C597B2 /* Util-unix.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Util-unix.cpp"; path = "../../../../common/Util-unix.cpp"; sourceTree = ""; };
+ BEA263522CBE38E20007435A /* Authorization.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Authorization.hpp; path = ../../../../common/Authorization.hpp; sourceTree = ""; };
+ BEA263532CBE38E20007435A /* Authorization.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Authorization.cpp; path = ../../../../common/Authorization.cpp; sourceTree = ""; };
+ BEA263542CBE38E20007435A /* ConfigUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ConfigUtil.hpp; path = ../../../../common/ConfigUtil.hpp; sourceTree = ""; };
+ BEA263552CBE38E20007435A /* ConfigUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ConfigUtil.cpp; path = ../../../../common/ConfigUtil.cpp; sourceTree = ""; };
+ BEA263562CBE38E20007435A /* FileUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FileUtil.cpp; path = ../../../../common/FileUtil.cpp; sourceTree = ""; };
+ BEA263572CBE38E20007435A /* Log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Log.hpp; path = ../../../../common/Log.hpp; sourceTree = ""; };
+ BEA263582CBE38E20007435A /* Log.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Log.cpp; path = ../../../../common/Log.cpp; sourceTree = ""; };
+ BEA263592CBE38E20007435A /* Png.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Png.hpp; path = ../../../../common/Png.hpp; sourceTree = ""; };
+ BEA2635A2CBE38E20007435A /* Protocol.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Protocol.hpp; path = ../../../../common/Protocol.hpp; sourceTree = ""; };
+ BEA2635B2CBE38E20007435A /* Protocol.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Protocol.cpp; path = ../../../../common/Protocol.cpp; sourceTree = ""; };
+ BEA2635C2CBE38E20007435A /* Session.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Session.hpp; path = ../../../../common/Session.hpp; sourceTree = ""; };
+ BEA2635D2CBE38E20007435A /* Session.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Session.cpp; path = ../../../../common/Session.cpp; sourceTree = ""; };
+ BEA2635E2CBE38E20007435A /* SigUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = SigUtil.hpp; path = ../../../../common/SigUtil.hpp; sourceTree = ""; };
+ BEA263602CBE38E20007435A /* Simd.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Simd.hpp; path = ../../../../common/Simd.hpp; sourceTree = ""; };
+ BEA263612CBE38E20007435A /* Simd.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Simd.cpp; path = ../../../../common/Simd.cpp; sourceTree = ""; };
+ BEA263622CBE38E20007435A /* SpookyV2.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SpookyV2.cpp; path = ../../../../common/SpookyV2.cpp; sourceTree = ""; };
+ BEA263632CBE38E20007435A /* StringVector.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = StringVector.hpp; path = ../../../../common/StringVector.hpp; sourceTree = ""; };
+ BEA263642CBE38E20007435A /* StringVector.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = StringVector.cpp; path = ../../../../common/StringVector.cpp; sourceTree = ""; };
+ BEA263652CBE38E20007435A /* TraceEvent.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = TraceEvent.hpp; path = ../../../../common/TraceEvent.hpp; sourceTree = ""; };
+ BEA263662CBE38E20007435A /* TraceEvent.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = TraceEvent.cpp; path = ../../../../common/TraceEvent.cpp; sourceTree = ""; };
+ BEA263672CBE38E20007435A /* Unit.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Unit.hpp; path = ../../../../common/Unit.hpp; sourceTree = ""; };
+ BEA263682CBE38E20007435A /* Unit.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Unit.cpp; path = ../../../../common/Unit.cpp; sourceTree = ""; };
+ BEA263692CBE38E20007435A /* Uri.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Uri.hpp; path = ../../../../common/Uri.hpp; sourceTree = ""; };
+ BEA2636A2CBE38E20007435A /* Uri.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Uri.cpp; path = ../../../../common/Uri.cpp; sourceTree = ""; };
+ BEA2636B2CBE38E20007435A /* Util.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Util.hpp; path = ../../../../common/Util.hpp; sourceTree = "