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 = ""; }; + BEA2636C2CBE38E20007435A /* Util.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Util.cpp; path = ../../../../common/Util.cpp; sourceTree = ""; }; + BEA2637B2CBE38F50007435A /* coda-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "coda-Bridging-Header.h"; sourceTree = ""; }; + BEA264152CBE7A770007435A /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; + BEA2648B2CBE986C0007435A /* DeltaSimd.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DeltaSimd.h; path = ../../../../kit/DeltaSimd.h; sourceTree = ""; }; + BEA2648C2CBE986C0007435A /* DeltaSimd.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = DeltaSimd.c; path = ../../../../kit/DeltaSimd.c; sourceTree = ""; }; + BEA2648D2CBE986C0007435A /* ChildSession.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ChildSession.cpp; path = ../../../../kit/ChildSession.cpp; sourceTree = ""; }; + BEA2648F2CBE986C0007435A /* KitQueue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = KitQueue.hpp; path = ../../../../kit/KitQueue.hpp; sourceTree = ""; }; + BEA264902CBE986C0007435A /* KitQueue.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = KitQueue.cpp; path = ../../../../kit/KitQueue.cpp; sourceTree = ""; }; + BEA264912CBE986C0007435A /* KitWebSocket.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = KitWebSocket.cpp; path = ../../../../kit/KitWebSocket.cpp; sourceTree = ""; }; + BEA264972CBE98CD0007435A /* AsyncDNS.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = AsyncDNS.hpp; path = ../../../../net/AsyncDNS.hpp; sourceTree = ""; }; + BEA264982CBE98CD0007435A /* FakeSocket.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FakeSocket.cpp; path = ../../../../net/FakeSocket.cpp; sourceTree = ""; }; + BEA2649D2CBE98CD0007435A /* Socket.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Socket.hpp; path = ../../../../net/Socket.hpp; sourceTree = ""; }; + BEA2649E2CBE98CD0007435A /* Socket.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Socket.cpp; path = ../../../../net/Socket.cpp; sourceTree = ""; }; + BEA264A52CBE99340007435A /* ClientSession.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ClientSession.cpp; path = ../../../../wsd/ClientSession.cpp; sourceTree = ""; }; + BEA264A62CBE99340007435A /* COOLWSD.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = COOLWSD.cpp; path = ../../../../wsd/COOLWSD.cpp; sourceTree = ""; }; + BEA264A72CBE99340007435A /* DocumentBroker.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DocumentBroker.cpp; path = ../../../../wsd/DocumentBroker.cpp; sourceTree = ""; }; + BEA264A82CBE99340007435A /* RequestDetails.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = RequestDetails.hpp; path = ../../../../wsd/RequestDetails.hpp; sourceTree = ""; }; + BEA264A92CBE99340007435A /* RequestDetails.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = RequestDetails.cpp; path = ../../../../wsd/RequestDetails.cpp; sourceTree = ""; }; + BEA264AB2CBE99340007435A /* Storage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Storage.cpp; path = ../../../../wsd/Storage.cpp; sourceTree = ""; }; + BEA264AC2CBE99340007435A /* TileCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = TileCache.cpp; path = ../../../../wsd/TileCache.cpp; sourceTree = ""; }; + BEA264B52CBE9BEF0007435A /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Storage.hpp; path = ../../../../wsd/Storage.hpp; sourceTree = ""; }; + BECC4BAB2CBD3FA300A120B3 /* coda.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = coda.app; sourceTree = BUILT_PRODUCTS_DIR; }; + BECC4BBC2CBD3FA400A120B3 /* codaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = codaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BECC4BC62CBD3FA400A120B3 /* codaUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = codaUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BEEF3F822CE20BDD00ABE785 /* SetupKitEnvironment.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = SetupKitEnvironment.hpp; path = ../../../../kit/SetupKitEnvironment.hpp; sourceTree = ""; }; + BEEF3F852CE2139E00ABE785 /* coolkitconfig.xcu */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = coolkitconfig.xcu; path = ../../../coolkitconfig.xcu; sourceTree = ""; }; + BEF5CE8E2D633CE60006FD77 /* discovery.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = discovery.xml; path = ../../../discovery.xml; sourceTree = ""; }; + BEF6F02B2DD1EC0200EA06BF /* RegexUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = RegexUtil.hpp; path = ../../../../common/RegexUtil.hpp; sourceTree = ""; }; + BEF6F02C2DD1EC0200EA06BF /* RegexUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = RegexUtil.cpp; path = ../../../../common/RegexUtil.cpp; sourceTree = ""; }; + BEFC03182D64986E009FF7B8 /* Kit.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Kit.hpp; path = ../../../../kit/Kit.hpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + BECC4BAD2CBD3FA300A120B3 /* coda */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = coda; + sourceTree = ""; + }; + BECC4BBF2CBD3FA400A120B3 /* codaTests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = codaTests; + sourceTree = ""; + }; + BECC4BC92CBD3FA400A120B3 /* codaUITests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = codaUITests; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + BECC4BA82CBD3FA300A120B3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BECC4BB92CBD3FA400A120B3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BECC4BC32CBD3FA400A120B3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + BE22492A2CC79AAC00385A9C /* Resources */ = { + isa = PBXGroup; + children = ( + BE22AA9B2CC7AE7400385A9C /* images */, + BE44ACF52D7F1F6B00BFA3B2 /* branding.css */, + BE44ACF62D7F1F6B00BFA3B2 /* branding.js */, + BE44ACF72D7F1F6B00BFA3B2 /* branding-desktop.css */, + BE44ACF82D7F1F6B00BFA3B2 /* branding-mobile.css */, + BE44ACF92D7F1F6B00BFA3B2 /* branding-tablet.css */, + BE44ACFA2D7F1F6B00BFA3B2 /* branding-unsupported.css */, + BE44ACFB2D7F1F6B00BFA3B2 /* branding-unsupported.js */, + BE22492D2CC79BAE00385A9C /* bundle.css */, + BE22492E2CC79BAE00385A9C /* bundle.js */, + BE22492B2CC79AD700385A9C /* cool.html */, + BE22492F2CC79BAE00385A9C /* cool-help.html */, + BEEF3F852CE2139E00ABE785 /* coolkitconfig.xcu */, + BE4C62602D5B736200047DF2 /* coolwsd.xml */, + BE2249302CC79BAE00385A9C /* device-desktop.css */, + BEF5CE8E2D633CE60006FD77 /* discovery.xml */, + BE2249312CC79BAE00385A9C /* global.js */, + BE16C9EF2CD51F58005E5960 /* hello.odt */, + ); + path = Resources; + sourceTree = ""; + }; + BEA2637C2CBE38FE0007435A /* common */ = { + isa = PBXGroup; + children = ( + BEA263532CBE38E20007435A /* Authorization.cpp */, + BEA263522CBE38E20007435A /* Authorization.hpp */, + BE4C61FF2D59E85500047DF2 /* Common.hpp */, + BEA263542CBE38E20007435A /* ConfigUtil.hpp */, + BEA263552CBE38E20007435A /* ConfigUtil.cpp */, + BE4C62012D59E85500047DF2 /* Crypto.hpp */, + BE44ACCE2D7F067500BFA3B2 /* Crypto-stub.cpp */, + BE44ACBF2D7EFDE900BFA3B2 /* DummyTraceEventEmitter.cpp */, + BE5A7A282D2BE28200FE7D60 /* FileUtil.hpp */, + BEA263562CBE38E20007435A /* FileUtil.cpp */, + BE44ACC62D7F011F00BFA3B2 /* FileUtil-apple.mm */, + BE9115AD2CECBD3100C597B2 /* FileUtil-unix.cpp */, + BEA263572CBE38E20007435A /* Log.hpp */, + BEA263582CBE38E20007435A /* Log.cpp */, + BE4C62052D59E85500047DF2 /* MobileApp.hpp */, + BE4C62062D59E85500047DF2 /* MobileApp.cpp */, + BEA263592CBE38E20007435A /* Png.hpp */, + BEA2635A2CBE38E20007435A /* Protocol.hpp */, + BEA2635B2CBE38E20007435A /* Protocol.cpp */, + BEF6F02B2DD1EC0200EA06BF /* RegexUtil.hpp */, + BEF6F02C2DD1EC0200EA06BF /* RegexUtil.cpp */, + BEA2635C2CBE38E20007435A /* Session.hpp */, + BEA2635D2CBE38E20007435A /* Session.cpp */, + BE44ACC82D7F018700BFA3B2 /* SigHandlerTrap.hpp */, + BEA2635E2CBE38E20007435A /* SigUtil.hpp */, + BE44ACC92D7F018700BFA3B2 /* SigUtil-mobile.cpp */, + BEA263602CBE38E20007435A /* Simd.hpp */, + BEA263612CBE38E20007435A /* Simd.cpp */, + BE4C620A2D59E85500047DF2 /* SpookyV2.h */, + BEA263622CBE38E20007435A /* SpookyV2.cpp */, + BEA263632CBE38E20007435A /* StringVector.hpp */, + BEA263642CBE38E20007435A /* StringVector.cpp */, + BEA263652CBE38E20007435A /* TraceEvent.hpp */, + BEA263662CBE38E20007435A /* TraceEvent.cpp */, + BEA263672CBE38E20007435A /* Unit.hpp */, + BEA263682CBE38E20007435A /* Unit.cpp */, + BE44ACD02D7F073000BFA3B2 /* Unit-mobile.cpp */, + BEA263692CBE38E20007435A /* Uri.hpp */, + BEA2636A2CBE38E20007435A /* Uri.cpp */, + BEA2636B2CBE38E20007435A /* Util.hpp */, + BEA2636C2CBE38E20007435A /* Util.cpp */, + BE4C620C2D59E85500047DF2 /* Util-macos.cpp */, + BE44ACC12D7EFF1500BFA3B2 /* Util-mobile.cpp */, + BE9115AF2CECBECF00C597B2 /* Util-unix.cpp */, + ); + path = common; + sourceTree = ""; + }; + BEA2637D2CBE39C30007435A /* Online */ = { + isa = PBXGroup; + children = ( + BEA2637C2CBE38FE0007435A /* common */, + BEA264882CBE97C30007435A /* kit */, + BEA264892CBE97D40007435A /* net */, + BEA2648A2CBE98010007435A /* wsd */, + ); + path = Online; + sourceTree = ""; + }; + BEA264882CBE97C30007435A /* kit */ = { + isa = PBXGroup; + children = ( + BEA2648B2CBE986C0007435A /* DeltaSimd.h */, + BEA2648C2CBE986C0007435A /* DeltaSimd.c */, + BEA2648D2CBE986C0007435A /* ChildSession.cpp */, + BEFC03182D64986E009FF7B8 /* Kit.hpp */, + BE4C625D2D5A3DDA00047DF2 /* Kit.cpp */, + BEA2648F2CBE986C0007435A /* KitQueue.hpp */, + BEA264902CBE986C0007435A /* KitQueue.cpp */, + BEA264912CBE986C0007435A /* KitWebSocket.cpp */, + BE5FDE352DAD3DCF00C48DB2 /* LogUI.hpp */, + BE5FDE362DAD3DCF00C48DB2 /* LogUI.cpp */, + BEEF3F822CE20BDD00ABE785 /* SetupKitEnvironment.hpp */, + ); + path = kit; + sourceTree = ""; + }; + BEA264892CBE97D40007435A /* net */ = { + isa = PBXGroup; + children = ( + BEA264972CBE98CD0007435A /* AsyncDNS.hpp */, + BEA264982CBE98CD0007435A /* FakeSocket.cpp */, + BEA2649D2CBE98CD0007435A /* Socket.hpp */, + BEA2649E2CBE98CD0007435A /* Socket.cpp */, + ); + path = net; + sourceTree = ""; + }; + BEA2648A2CBE98010007435A /* wsd */ = { + isa = PBXGroup; + children = ( + BE44ACC32D7F00B000BFA3B2 /* ClientRequestDispatcher.hpp */, + BE44ACC42D7F00B000BFA3B2 /* ClientRequestDispatcher.cpp */, + BE4C62252D59EBAC00047DF2 /* ClientSession.hpp */, + BEA264A52CBE99340007435A /* ClientSession.cpp */, + BE4C62262D59EBAC00047DF2 /* COOLWSD.hpp */, + BEA264A62CBE99340007435A /* COOLWSD.cpp */, + BE4C62272D59EBAC00047DF2 /* DocumentBroker.hpp */, + BEA264A72CBE99340007435A /* DocumentBroker.cpp */, + BEA264A82CBE99340007435A /* RequestDetails.hpp */, + BEA264A92CBE99340007435A /* RequestDetails.cpp */, + BE44ACD82D7F089200BFA3B2 /* RequestVettingStation.hpp */, + BE44ACD92D7F089200BFA3B2 /* RequestVettingStation.cpp */, + BEA264B52CBE9BEF0007435A /* Storage.hpp */, + BEA264AB2CBE99340007435A /* Storage.cpp */, + BE4C623E2D59EBAC00047DF2 /* TileCache.hpp */, + BEA264AC2CBE99340007435A /* TileCache.cpp */, + ); + path = wsd; + sourceTree = ""; + }; + BECC4BA22CBD3FA300A120B3 = { + isa = PBXGroup; + children = ( + BE22492A2CC79AAC00385A9C /* Resources */, + BEA2637D2CBE39C30007435A /* Online */, + BECC4BAD2CBD3FA300A120B3 /* coda */, + BECC4BBF2CBD3FA400A120B3 /* codaTests */, + BECC4BC92CBD3FA400A120B3 /* codaUITests */, + BECC4BAC2CBD3FA300A120B3 /* Products */, + BEA2637B2CBE38F50007435A /* coda-Bridging-Header.h */, + BEA264152CBE7A770007435A /* Config.xcconfig */, + ); + sourceTree = ""; + }; + BECC4BAC2CBD3FA300A120B3 /* Products */ = { + isa = PBXGroup; + children = ( + BECC4BAB2CBD3FA300A120B3 /* coda.app */, + BECC4BBC2CBD3FA400A120B3 /* codaTests.xctest */, + BECC4BC62CBD3FA400A120B3 /* codaUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + BECC4BAA2CBD3FA300A120B3 /* coda */ = { + isa = PBXNativeTarget; + buildConfigurationList = BECC4BD02CBD3FA400A120B3 /* Build configuration list for PBXNativeTarget "coda" */; + buildPhases = ( + BECC4BA72CBD3FA300A120B3 /* Sources */, + BECC4BA82CBD3FA300A120B3 /* Frameworks */, + BECC4BA92CBD3FA300A120B3 /* Resources */, + BEE076602CDA3024004111EF /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + BECC4BAD2CBD3FA300A120B3 /* coda */, + ); + name = coda; + packageProductDependencies = ( + ); + productName = coda; + productReference = BECC4BAB2CBD3FA300A120B3 /* coda.app */; + productType = "com.apple.product-type.application"; + }; + BECC4BBB2CBD3FA400A120B3 /* codaTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = BECC4BD32CBD3FA400A120B3 /* Build configuration list for PBXNativeTarget "codaTests" */; + buildPhases = ( + BECC4BB82CBD3FA400A120B3 /* Sources */, + BECC4BB92CBD3FA400A120B3 /* Frameworks */, + BECC4BBA2CBD3FA400A120B3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BECC4BBE2CBD3FA400A120B3 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + BECC4BBF2CBD3FA400A120B3 /* codaTests */, + ); + name = codaTests; + packageProductDependencies = ( + ); + productName = codaTests; + productReference = BECC4BBC2CBD3FA400A120B3 /* codaTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + BECC4BC52CBD3FA400A120B3 /* codaUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = BECC4BD62CBD3FA400A120B3 /* Build configuration list for PBXNativeTarget "codaUITests" */; + buildPhases = ( + BECC4BC22CBD3FA400A120B3 /* Sources */, + BECC4BC32CBD3FA400A120B3 /* Frameworks */, + BECC4BC42CBD3FA400A120B3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BECC4BC82CBD3FA400A120B3 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + BECC4BC92CBD3FA400A120B3 /* codaUITests */, + ); + name = codaUITests; + packageProductDependencies = ( + ); + productName = codaUITests; + productReference = BECC4BC62CBD3FA400A120B3 /* codaUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BECC4BA32CBD3FA300A120B3 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1600; + LastUpgradeCheck = 1600; + TargetAttributes = { + BECC4BAA2CBD3FA300A120B3 = { + CreatedOnToolsVersion = 16.0; + LastSwiftMigration = 1600; + }; + BECC4BBB2CBD3FA400A120B3 = { + CreatedOnToolsVersion = 16.0; + TestTargetID = BECC4BAA2CBD3FA300A120B3; + }; + BECC4BC52CBD3FA400A120B3 = { + CreatedOnToolsVersion = 16.0; + TestTargetID = BECC4BAA2CBD3FA300A120B3; + }; + }; + }; + buildConfigurationList = BECC4BA62CBD3FA300A120B3 /* Build configuration list for PBXProject "coda" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = BECC4BA22CBD3FA300A120B3; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = BECC4BAC2CBD3FA300A120B3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BECC4BAA2CBD3FA300A120B3 /* coda */, + BECC4BBB2CBD3FA400A120B3 /* codaTests */, + BECC4BC52CBD3FA400A120B3 /* codaUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + BECC4BA92CBD3FA300A120B3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BE22AA9C2CC7AE7400385A9C /* images in Resources */, + BEF5CE8F2D633CE60006FD77 /* discovery.xml in Resources */, + BE4C62612D5B736200047DF2 /* coolwsd.xml in Resources */, + BE2252292CC79BAE00385A9C /* bundle.js in Resources */, + BE22529E2CC79BAE00385A9C /* cool-help.html in Resources */, + BE2253F32CC79BAE00385A9C /* device-desktop.css in Resources */, + BE22557E2CC79BAE00385A9C /* global.js in Resources */, + BE16C9F02CD51F58005E5960 /* hello.odt in Resources */, + BE2257AD2CC79BAE00385A9C /* bundle.css in Resources */, + BE22492C2CC79AD700385A9C /* cool.html in Resources */, + BE44ACFC2D7F1F6B00BFA3B2 /* branding-mobile.css in Resources */, + BE44ACFD2D7F1F6B00BFA3B2 /* branding-unsupported.js in Resources */, + BE44ACFE2D7F1F6B00BFA3B2 /* branding.js in Resources */, + BE44ACFF2D7F1F6B00BFA3B2 /* branding-desktop.css in Resources */, + BE44AD002D7F1F6B00BFA3B2 /* branding-tablet.css in Resources */, + BE44AD012D7F1F6B00BFA3B2 /* branding.css in Resources */, + BE44AD022D7F1F6B00BFA3B2 /* branding-unsupported.css in Resources */, + BEEF3F862CE2139E00ABE785 /* coolkitconfig.xcu in Resources */, + BEA264162CBE7A770007435A /* Config.xcconfig in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BECC4BBA2CBD3FA400A120B3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BECC4BC42CBD3FA400A120B3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + BEE076602CDA3024004111EF /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Copy lokit to the bundle\nif [ -z \"$LO_PATH\" ]; then\n echo \"Error: LO_PATH is not set. Please configure it in Config.xcconfig.\"\n exit 1\nfi\n\necho \"Copying $LO_PATH/Contents/ to ${CONFIGURATION_BUILD_DIR}/${TARGET_NAME}.app/Contents/lokit/\"\nrsync -avz --exclude=\"**/gengal\" --exclude=\"**/opencltest\" --exclude=\"**/soffice\" --exclude=\"**/uno\" --exclude=\"**/unopkg\" --exclude=\"**/uri-encode\" --exclude=\"**/QuickLookPreview.appex\" --exclude=\"**/QuickLookThumbnail.appex\" $LO_PATH/Contents/ ${CONFIGURATION_BUILD_DIR}/${TARGET_NAME}.app/Contents/lokit/\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BECC4BA72CBD3FA300A120B3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BE4C625E2D5A3DDA00047DF2 /* Kit.cpp in Sources */, + BEA2636D2CBE38E20007435A /* Uri.cpp in Sources */, + BEA264922CBE986C0007435A /* KitQueue.cpp in Sources */, + BE9115AE2CECBD3100C597B2 /* FileUtil-unix.cpp in Sources */, + BEA264932CBE986C0007435A /* KitWebSocket.cpp in Sources */, + BEA264942CBE986C0007435A /* ChildSession.cpp in Sources */, + BEA264962CBE986C0007435A /* DeltaSimd.c in Sources */, + BEF6F02D2DD1EC0200EA06BF /* RegexUtil.cpp in Sources */, + BEA2636E2CBE38E20007435A /* Util.cpp in Sources */, + BE44ACC22D7EFF1500BFA3B2 /* Util-mobile.cpp in Sources */, + BEA2636F2CBE38E20007435A /* Unit.cpp in Sources */, + BEA263702CBE38E20007435A /* Log.cpp in Sources */, + BE44ACDA2D7F089200BFA3B2 /* RequestVettingStation.cpp in Sources */, + BEA263712CBE38E20007435A /* Simd.cpp in Sources */, + BEA263722CBE38E20007435A /* FileUtil.cpp in Sources */, + BEA263732CBE38E20007435A /* Session.cpp in Sources */, + BE44ACC02D7EFDE900BFA3B2 /* DummyTraceEventEmitter.cpp in Sources */, + BE5FDE372DAD3DCF00C48DB2 /* LogUI.cpp in Sources */, + BEA263742CBE38E20007435A /* TraceEvent.cpp in Sources */, + BE44ACD12D7F073000BFA3B2 /* Unit-mobile.cpp in Sources */, + BEA264A12CBE98CD0007435A /* Socket.cpp in Sources */, + BE9115B02CECBECF00C597B2 /* Util-unix.cpp in Sources */, + BE4C620E2D59E85500047DF2 /* MobileApp.cpp in Sources */, + BE4C620F2D59E85500047DF2 /* Util-macos.cpp in Sources */, + BE44ACCF2D7F067500BFA3B2 /* Crypto-stub.cpp in Sources */, + BE44ACC72D7F011F00BFA3B2 /* FileUtil-apple.mm in Sources */, + BEA264A32CBE98CD0007435A /* FakeSocket.cpp in Sources */, + BEA263762CBE38E20007435A /* SpookyV2.cpp in Sources */, + BE44ACCA2D7F018700BFA3B2 /* SigUtil-mobile.cpp in Sources */, + BEA264AD2CBE99340007435A /* Storage.cpp in Sources */, + BEA264AE2CBE99340007435A /* TileCache.cpp in Sources */, + BEA264AF2CBE99340007435A /* COOLWSD.cpp in Sources */, + BEA264B02CBE99340007435A /* ClientSession.cpp in Sources */, + BEA264B12CBE99340007435A /* RequestDetails.cpp in Sources */, + BEA264B32CBE99340007435A /* DocumentBroker.cpp in Sources */, + BEA263772CBE38E20007435A /* ConfigUtil.cpp in Sources */, + BEA263782CBE38E20007435A /* StringVector.cpp in Sources */, + BEA263792CBE38E20007435A /* Protocol.cpp in Sources */, + BEA2637A2CBE38E20007435A /* Authorization.cpp in Sources */, + BE44ACC52D7F00B000BFA3B2 /* ClientRequestDispatcher.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BECC4BB82CBD3FA400A120B3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BECC4BC22CBD3FA400A120B3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + BECC4BBE2CBD3FA400A120B3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BECC4BAA2CBD3FA300A120B3 /* coda */; + targetProxy = BECC4BBD2CBD3FA400A120B3 /* PBXContainerItemProxy */; + }; + BECC4BC82CBD3FA400A120B3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BECC4BAA2CBD3FA300A120B3 /* coda */; + targetProxy = BECC4BC72CBD3FA400A120B3 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + BECC4BCE2CBD3FA400A120B3 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BEA264152CBE7A770007435A /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)", + "$(SRCROOT)/coda", + "$(SRCROOT)/../..", + "$(SRCROOT)/../../common", + "$(SRCROOT)/../../kit", + "$(SRCROOT)/../../net", + "$(SRCROOT)/../../wsd", + "$(HOMEBREW_PATH)/include", + ); + LIBRARY_SEARCH_PATHS = "$(HOMEBREW_PATH)/lib"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ( + "-lPocoFoundation", + "-lPocoUtil", + "-lPocoXML", + "-lPocoJSON", + "-lPocoNet", + "-lzstd", + "-lpng", + ); + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + BECC4BCF2CBD3FA400A120B3 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BEA264152CBE7A770007435A /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)", + "$(SRCROOT)/coda", + "$(SRCROOT)/../..", + "$(SRCROOT)/../../common", + "$(SRCROOT)/../../kit", + "$(SRCROOT)/../../net", + "$(SRCROOT)/../../wsd", + "$(HOMEBREW_PATH)/include", + ); + LIBRARY_SEARCH_PATHS = "$(HOMEBREW_PATH)/lib"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = ( + "-lPocoFoundation", + "-lPocoUtil", + "-lPocoXML", + "-lPocoJSON", + "-lPocoNet", + "-lzstd", + "-lpng", + ); + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + BECC4BD12CBD3FA400A120B3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = coda/coda.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = J4FQ687VJK; + ENABLE_APP_SANDBOX = NO; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = coda/Info.plist; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSMainStoryboardFile = Main; + INFOPLIST_KEY_NSPrincipalClass = NSApplication; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "-liconv", + "-lpam", + "-lPocoFoundation", + "-lPocoUtil", + "-lPocoXML", + "-lPocoJSON", + "-lPocoNet", + "-lPocoNetSSL", + "-lPocoCrypto", + "-lzstd", + "-lpng", + "-lz", + "-lssl", + "-lcrypto", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.collabora.coda; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "coda-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + BECC4BD22CBD3FA400A120B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = coda/coda.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = J4FQ687VJK; + ENABLE_APP_SANDBOX = NO; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + EXCLUDED_ARCHS = x86_64; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = coda/Info.plist; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSMainStoryboardFile = Main; + INFOPLIST_KEY_NSPrincipalClass = NSApplication; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "-liconv", + "-lpam", + "-lPocoFoundation", + "-lPocoUtil", + "-lPocoXML", + "-lPocoJSON", + "-lPocoNet", + "-lPocoNetSSL", + "-lPocoCrypto", + "-lzstd", + "-lpng", + "-lz", + "-lssl", + "-lcrypto", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.collabora.coda; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "coda-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + BECC4BD42CBD3FA400A120B3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = J4FQ687VJK; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.collabora.codaTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/coda.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/coda"; + }; + name = Debug; + }; + BECC4BD52CBD3FA400A120B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = J4FQ687VJK; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.collabora.codaTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/coda.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/coda"; + }; + name = Release; + }; + BECC4BD72CBD3FA400A120B3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = J4FQ687VJK; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.collabora.codaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = coda; + }; + name = Debug; + }; + BECC4BD82CBD3FA400A120B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = J4FQ687VJK; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.collabora.codaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = coda; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BECC4BA62CBD3FA300A120B3 /* Build configuration list for PBXProject "coda" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BECC4BCE2CBD3FA400A120B3 /* Debug */, + BECC4BCF2CBD3FA400A120B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BECC4BD02CBD3FA400A120B3 /* Build configuration list for PBXNativeTarget "coda" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BECC4BD12CBD3FA400A120B3 /* Debug */, + BECC4BD22CBD3FA400A120B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BECC4BD32CBD3FA400A120B3 /* Build configuration list for PBXNativeTarget "codaTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BECC4BD42CBD3FA400A120B3 /* Debug */, + BECC4BD52CBD3FA400A120B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BECC4BD62CBD3FA400A120B3 /* Build configuration list for PBXNativeTarget "codaUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BECC4BD72CBD3FA400A120B3 /* Debug */, + BECC4BD82CBD3FA400A120B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BECC4BA32CBD3FA300A120B3 /* Project object */; +} diff --git a/macos/coda/coda.xcodeproj/xcshareddata/xcschemes/coda.xcscheme b/macos/coda/coda.xcodeproj/xcshareddata/xcschemes/coda.xcscheme new file mode 100644 index 0000000000000..66cb7c4100a21 --- /dev/null +++ b/macos/coda/coda.xcodeproj/xcshareddata/xcschemes/coda.xcscheme @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/coda/coda/AppDelegate.swift b/macos/coda/coda/AppDelegate.swift new file mode 100644 index 0000000000000..54950d625a1b5 --- /dev/null +++ b/macos/coda/coda/AppDelegate.swift @@ -0,0 +1,30 @@ +/* + * 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/. + */ + +import Cocoa + +@main +class AppDelegate: NSObject, NSApplicationDelegate { + + func applicationDidFinishLaunching(_ aNotification: Notification) { + // Initialize the COOLWSD + COWrapper.startServer() + } + + func applicationWillTerminate(_ aNotification: Notification) { + // Insert code here to tear down your application + COWrapper.stopServer() + } + + func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } + +} diff --git a/macos/coda/coda/Assets.xcassets/AccentColor.colorset/Contents.json b/macos/coda/coda/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000000..eb87897008164 --- /dev/null +++ b/macos/coda/coda/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000000..64dc11ee7438f --- /dev/null +++ b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "filename" : "icon_16x16.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "filename" : "icon_16x16@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "filename" : "icon_32x32.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "filename" : "icon_32x32@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "filename" : "icon_128x128.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "filename" : "icon_128x128@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "filename" : "icon_256x256.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "filename" : "icon_256x256@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "filename" : "icon_512x512.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "filename" : "icon_512x512@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_128x128.png b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_128x128.png new file mode 100644 index 0000000000000..624cdab34e5f8 Binary files /dev/null and b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_128x128.png differ diff --git a/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png new file mode 100644 index 0000000000000..42f62f9b6548c Binary files /dev/null and b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png differ diff --git a/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_16x16.png b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_16x16.png new file mode 100644 index 0000000000000..4b9e9f769142a Binary files /dev/null and b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_16x16.png differ diff --git a/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png new file mode 100644 index 0000000000000..ae2e0b42ee76d Binary files /dev/null and b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png differ diff --git a/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_256x256.png b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_256x256.png new file mode 100644 index 0000000000000..42f62f9b6548c Binary files /dev/null and b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_256x256.png differ diff --git a/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png new file mode 100644 index 0000000000000..c2f4f21bc2e59 Binary files /dev/null and b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png differ diff --git a/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_32x32.png b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_32x32.png new file mode 100644 index 0000000000000..ae2e0b42ee76d Binary files /dev/null and b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_32x32.png differ diff --git a/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png new file mode 100644 index 0000000000000..8d2ca82d16c9d Binary files /dev/null and b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png differ diff --git a/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_512x512.png b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_512x512.png new file mode 100644 index 0000000000000..c2f4f21bc2e59 Binary files /dev/null and b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_512x512.png differ diff --git a/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png new file mode 100644 index 0000000000000..e836638371997 Binary files /dev/null and b/macos/coda/coda/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png differ diff --git a/macos/coda/coda/Assets.xcassets/Contents.json b/macos/coda/coda/Assets.xcassets/Contents.json new file mode 100644 index 0000000000000..73c00596a7fca --- /dev/null +++ b/macos/coda/coda/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/coda/coda/Base.lproj/Main.storyboard b/macos/coda/coda/Base.lproj/Main.storyboard new file mode 100644 index 0000000000000..89b2e59cf49c7 --- /dev/null +++ b/macos/coda/coda/Base.lproj/Main.storyboard @@ -0,0 +1,767 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/coda/coda/COWrapper.h b/macos/coda/coda/COWrapper.h new file mode 100644 index 0000000000000..52237cf5e55b5 --- /dev/null +++ b/macos/coda/coda/COWrapper.h @@ -0,0 +1,36 @@ +/* + * 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/. + */ + +#import +#import + +@class Document; + +@interface COWrapper : NSObject { +} + ++ (void)startServer; ++ (void)stopServer; + ++ (void)handleHULLOWithDocument:(Document *_Nonnull)document; ++ (void)handleMessageWith:(Document *_Nonnull)document message:(NSString *_Nonnull)message; ++ (void)saveAsWith:(Document *_Nonnull)document url:(NSString *_Nonnull)url format:(NSString *_Nonnull)format filterOptions:(NSString *_Nullable)filterOptions; ++ (NSArray> * _Nullable) getClipboardWith:(Document *_Nonnull)document NS_SWIFT_NAME(getClipboard(_:)); ++ (void)setClipboardWith:(Document *_Nonnull)document from:(NSPasteboard *_Nonnull)pasteboard NS_SWIFT_NAME(setClipboard(_:from:)); ++ (bool)sendToInternalWith:(Document *_Nonnull)document content:(NSString *_Nonnull)content NS_SWIFT_NAME(sendToInternalClipboard(_:content:)); + ++ (int)generateNewAppDocId; ++ (int)fakeSocketSocket; + ++ (void)LOG_DBG:(NSString *_Nonnull)message NS_SWIFT_NAME(LOG_DBG(_:)); ++ (void)LOG_ERR:(NSString *_Nonnull)message NS_SWIFT_NAME(LOG_ERR(_:)); ++ (void)LOG_TRC:(NSString *_Nonnull)message NS_SWIFT_NAME(LOG_TRC(_:)); + +@end diff --git a/macos/coda/coda/COWrapper.mm b/macos/coda/coda/COWrapper.mm new file mode 100644 index 0000000000000..02c46383f01cc --- /dev/null +++ b/macos/coda/coda/COWrapper.mm @@ -0,0 +1,365 @@ +/* + * 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 + +#define LIBO_INTERNAL_ONLY +#include + +#import +#import + +#import "coda-Swift.h" +#import "COWrapper.h" +#import "macos.h" + +// Include necessary C++ headers +#include +#include +#include +#include +#include +#include +#include +#include + +// Declare the coolwsd pointer at global scope +COOLWSD *coolwsd = nullptr; + +static int closeNotificationPipeForForwardingThread[2]; + +/** + * Wrapper to be able to call the C++ code from Swift. + * + * The main purpose is to initialize the COOLWSD and interact with it. + */ +@implementation COWrapper + ++ (void)startServer { + // Initialize logging + // Use "debug" or potentially even "trace" for debugging +#if DEBUG + Log::initialize("Mobile", "debug"); +#else + Log::initialize("Mobile", "information"); +#endif + Util::setThreadName("main"); + + // Set up the logging callback + fakeSocketSetLoggingCallback([](const std::string& line) { + LOG_TRC_NOFILE(line); + }); + + // Start the COOLWSD server in a detached thread + NSLog(@"CollaboraOffice: Starting the thread"); + std::thread([]{ + assert(coolwsd == nullptr); + + // Prepare arguments for COOLWSD + std::vector args = { + "coda" + }; + + Util::setThreadName("app"); + + coolwsd = new COOLWSD(); + coolwsd->run(args); + delete coolwsd; + coolwsd = nullptr; // Reset the pointer after deletion + NSLog(@"CollaboraOffice: The COOLWSD thread completed"); + }).detach(); +} + ++ (void)stopServer { + if (coolwsd) { + delete coolwsd; + coolwsd = nullptr; + } +} + ++ (void)handleHULLOWithDocument:(Document *)document { + // Contact the permanently (during app lifetime) listening COOLWSD server + // "public" socket + assert(coolwsd_server_socket_fd != -1); + int rc = fakeSocketConnect(document.fakeClientFd, coolwsd_server_socket_fd); + assert(rc != -1); + + // Create a socket pair to notify the below thread when the document has been closed + fakeSocketPipe2(closeNotificationPipeForForwardingThread); + + // Start another thread to read responses and forward them to the JavaScript + dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^{ + Util::setThreadName("app2js"); + while (true) { + struct pollfd p[2]; + p[0].fd = document.fakeClientFd; + p[0].events = POLLIN; + p[1].fd = closeNotificationPipeForForwardingThread[1]; + p[1].events = POLLIN; + if (fakeSocketPoll(p, 2, -1) > 0) { + if (p[1].revents == POLLIN) { + // The code below handling the "BYE" fake Websocket + // message has closed the other end of the + // closeNotificationPipeForForwardingThread. Let's close + // the other end too just for cleanliness, even if a + // FakeSocket as such is not a system resource so nothing + // is saved by closing it. + fakeSocketClose(closeNotificationPipeForForwardingThread[1]); + + // Close our end of the fake socket connection to the + // ClientSession thread, so that it terminates + fakeSocketClose(document.fakeClientFd); + + return; + } + if (p[0].revents == POLLIN) { + size_t n = fakeSocketAvailableDataLength(document.fakeClientFd); + // I don't want to check for n being -1 here, even if + // that will lead to a crash (std::length_error from the + // below std::vector constructor), as n being -1 is a + // sign of something being wrong elsewhere anyway, and I + // prefer to fix the root cause. Let's see how well this + // works out. See tdf#122543 for such a case. + if (n == 0) + return; + std::vector buf(n); + n = fakeSocketRead(document.fakeClientFd, buf.data(), n); + [document send2JS:buf.data() length:n]; + } + } + else + break; + } + assert(false); + }); + + // First we simply send the Online C++ parts the URL and the appDocId. This corresponds + // to the GET request with Upgrade to WebSocket. + std::string url([[document.tempFileURL absoluteString] UTF8String]); + struct pollfd p; + p.fd = document.fakeClientFd; + p.events = POLLOUT; + fakeSocketPoll(&p, 1, -1); + + // appDocId is read in ClientRequestDispatcher::handleIncomingMessage() in COOLWSD.cpp + std::string message(url + " " + std::to_string(document.appDocId)); + fakeSocketWrite(document.fakeClientFd, message.c_str(), message.size()); +} + ++ (void)handleMessageWith:(Document *)document message:(NSString *)message { + const char *buf = [message UTF8String]; + struct pollfd p; + p.fd = document.fakeClientFd; + p.events = POLLOUT; + fakeSocketPoll(&p, 1, -1); + fakeSocketWrite(document.fakeClientFd, buf, strlen(buf)); +} + ++ (void)saveAsWith:(Document *)document url:(NSString *)url format:(NSString *)format filterOptions:(NSString *)filterOptions { + DocumentData::get(document.appDocId).loKitDocument->saveAs([url UTF8String], [format UTF8String], [filterOptions UTF8String]); +} + +/** + * Call the LOKit getClipboard and return it so that it can be used in Swift. + */ ++ (NSArray> * _Nullable) getClipboardInternalWith:(Document *_Nonnull)document mimeTypes:(const char**)mimeTypes { + size_t outCount = 0; + char **outMimeTypes = nullptr; + size_t *outSizes = nullptr; + char **outStreams = nullptr; + + if (DocumentData::get(document.appDocId).loKitDocument->getClipboard(mimeTypes, + &outCount, &outMimeTypes, + &outSizes, &outStreams)) + { + // return early + if (outCount == 0) + return nil; + + NSMutableArray> *result = [NSMutableArray array]; + + for (size_t i = 0; i < outCount; ++i) { + NSString * identifier = [NSString stringWithUTF8String:outMimeTypes[i]]; + + // For interop with other apps, if this mime-type is known we can export it + UTType * uti = [UTType typeWithMIMEType:identifier]; + if (uti != nil && !uti.dynamic) { + if ([uti conformsToType:UTTypePlainText] && outStreams[i] != nullptr) { + [result addObject:[NSString stringWithUTF8String:outStreams[i]]]; + } + else if ([uti conformsToType:UTTypeImage]) { + [result addObject:[[NSImage alloc] initWithData:[NSData dataWithBytes:outStreams[i] length:outSizes[i]]]]; + } + } + + // Also preserve the data we need, we'll always also export the raw, unaltered bytes + NSPasteboardItem * item = [[NSPasteboardItem alloc] init]; + [item setData:[NSData dataWithBytes:outStreams[i] length:outSizes[i]] forType:identifier]; + } + + return result; + } + else + LOG_DBG("failed to fetch mime-types"); + + return nil; +} + +/** + * Get the clipboard content. Defaults to fetching text and/or html only, when a generic query fails. + */ ++ (NSArray> * _Nullable) getClipboardWith:(Document *_Nonnull)document { + NSArray> * result = [COWrapper getClipboardInternalWith:document mimeTypes:nullptr]; + if (result != nil) + return result; + + const char* textMimeTypes[] = { + "text/plain;charset=utf-8", + "text/html", + nullptr + }; + + return [COWrapper getClipboardInternalWith:document mimeTypes:textMimeTypes]; +} + +/** + * Sets the LOKit internal clipboard with the content of NSPasteboard. + */ ++ (void)setClipboardWith:(Document *_Nonnull)document from:(NSPasteboard *_Nonnull)pasteboard { + NSMutableDictionary * pasteboardItems = [NSMutableDictionary new]; + + if (pasteboard.pasteboardItems.count != 0) { + NSPasteboardItem *item = pasteboard.pasteboardItems.firstObject; + + for (NSPasteboardType identifier in item.types) + { + UTType * uti = [UTType typeWithIdentifier:identifier]; + NSString * mime = uti? uti.preferredMIMEType: identifier; + + if (mime == nil) { + LOG_WRN("UTI " << [identifier UTF8String] << " did not have associated mime type when deserializing clipboard, skipping..."); + continue; + } + + NSData * value = [item dataForType:identifier]; + if (value == nil) + continue; + + if (uti != nil && [pasteboardItems objectForKey:mime] != nil) { + // We export both mime and UTI keys, don't overwrite the mime-type ones with the UTI ones + continue; + } + + [pasteboardItems setObject:value forKey:mime]; + } + } + + const char * pInMimeTypes[pasteboardItems.count]; + size_t pInSizes[pasteboardItems.count]; + const char * pInStreams[pasteboardItems.count]; + + size_t i = 0; + + for (NSString * mime in pasteboardItems) { + pInMimeTypes[i] = [mime UTF8String]; + pInStreams[i] = (const char*)[pasteboardItems[mime] bytes]; + pInSizes[i] = [pasteboardItems[mime] length]; + i++; + } + + DocumentData::get(document.appDocId).loKitDocument->setClipboard(pasteboardItems.count, pInMimeTypes, pInSizes, pInStreams); +} + +/** + * Insert data into the internal clipboard. The content's format is mimeType\nlegth\ndata\n[...repeat for more mimetypes...]. + */ ++ (bool)sendToInternalWith:(Document *_Nonnull)document content:(NSString *_Nonnull)content { + std::vector html; + + ClipboardData data; + size_t nInCount; + + if ([content hasPrefix:@""]) { + // Content is just HTML + const char * _Nullable content_cstr = [content cStringUsingEncoding:NSUTF8StringEncoding]; + html = std::vector(content_cstr, content_cstr + [content lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + nInCount = 1; + } + else { + // objcString -> std::string (keeps embedded NULs, no extra copy for UTF-8) + std::string buffer(static_cast([content UTF8String]), + [content lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + + // put the buffer into a std::stringbuf, treated as binary (allow NULs in there), and create the input stream + std::stringbuf sb(buffer, std::ios::in | std::ios::binary); + std::istream stream(&sb); + + // read the data + data.read(stream); + nInCount = data.size(); + // DEBUG: data.dumpState(std::cout); + } + + std::vector pInSizes(nInCount); + std::vector pInMimeTypes(nInCount); + std::vector pInStreams(nInCount); + + if (html.empty()) { + for (size_t i = 0; i < nInCount; ++i) { + pInSizes[i] = data._content[i].length(); + pInStreams[i] = data._content[i].c_str(); + pInMimeTypes[i] = data._mimeTypes[i].c_str(); + } + } + else { + pInSizes[0] = html.size(); + pInStreams[0] = html.data(); + pInMimeTypes[0] = "text/html"; + } + + return DocumentData::get(document.appDocId).loKitDocument->setClipboard(nInCount, pInMimeTypes.data(), pInSizes.data(), pInStreams.data()); +} + +/** + * We keep a running count of opening documents here. This is not necessarily in sync with the + * DocBrokerId in DocumentBroker due to potential parallelism when opening multiple documents in + * quick succession. + */ +static std::atomic appDocIdCounter(1); + ++ (int)generateNewAppDocId { + DocumentData::allocate(appDocIdCounter); + return appDocIdCounter++; +} + ++ (int)fakeSocketSocket { + return fakeSocketSocket(); +} + +/** + * Convert NSString to std::string & call the C++ version of the logging function. + */ ++ (void)LOG_DBG:(NSString *)message { + std::string stdMessage = [message UTF8String]; + LOG_DBG(stdMessage); +} + ++ (void)LOG_ERR:(NSString *)message { + std::string stdMessage = [message UTF8String]; + LOG_ERR(stdMessage); +} + ++ (void)LOG_TRC:(NSString *)message { + std::string stdMessage = [message UTF8String]; + LOG_TRC(stdMessage); +} + +@end diff --git a/macos/coda/coda/Document.swift b/macos/coda/coda/Document.swift new file mode 100644 index 0000000000000..8b099fe73c8dd --- /dev/null +++ b/macos/coda/coda/Document.swift @@ -0,0 +1,317 @@ +/* + * 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/. + */ + +import Cocoa +import PDFKit +import WebKit + +/** + * Represents a document in the application. + */ +class Document: NSDocument { + + // MARK: - Properties + + /// For the COWrapper to send the messages to the right file descriptor. + @objc + var fakeClientFd: Int32 = -1 + + /// ID to identify the document to be able to get access to lok::Document (eg. for printing) + @objc + var appDocId: Int32 = -1 + + /// Is this a read-only document? + private var readOnly: Bool = false + + /// The webview that contains the document. + var webView: WKWebView! + + /// The URL of the temporary directory where the document's working files are stored. + private var tempDirectoryURL: URL? + + /// The URL of the temporary file that represents the "live" version of the document. + @objc + var tempFileURL: URL? + + /// Make sure the isModified access can be atomic. + private var modifiedLock = NSLock() + private var _isModified: Bool = false + + /** + * Modified status mirrored from the core. + */ + var isModified: Bool { + get { + modifiedLock.lock() + let value = _isModified + modifiedLock.unlock() + + return value + } + set { + modifiedLock.lock() + + let oldValue = _isModified + _isModified = newValue + + // trigger the saving operation when the document was previously marked as modified, but changes to non-modified + if oldValue && !newValue { + updateChangeCount(.changeDone) + } + + modifiedLock.unlock() + } + } + + // MARK: - Initialization + + override init() { + super.init() + // Initialization code here. + } + + // MARK: - NSDocument Overrides + + /** + * Enables autosaving. + */ + override class var autosavesInPlace: Bool { + return true + } + + /** + * Creates the window controllers for the document. + */ + override func makeWindowControllers() { + // Load the storyboard and get the window controller. + let storyboard = NSStoryboard(name: "Main", bundle: nil) + let identifier = NSStoryboard.SceneIdentifier("DocumentWindowController") + guard let windowController = storyboard.instantiateController(withIdentifier: identifier) as? WindowController else { + fatalError("Unable to find DocumentWindowController in storyboard.") + } + self.addWindowController(windowController) + + if let viewController = windowController.contentViewController as? ViewController { + viewController.loadDocument(self) + } + } + + /** + * Called by the system when it wants to save or autosave the document. + */ + override func data(ofType typeName: String) throws -> Data { + guard let tempFileURL = self.tempFileURL else { + // FIXME: handle error? + return Data() + } + + // Read the latest data from the temp file + let data = try Data(contentsOf: tempFileURL) + return data + } + + /** + * We save asynchronously, so that COOL can first write the file, and we can then copy it to the right location. + */ + override func canAsynchronouslyWrite(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType) -> Bool { + return true + } + + /** + * Make sure that we first save by COOL when the user chooses to Save, and only then copy the content to the resulting place. + */ + override func save(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, completionHandler: @escaping ((any Error)?) -> Void) { + + if isModified { + // we have to wait for COOL to save first + DispatchQueue.main.async { + COWrapper.handleMessage(with: self, message: "save dontTerminateEdit=1 dontSaveIfUnmodified=1") + } + + // FIXME would be much better to have handleMessage() with some kind of await or completion handler, + // and call the completionHandler() from there, so that we can be 100% sure the save has concluded + DispatchQueue.main.async { + completionHandler(nil) + } + } + else { + // all is good, we can proceed with copying the data from COOL + super.save(to: url, ofType: typeName, for: saveOperation, completionHandler: completionHandler) + } + } + + /** + * Called by the system when the document is opened. The system provides the file contents as `Data`. + * We create a non-predictable temporary directory using a UUID, and store the `data` there. + */ + override func read(from data: Data, ofType typeName: String) throws { + // Create a unique temp directory + let tempDirBase = FileManager.default.temporaryDirectory + let uniqueDirName = UUID().uuidString + let tempDir = tempDirBase.appendingPathComponent(uniqueDirName, isDirectory: true) + + try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true, attributes: nil) + + self.tempDirectoryURL = tempDir + + // If fileURL is available (document opened from a file), preserve the original filename. + // If not available, use a generic name. + let fileName: String + if let fileURL = self.fileURL { + fileName = fileURL.lastPathComponent + } + else { + fileName = "Document-\(UUID().uuidString)" + } + + let tempFile = tempDir.appendingPathComponent(fileName) + try data.write(to: tempFile, options: .atomic) + + self.tempFileURL = tempFile + } + + /** + * Implement printing. + */ + override func printOperation(withSettings printSettings: [NSPrintInfo.AttributeKey : Any]) throws -> NSPrintOperation { + // export to a temporary PDF file + let tmpURL = FileManager.default + .temporaryDirectory + .appendingPathComponent(UUID().uuidString) + .appendingPathExtension("pdf") + + COWrapper.saveAs(with: self, url: tmpURL.absoluteString, format: "pdf", filterOptions: nil) + + // load the PDF into a PDFView + guard let pdfDocument = PDFDocument(url: tmpURL) else { + throw CocoaError(.fileReadCorruptFile, userInfo: [NSURLErrorKey: tmpURL]) + } + + // we no longer need the file + try? FileManager.default.removeItem(at: tmpURL) + + guard let op = pdfDocument.printOperation(for: self.printInfo, scalingMode: .pageScaleNone, autoRotate: true) else { + throw CocoaError(.fileReadCorruptFile, userInfo: [NSURLErrorKey: tmpURL]) + } + + return op + } + + /** + * Clean up the temporary directory when the document closes. + */ + override func close() { + super.close() + if let tempDir = self.tempDirectoryURL { + try? FileManager.default.removeItem(at: tempDir) + } + } + + /** + * Initiate loading of cool.html, which also triggers loading of the document via lokit. + */ + func loadDocumentInWebView(webView: WKWebView, readOnly: Bool) { + self.webView = webView + self.readOnly = readOnly + + self.appDocId = COWrapper.generateNewAppDocId() + self.fakeClientFd = COWrapper.fakeSocketSocket() + + guard let url = Bundle.main.url(forResource: "cool", withExtension: "html") else { + fatalError("Resource 'cool.html' not found in the main bundle.") + } + + var components = URLComponents(url: url, resolvingAgainstBaseURL: false)! + let permission = readOnly ? "readonly" : "edit" + + components.queryItems = [ + URLQueryItem(name: "file_path", value: tempFileURL!.absoluteString), + URLQueryItem(name: "closebutton", value: "1"), + URLQueryItem(name: "permission", value: permission), + // TODO: add "lang" if needed + URLQueryItem(name: "appdocid", value: "\(self.appDocId)"), + URLQueryItem(name: "userinterfacemode", value: "notebookbar"), + // TODO: add "dir" if needed + ] + + let finalURL = components.url! + let request = URLRequest(url: finalURL) + let urlDir = url.deletingLastPathComponent() + + // If you need read access to a local file, use loadFileURL(_:allowingReadAccessTo:): + // If `finalURL` is a file URL, do: + if finalURL.isFileURL { + webView.loadFileURL(finalURL, allowingReadAccessTo: urlDir) + } else { + // If it's not a file URL, just load the request normally + webView.load(request) + } + } + + /** + * Abbreviated message for debugging. + */ + private func abbreviatedMessage(buffer: UnsafePointer, length: Int) -> String { + // Implement your logic or return a placeholder: + let msgData = Data(bytes: buffer, count: length) + let msgStr = String(data: msgData, encoding: .utf8) ?? "" + return msgStr.prefix(100) + (msgStr.count > 100 ? "..." : "") + } + + /** + * Check if the message is of the given type. + */ + private func isMessageOfType(_ buffer: UnsafePointer, _ prefix: String, length: Int) -> Bool { + let msgData = Data(bytes: buffer, count: min(length, prefix.count)) + guard let msgStr = String(data: msgData, encoding: .utf8) else { return false } + return msgStr == prefix + } + + @objc + func send2JS(_ buffer: UnsafePointer, length: Int) { + let abbrMsg = abbreviatedMessage(buffer: buffer, length: length) + COWrapper.LOG_TRC("To JS: \(abbrMsg)") + + let binaryMessage = (isMessageOfType(buffer, "tile:", length: length) || + isMessageOfType(buffer, "tilecombine:", length: length) || + isMessageOfType(buffer, "delta:", length: length) || + isMessageOfType(buffer, "renderfont:", length: length) || + isMessageOfType(buffer, "rendersearchlist:", length: length) || + isMessageOfType(buffer, "windowpaint:", length: length)) + + let pretext = binaryMessage + ? "window.TheFakeWebSocket.onmessage({'data': window.atob('" + : "window.TheFakeWebSocket.onmessage({'data': window.b64d('" + let posttext = "')});" + + // Convert the buffer to Data + let payloadData = Data(bytes: buffer, count: length) + let encodedPayload = payloadData.base64EncodedString(options: []) + + // Construct the full JavaScript string + let js = pretext + encodedPayload + posttext + + // Truncate for logging + let truncatedJS = js.count > 100 ? (js.prefix(100) + "...") : js[...] + COWrapper.LOG_TRC("Evaluating JavaScript: \(truncatedJS)") + + // Evaluate on main queue + DispatchQueue.main.async { + self.webView.evaluateJavaScript(js) { (obj, error) in + if let error = error as NSError? { + COWrapper.LOG_ERR("Error after \(truncatedJS): \(error.localizedDescription)") + if let jsException = error.userInfo["WKJavaScriptExceptionMessage"] as? String { + COWrapper.LOG_ERR("JavaScript exception: \(jsException)") + } + } + } + } + } +} diff --git a/macos/coda/coda/Info.plist b/macos/coda/coda/Info.plist new file mode 100644 index 0000000000000..0c26c42835731 --- /dev/null +++ b/macos/coda/coda/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleIconFile + AppIcon + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleDocumentTypes + + + NSDocumentClass + $(PRODUCT_MODULE_NAME).Document + CFBundleTypeName + OpenDocument Text + CFBundleTypeRole + Editor + LSItemContentTypes + + org.oasis-open.opendocument.text + + + + NSDocumentClass + $(PRODUCT_MODULE_NAME).Document + CFBundleTypeName + OpenDocument Spreadsheet + CFBundleTypeRole + Editor + LSItemContentTypes + + org.oasis-open.opendocument.spreadsheet + + + + NSDocumentClass + $(PRODUCT_MODULE_NAME).Document + CFBundleTypeName + OpenDocument Presentation + CFBundleTypeRole + Editor + LSItemContentTypes + + org.oasis-open.opendocument.presentation + + + + + diff --git a/macos/coda/coda/ViewController.swift b/macos/coda/coda/ViewController.swift new file mode 100644 index 0000000000000..fa35cf238cf86 --- /dev/null +++ b/macos/coda/coda/ViewController.swift @@ -0,0 +1,340 @@ +/* + * 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/. + */ + +import Cocoa +import WebKit + +class ViewController: NSViewController, WKScriptMessageHandlerWithReply, WKNavigationDelegate { + + /// Access to the NSDocument (document loading & saving infrastructure). + var document: Document! + + /// The actual webview holding the document. + var webView: WKWebView! + + override func viewDidLoad() { + super.viewDidLoad() + + // Setup jsHandler as the entry point co call back from JavaScript + let contentController = WKUserContentController() + contentController.addScriptMessageHandler(self, contentWorld: .page, name: "debug") + contentController.addScriptMessageHandler(self, contentWorld: .page, name: "lok") + contentController.addScriptMessageHandler(self, contentWorld: .page, name: "error") + contentController.addScriptMessageHandler(self, contentWorld: .page, name: "clipboard") + + let config = WKWebViewConfiguration() + config.userContentController = contentController + + // Create the web view + webView = WKWebView(frame: .zero, configuration: config) + webView.navigationDelegate = self + +#if DEBUG + // Enable possibility to debug the webview from Safari + webView.isInspectable = true +#endif + + // Add it to the view controller's view + self.view.addSubview(webView) + + webView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), + webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), + webView.topAnchor.constraint(equalTo: self.view.topAnchor), + webView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor) + ]) + } + + /** + * Load the the document; to be called from the Document (NSDocument) instance. + */ + func loadDocument(_ document: Document) { + self.document = document + self.document.loadDocumentInWebView(webView: webView, readOnly: false) + } + + /** + * Receive message from JavaScript, with the possibility to reply + */ + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { + switch message.name { + + case "error": + if let body = message.body as? String { + COWrapper.LOG_ERR("Error from WebView: \(body)") + } + + case "debug": + if let body = message.body as? String { + print("==> \(body)") + } + + case "clipboard": + if let body = message.body as? String { + switch body { + + case "read": + COWrapper.setClipboard(document, from: .general) + return ("(internal)", nil); + + case "write": + guard let content = COWrapper.getClipboard(document) else { + COWrapper.LOG_ERR("Failed to get clipboard contents") + return (nil, nil) + } + NSPasteboard.general.clearContents() + NSPasteboard.general.writeObjects(content) + + case let s where s.hasPrefix("sendToInternal "): + if !COWrapper.sendToInternalClipboard(document, content: String(s.dropFirst("sendToInternal ".count))) { + COWrapper.LOG_ERR("set clipboard returned failure"); + return (nil, "set clipboard returned failure"); + } + + default: + COWrapper.LOG_ERR("Invalid clipboard action \(body)") + } + } + + case "lok": + if let body = message.body as? String { + COWrapper.LOG_DBG("To Online: \(message.body)") + + if body == "HULLO" { + // Now we know that the JS has started completely + COWrapper.handleHULLO(with: document) + return (nil, nil) + } + else if body == "BYE" { + COWrapper.LOG_TRC("Document window terminating on JavaScript side. Closing our end of the socket.") + //self.bye() + return (nil, nil) + } + else if body.hasPrefix("COMMANDSTATECHANGED ") { + if let brace = body.firstIndex(of: "{") { + // substring that shares storage with the original string + let jsonSlice = body[brace...] + + // convert directly to Data and decode. + let data = Data(jsonSlice.utf8) + do { + let state = try JSONDecoder().decode(CommandStateChange.self, from: data) + + // Has the modification state of the document changed? + // This is smportant for saving which has to copy the document from the temporary location. + if state.commandName == ".uno:ModifiedStatus" { + document?.isModified = (state.state == "true") + } + + // remember states of the commands for app menu handling + if let windowController = view.window?.windowController as? WindowController { + windowController.handleCommandStateChange(state) + } + } catch {} + } + } + else if body == "SLIDESHOW" { + COWrapper.LOG_ERR("TODO: Implement slideshow") + /* + + // Create the SVG for the slideshow + // You need to wrap the C++ functions used here + // Example: + // self.slideshowFile = FileUtil.createRandomTmpDir() + "/slideshow.svg" + // self.slideshowURL = URL(fileURLWithPath: self.slideshowFile) + // DocumentData.get(self.document.appDocId).loKitDocument.saveAs(self.slideshowURL.absoluteString, "svg", nil) + + // Add a new full-screen WebView displaying the slideshow + let configuration = WKWebViewConfiguration() + let userContentController = WKUserContentController() + userContentController.add(self, name: "lok") + configuration.userContentController = userContentController + + self.slideshowWebView = WKWebView(frame: .zero, configuration: configuration) + self.slideshowWebView?.becomeFirstResponder() + self.slideshowWebView?.contentMode = .scaleAspectFit + self.slideshowWebView?.translatesAutoresizingMaskIntoConstraints = false + self.slideshowWebView?.navigationDelegate = self + self.slideshowWebView?.uiDelegate = self + + self.webView.isHidden = true + + self.view.addSubview(self.slideshowWebView!) + self.view.bringSubviewToFront(self.slideshowWebView!) + + // Add constraints + if let slideshowWebView = self.slideshowWebView { + NSLayoutConstraint.activate([ + slideshowWebView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), + slideshowWebView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), + slideshowWebView.topAnchor.constraint(equalTo: self.view.topAnchor), + slideshowWebView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor) + ]) + } + + if let slideshowURL = self.slideshowURL { + let request = URLRequest(url: slideshowURL) + self.slideshowWebView?.load(request) + } + */ + + return (nil, nil) + } + else if body == "EXITSLIDESHOW" { + COWrapper.LOG_ERR("TODO: Implement EXITSLIDESHOW") + /* + // Remove the slideshow file + do { + try FileManager.default.removeItem(atPath: self.slideshowFile) + } catch { + COOLWrapper.LOG_ERR("Failed to remove slideshow file: \(error)") + } + + self.slideshowWebView?.removeFromSuperview() + self.slideshowWebView = nil + self.webView.isHidden = false + */ + return (nil, nil) + } + else if body == "PRINT" { + document.printDocument(self) + return (nil, nil) + } + else if body == "FOCUSIFHWKBD" { + COWrapper.LOG_ERR("TODO: Implement FOCUSIFHWKBD") + /* + if isExternalKeyboardAttached() { + let hwKeyboardMagic = """ + { + if (window.MagicToGetHWKeyboardWorking) { + window.MagicToGetHWKeyboardWorking(); + } + } + """ + self.webView.evaluateJavaScript(hwKeyboardMagic) { (result, error) in + if let error = error { + COOLWrapper.LOG_ERR("Error after \(hwKeyboardMagic): \(error.localizedDescription)") + if let jsException = (error as NSError).userInfo["WKJavaScriptExceptionMessage"] as? String { + COOLWrapper.LOG_ERR("JavaScript exception: \(jsException)") + } + } + } + } + */ + return (nil, nil) + } + else if body.hasPrefix("HYPERLINK") { + let messageBodyItems = body.components(separatedBy: " ") + if messageBodyItems.count >= 2 { + if let url = URL(string: messageBodyItems[1]) { + NSWorkspace.shared.open(url) + return (nil, nil) + } + } + return (nil, nil) + } + else if body == "FONTPICKER" { + COWrapper.LOG_ERR("TODO: Implement FONTPICKER") + /* + // Font picker is not available on macOS like on iOS, but you can use NSFontPanel + let fontManager = NSFontManager.shared + fontManager.target = self + fontManager.action = #selector(changeFont(_:)) + fontManager.orderFrontFontPanel(self) + */ + return (nil, nil) + } + else if body.hasPrefix("downloadas ") { + COWrapper.LOG_ERR("TODO: Implement downloadas") + /* + let messageBodyItems = body.components(separatedBy: " ") + var format: String? + if messageBodyItems.count >= 2 { + for item in messageBodyItems[1...] { + if item.hasPrefix("format=") { + format = String(item.dropFirst("format=".count)) + } + } + guard let format = format else { return } + + // Handle special "direct-" formats + var adjustedFormat = format + if adjustedFormat.hasPrefix("direct-") { + adjustedFormat = String(adjustedFormat.dropFirst("direct-".count)) + } + + // Save the document in the requested format + let tmpFileDirectory = FileManager.default.temporaryDirectory.appendingPathComponent("export") + do { + try FileManager.default.createDirectory(at: tmpFileDirectory, withIntermediateDirectories: true, attributes: nil) + } catch { + COOLWrapper.LOG_ERR("Could not create directory \(tmpFileDirectory.path)") + return + } + let tmpFileName = self.document.copyFileURL.deletingPathExtension().lastPathComponent + "." + adjustedFormat + self.downloadAsTmpURL = tmpFileDirectory.appendingPathComponent(tmpFileName) + + // Remove any existing file + do { + try FileManager.default.removeItem(at: self.downloadAsTmpURL!) + } catch { + // File may not exist, ignore error + } + + // Save the document using your C++ code + // Example: + // DocumentData.get(self.document.appDocId).loKitDocument.saveAs(self.downloadAsTmpURL!.absoluteString, adjustedFormat, nil) + + // Verify the file was saved + let fileExists = FileManager.default.fileExists(atPath: self.downloadAsTmpURL!.path) + if !fileExists { + COOLWrapper.LOG_ERR("Could not save to '\(self.downloadAsTmpURL!.path)'") + return + } + + // Present a save panel to let the user choose where to save the file + let savePanel = NSSavePanel() + savePanel.directoryURL = FileManager.default.homeDirectoryForCurrentUser + savePanel.nameFieldStringValue = tmpFileName + savePanel.begin { (result) in + if result == .OK, let url = savePanel.url { + do { + try FileManager.default.copyItem(at: self.downloadAsTmpURL!, to: url) + // Remove the temporary file + try FileManager.default.removeItem(at: self.downloadAsTmpURL!) + } catch { + COOLWrapper.LOG_ERR("Error during file save: \(error)") + } + } + } + return + } + */ + return (nil, nil) + } + else { + // Just send the message + COWrapper.handleMessage(with: document, message: body) + } + } + + default: + if let body = message.body as? String { + COWrapper.LOG_ERR("Unrecognized kind of message received from WebView: \(message.name):\(body)") + } + else { + COWrapper.LOG_ERR("Unrecognized kind of message received from WebView: \(message.name)") + } + } + + return (nil, nil) + } +} diff --git a/macos/coda/coda/WindowController.swift b/macos/coda/coda/WindowController.swift new file mode 100644 index 0000000000000..827c18b143ecf --- /dev/null +++ b/macos/coda/coda/WindowController.swift @@ -0,0 +1,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/. + */ + +import Cocoa +import WebKit + +enum FormatMenuCommand: Int { + case bold = 1 + case italic + case underline +} + +let formatMenuCommandNames: [FormatMenuCommand: String] = [ + .bold: "Bold", + .italic: "Italic", + .underline: "Underline" +] + +/// The structure we expect in the COMMANDSTATECHANGED payload. +struct CommandStateChange: Decodable { + let commandName: String + let state: String +} + +/// Window controller that manages the document window & menus +class WindowController: NSWindowController, NSMenuItemValidation { + + /// Track menu states here, to be able to show the state in the menu + var commandState: [String: String] = [:] + + /// Single IBAction for all Format commands in the menu + @IBAction func formatMenuAction(_ sender: NSMenuItem) { + guard let command = FormatMenuCommand(rawValue: sender.tag) else { return } + + // Forward to the ViewController so it can actually set bold/italic/... + if let vc = contentViewController as? ViewController { + if let unoCommand = formatMenuCommandNames[command] ?? nil { + COWrapper.handleMessage(with: vc.document, message: "uno .uno:\(unoCommand)") + } + } + } + + /// Remember the new command state value as provided by JS. + func handleCommandStateChange(_ stateChange: CommandStateChange) { + // extract the command name from the ".uno:CommandName" form + let unoCommmandName = stateChange.commandName + guard let colon = unoCommmandName.firstIndex(of: ":") else { return } + let commandName = unoCommmandName[unoCommmandName.index(after: colon)...] + + // store it + commandState[String(commandName)] = stateChange.state + } + + /// Show on/off checkmarks for the particular command(s) like Bold/Italics/... + func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { + if menuItem.action != #selector(formatMenuAction(_:)) { + return true // not our action + } + + // convert the id to a command name + guard let command = FormatMenuCommand(rawValue: menuItem.tag) else { return false } + guard let commandName = formatMenuCommandNames[command] else { return false } + + // Show a check if the command is currently ON + let isOn = commandState[commandName] == "true" + menuItem.state = isOn ? .on : .off + return true + } +} + +/** + * Extend the WKWebView so that we can set the state of Cut/Copy/Paste in the macOS menu too. + */ +extension WKWebView: @retroactive NSMenuItemValidation { + + public func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { + guard let commandState = (window?.windowController as? WindowController)?.commandState else { + return true + } + + if menuItem.action == #selector(NSText.cut(_:)) { + return commandState["Cut"] != "disabled" + } + else if menuItem.action == #selector(NSText.copy(_:)) { + return commandState["Copy"] != "disabled" + } + else if menuItem.action == #selector(NSText.paste(_:)) { + return commandState["Paste"] != "disabled" + } + + return true + } +} diff --git a/macos/coda/coda/coda.entitlements b/macos/coda/coda/coda.entitlements new file mode 100644 index 0000000000000..d0a021865287e --- /dev/null +++ b/macos/coda/coda/coda.entitlements @@ -0,0 +1,20 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.cs.disable-library-validation + + com.apple.security.files.user-selected.read-write + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.print + + + diff --git a/macos/coda/coda/macos.h b/macos/coda/coda/macos.h new file mode 100644 index 0000000000000..f07ee1518bcc3 --- /dev/null +++ b/macos/coda/coda/macos.h @@ -0,0 +1,42 @@ +/* -*- 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 + +extern const char *user_name; + +extern int coolwsd_server_socket_fd; + +extern LibreOfficeKit *lo_kit; + +/** + * Get the own installation path. + */ +std::string getBundlePath(); + +/** + * Get path of the Application Support Directory (app-specific data files that should be preserved between app launches and across updates). + */ +std::string getAppSupportURL(); + +/** + * Get URL of a resource in the bundle. + */ +std::string getResourceURL(const char *name, const char *ext); + +/** + * Get (filelystem) path of a resource in the bundle. + */ +std::string getResourcePath(const char *name, const char *ext); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/macos/coda/coda/macos.mm b/macos/coda/coda/macos.mm new file mode 100644 index 0000000000000..093f6908f6e34 --- /dev/null +++ b/macos/coda/coda/macos.mm @@ -0,0 +1,72 @@ +// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- +// +// 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 "macos.h" + +#import +#import + +const char *user_name = nullptr; + +int coolwsd_server_socket_fd = -1; + +LibreOfficeKit *lo_kit; + +std::string getBundlePath() { + static std::string bundlePath; + if (bundlePath.empty()) { + NSString *path = [[NSBundle mainBundle] bundlePath]; + bundlePath = std::string([path UTF8String]); + } + return bundlePath; +} + +std::string getAppSupportURL() { + @autoreleasepool { + NSFileManager *fileManager = [NSFileManager defaultManager]; + + // Get the URLs for the Application Support directory in the user domain + NSArray *urls = [fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask]; + NSURL *appSupportURL = [urls firstObject]; + if (!appSupportURL) + return std::string(); + + // Get the app's name from the bundle + NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; + if (!appName) { + // Fallback if CFBundleName is not set + appName = @"CODA"; + } + + // Append your app's name to create a unique directory + NSURL *appDirectoryURL = [appSupportURL URLByAppendingPathComponent:appName isDirectory:YES]; + + // Create the directory if it doesn't exist + if (![fileManager fileExistsAtPath:[appDirectoryURL path]]) { + NSError *error = nil; + BOOL success = [fileManager createDirectoryAtURL:appDirectoryURL withIntermediateDirectories:YES attributes:nil error:&error]; + if (!success) { + NSLog(@"Error creating Application Support directory: %@", error.localizedDescription); + return std::string(); + } + } + + // Return the URL as string + return [[appDirectoryURL absoluteString] UTF8String]; + } +} + +std::string getResourceURL(const char *name, const char *ext) { + NSURL *url = [[NSBundle mainBundle] URLForResource:[NSString stringWithUTF8String:name] withExtension:[NSString stringWithUTF8String:ext]]; + return std::string([[url absoluteString] UTF8String]); +} + +std::string getResourcePath(const char *name, const char *ext) { + NSString *path = [[NSBundle mainBundle] pathForResource:[NSString stringWithUTF8String:name] ofType:[NSString stringWithUTF8String:ext]]; + return std::string([path UTF8String]); +} + +// vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/macos/coda/codaTests/codaTests.swift b/macos/coda/codaTests/codaTests.swift new file mode 100644 index 0000000000000..df97b7089a5a0 --- /dev/null +++ b/macos/coda/codaTests/codaTests.swift @@ -0,0 +1,17 @@ +// +// codaTests.swift +// codaTests +// +// Created by Jan Holešovský on 14.10.2024. +// + +import Testing +@testable import coda + +struct codaTests { + + @Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. + } + +} diff --git a/macos/coda/codaUITests/codaUITests.swift b/macos/coda/codaUITests/codaUITests.swift new file mode 100644 index 0000000000000..ac22c15d88917 --- /dev/null +++ b/macos/coda/codaUITests/codaUITests.swift @@ -0,0 +1,43 @@ +// +// codaUITests.swift +// codaUITests +// +// Created by Jan Holešovský on 14.10.2024. +// + +import XCTest + +final class codaUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + @MainActor + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + @MainActor + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/macos/coda/codaUITests/codaUITestsLaunchTests.swift b/macos/coda/codaUITests/codaUITestsLaunchTests.swift new file mode 100644 index 0000000000000..acfe81df1a986 --- /dev/null +++ b/macos/coda/codaUITests/codaUITestsLaunchTests.swift @@ -0,0 +1,33 @@ +// +// codaUITestsLaunchTests.swift +// codaUITests +// +// Created by Jan Holešovský on 14.10.2024. +// + +import XCTest + +final class codaUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + @MainActor + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} diff --git a/macos/coolforkit/.gitignore b/macos/coolforkit/.gitignore new file mode 100644 index 0000000000000..176b3d2ea3277 --- /dev/null +++ b/macos/coolforkit/.gitignore @@ -0,0 +1,3 @@ +/Config.xcconfig +/coolforkit.xcodeproj/project.xcworkspace/ +/coolforkit.xcodeproj/xcuserdata/ diff --git a/macos/coolforkit/Config.xcconfig.in b/macos/coolforkit/Config.xcconfig.in new file mode 100644 index 0000000000000..671b1ae7138f5 --- /dev/null +++ b/macos/coolforkit/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/coolforkit/coolforkit.xcodeproj/project.pbxproj b/macos/coolforkit/coolforkit.xcodeproj/project.pbxproj new file mode 100644 index 0000000000000..64d8755726f8b --- /dev/null +++ b/macos/coolforkit/coolforkit.xcodeproj/project.pbxproj @@ -0,0 +1,442 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + BE9998852D7EC1FD00172B83 /* Common.hpp in Sources */ = {isa = PBXBuildFile; fileRef = BE9998732D7EBEE100172B83 /* Common.hpp */; }; + BE9998862D7EC1FD00172B83 /* ConfigUtil.hpp in Sources */ = {isa = PBXBuildFile; fileRef = BE9998742D7EBEE100172B83 /* ConfigUtil.hpp */; }; + BE9998872D7EC1FD00172B83 /* ConfigUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE9998752D7EBEE100172B83 /* ConfigUtil.cpp */; }; + BE9998882D7EC1FD00172B83 /* FileUtil.hpp in Sources */ = {isa = PBXBuildFile; fileRef = BE9998762D7EBEE100172B83 /* FileUtil.hpp */; }; + BE9998892D7EC1FD00172B83 /* FileUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE9998772D7EBEE100172B83 /* FileUtil.cpp */; }; + BE99988A2D7EC1FD00172B83 /* JailUtil.hpp in Sources */ = {isa = PBXBuildFile; fileRef = BE9998782D7EBEE100172B83 /* JailUtil.hpp */; }; + BE99988B2D7EC1FD00172B83 /* JailUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE9998792D7EBEE100172B83 /* JailUtil.cpp */; }; + BE99988C2D7EC1FD00172B83 /* Seccomp.hpp in Sources */ = {isa = PBXBuildFile; fileRef = BE99987A2D7EBEE100172B83 /* Seccomp.hpp */; }; + BE99988D2D7EC1FD00172B83 /* Seccomp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE99987B2D7EBEE100172B83 /* Seccomp.cpp */; }; + BE99988E2D7EC1FD00172B83 /* security.h in Sources */ = {isa = PBXBuildFile; fileRef = BE99987C2D7EBEE100172B83 /* security.h */; }; + BE99988F2D7EC1FD00172B83 /* SigUtil.hpp in Sources */ = {isa = PBXBuildFile; fileRef = BE99987D2D7EBEE100172B83 /* SigUtil.hpp */; }; + BE9998902D7EC1FD00172B83 /* SigUtil-server.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE99987E2D7EBEE100172B83 /* SigUtil-server.cpp */; }; + BE9998912D7EC1FD00172B83 /* Watchdog.hpp in Sources */ = {isa = PBXBuildFile; fileRef = BE99987F2D7EBEE100172B83 /* Watchdog.hpp */; }; + BE9998922D7EC1FD00172B83 /* DeltaSimd.h in Sources */ = {isa = PBXBuildFile; fileRef = BE9998802D7EBF3900172B83 /* DeltaSimd.h */; }; + BE9998932D7EC1FD00172B83 /* DeltaSimd.c in Sources */ = {isa = PBXBuildFile; fileRef = BE9998812D7EBF3900172B83 /* DeltaSimd.c */; }; + BE9998942D7EC1FD00172B83 /* ForKit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE9998712D7EBDF000172B83 /* ForKit.cpp */; }; + BE9998952D7EC1FD00172B83 /* forkit-main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE9998722D7EBDF000172B83 /* forkit-main.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + BE4679112D775BA900569CBF /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + BE4679132D775BA900569CBF /* coolforkit */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = coolforkit; sourceTree = BUILT_PRODUCTS_DIR; }; + BE9998712D7EBDF000172B83 /* ForKit.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ForKit.cpp; path = ../../../../kit/ForKit.cpp; sourceTree = ""; }; + BE9998722D7EBDF000172B83 /* forkit-main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "forkit-main.cpp"; path = "../../../../kit/forkit-main.cpp"; sourceTree = ""; }; + BE9998732D7EBEE100172B83 /* Common.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Common.hpp; path = ../../../../common/Common.hpp; sourceTree = ""; }; + BE9998742D7EBEE100172B83 /* ConfigUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ConfigUtil.hpp; path = ../../../../common/ConfigUtil.hpp; sourceTree = ""; }; + BE9998752D7EBEE100172B83 /* ConfigUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ConfigUtil.cpp; path = ../../../../common/ConfigUtil.cpp; sourceTree = ""; }; + BE9998762D7EBEE100172B83 /* FileUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = FileUtil.hpp; path = ../../../../common/FileUtil.hpp; sourceTree = ""; }; + BE9998772D7EBEE100172B83 /* FileUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FileUtil.cpp; path = ../../../../common/FileUtil.cpp; sourceTree = ""; }; + BE9998782D7EBEE100172B83 /* JailUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = JailUtil.hpp; path = ../../../../common/JailUtil.hpp; sourceTree = ""; }; + BE9998792D7EBEE100172B83 /* JailUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = JailUtil.cpp; path = ../../../../common/JailUtil.cpp; sourceTree = ""; }; + BE99987A2D7EBEE100172B83 /* Seccomp.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Seccomp.hpp; path = ../../../../common/Seccomp.hpp; sourceTree = ""; }; + BE99987B2D7EBEE100172B83 /* Seccomp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Seccomp.cpp; path = ../../../../common/Seccomp.cpp; sourceTree = ""; }; + BE99987C2D7EBEE100172B83 /* security.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = security.h; path = ../../../../common/security.h; sourceTree = ""; }; + BE99987D2D7EBEE100172B83 /* SigUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = SigUtil.hpp; path = ../../../../common/SigUtil.hpp; sourceTree = ""; }; + BE99987E2D7EBEE100172B83 /* SigUtil-server.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "SigUtil-server.cpp"; path = "../../../../common/SigUtil-server.cpp"; sourceTree = ""; }; + BE99987F2D7EBEE100172B83 /* Watchdog.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Watchdog.hpp; path = ../../../../common/Watchdog.hpp; sourceTree = ""; }; + BE9998802D7EBF3900172B83 /* DeltaSimd.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DeltaSimd.h; path = ../../../../kit/DeltaSimd.h; sourceTree = ""; }; + BE9998812D7EBF3900172B83 /* DeltaSimd.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = DeltaSimd.c; path = ../../../../kit/DeltaSimd.c; sourceTree = ""; }; + BE9998962D7EC58B00172B83 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + BE4679152D775BA900569CBF /* coolforkit */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = coolforkit; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + BE4679102D775BA900569CBF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + BE46790A2D775BA900569CBF = { + isa = PBXGroup; + children = ( + BE99986E2D7EBDA100172B83 /* Online */, + BE4679152D775BA900569CBF /* coolforkit */, + BE9998962D7EC58B00172B83 /* Config.xcconfig */, + BE4679142D775BA900569CBF /* Products */, + ); + sourceTree = ""; + }; + BE4679142D775BA900569CBF /* Products */ = { + isa = PBXGroup; + children = ( + BE4679132D775BA900569CBF /* coolforkit */, + ); + name = Products; + sourceTree = ""; + }; + BE99986E2D7EBDA100172B83 /* Online */ = { + isa = PBXGroup; + children = ( + BE99986F2D7EBDAD00172B83 /* common */, + BE9998702D7EBDBE00172B83 /* kit */, + ); + path = Online; + sourceTree = ""; + }; + BE99986F2D7EBDAD00172B83 /* common */ = { + isa = PBXGroup; + children = ( + BE9998732D7EBEE100172B83 /* Common.hpp */, + BE9998742D7EBEE100172B83 /* ConfigUtil.hpp */, + BE9998752D7EBEE100172B83 /* ConfigUtil.cpp */, + BE9998762D7EBEE100172B83 /* FileUtil.hpp */, + BE9998772D7EBEE100172B83 /* FileUtil.cpp */, + BE9998782D7EBEE100172B83 /* JailUtil.hpp */, + BE9998792D7EBEE100172B83 /* JailUtil.cpp */, + BE99987A2D7EBEE100172B83 /* Seccomp.hpp */, + BE99987B2D7EBEE100172B83 /* Seccomp.cpp */, + BE99987C2D7EBEE100172B83 /* security.h */, + BE99987D2D7EBEE100172B83 /* SigUtil.hpp */, + BE99987E2D7EBEE100172B83 /* SigUtil-server.cpp */, + BE99987F2D7EBEE100172B83 /* Watchdog.hpp */, + ); + path = common; + sourceTree = ""; + }; + BE9998702D7EBDBE00172B83 /* kit */ = { + isa = PBXGroup; + children = ( + BE9998802D7EBF3900172B83 /* DeltaSimd.h */, + BE9998812D7EBF3900172B83 /* DeltaSimd.c */, + BE9998712D7EBDF000172B83 /* ForKit.cpp */, + BE9998722D7EBDF000172B83 /* forkit-main.cpp */, + ); + path = kit; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + BE4679122D775BA900569CBF /* coolforkit */ = { + isa = PBXNativeTarget; + buildConfigurationList = BE46791A2D775BA900569CBF /* Build configuration list for PBXNativeTarget "coolforkit" */; + buildPhases = ( + BE46790F2D775BA900569CBF /* Sources */, + BE4679102D775BA900569CBF /* Frameworks */, + BE4679112D775BA900569CBF /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + BE4679152D775BA900569CBF /* coolforkit */, + ); + name = coolforkit; + packageProductDependencies = ( + ); + productName = coolforkit; + productReference = BE4679132D775BA900569CBF /* coolforkit */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BE46790B2D775BA900569CBF /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1620; + TargetAttributes = { + BE4679122D775BA900569CBF = { + CreatedOnToolsVersion = 16.2; + }; + }; + }; + buildConfigurationList = BE46790E2D775BA900569CBF /* Build configuration list for PBXProject "coolforkit" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = BE46790A2D775BA900569CBF; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = BE4679142D775BA900569CBF /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BE4679122D775BA900569CBF /* coolforkit */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + BE46790F2D775BA900569CBF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BE9998852D7EC1FD00172B83 /* Common.hpp in Sources */, + BE9998862D7EC1FD00172B83 /* ConfigUtil.hpp in Sources */, + BE9998872D7EC1FD00172B83 /* ConfigUtil.cpp in Sources */, + BE9998882D7EC1FD00172B83 /* FileUtil.hpp in Sources */, + BE9998892D7EC1FD00172B83 /* FileUtil.cpp in Sources */, + BE99988A2D7EC1FD00172B83 /* JailUtil.hpp in Sources */, + BE99988B2D7EC1FD00172B83 /* JailUtil.cpp in Sources */, + BE99988C2D7EC1FD00172B83 /* Seccomp.hpp in Sources */, + BE99988D2D7EC1FD00172B83 /* Seccomp.cpp in Sources */, + BE99988E2D7EC1FD00172B83 /* security.h in Sources */, + BE99988F2D7EC1FD00172B83 /* SigUtil.hpp in Sources */, + BE9998902D7EC1FD00172B83 /* SigUtil-server.cpp in Sources */, + BE9998912D7EC1FD00172B83 /* Watchdog.hpp in Sources */, + BE9998922D7EC1FD00172B83 /* DeltaSimd.h in Sources */, + BE9998932D7EC1FD00172B83 /* DeltaSimd.c in Sources */, + BE9998942D7EC1FD00172B83 /* ForKit.cpp in Sources */, + BE9998952D7EC1FD00172B83 /* forkit-main.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + BE4679182D775BA900569CBF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BE9998962D7EC58B00172B83 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 15.2; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + BE4679192D775BA900569CBF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 15.2; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + BE46791B2D775BA900569CBF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = J4FQ687VJK; + ENABLE_HARDENED_RUNTIME = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)", + "$(SRCROOT)/coda", + "$(SRCROOT)/../..", + "$(SRCROOT)/../../common", + "$(SRCROOT)/../../kit", + "$(SRCROOT)/../../net", + "$(SRCROOT)/../../wsd", + "$(HOMEBREW_PATH)/include", + ); + LIBRARY_SEARCH_PATHS = "$(HOMEBREW_PATH)/lib"; + OTHER_LDFLAGS = ( + "-liconv", + "-lpam", + "-lPocoFoundation", + "-lPocoUtil", + "-lPocoXML", + "-lPocoJSON", + "-lPocoNet", + "-lPocoNetSSL", + "-lPocoCrypto", + "-lzstd", + "-lpng", + "-lz", + "-lssl", + "-lcrypto", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + BE46791C2D775BA900569CBF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = J4FQ687VJK; + ENABLE_HARDENED_RUNTIME = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)", + "$(SRCROOT)/coda", + "$(SRCROOT)/../..", + "$(SRCROOT)/../../common", + "$(SRCROOT)/../../kit", + "$(SRCROOT)/../../net", + "$(SRCROOT)/../../wsd", + "$(HOMEBREW_PATH)/include", + ); + LIBRARY_SEARCH_PATHS = "$(HOMEBREW_PATH)/lib"; + OTHER_LDFLAGS = ( + "-liconv", + "-lpam", + "-lPocoFoundation", + "-lPocoUtil", + "-lPocoXML", + "-lPocoJSON", + "-lPocoNet", + "-lPocoNetSSL", + "-lPocoCrypto", + "-lzstd", + "-lpng", + "-lz", + "-lssl", + "-lcrypto", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BE46790E2D775BA900569CBF /* Build configuration list for PBXProject "coolforkit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BE4679182D775BA900569CBF /* Debug */, + BE4679192D775BA900569CBF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BE46791A2D775BA900569CBF /* Build configuration list for PBXNativeTarget "coolforkit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BE46791B2D775BA900569CBF /* Debug */, + BE46791C2D775BA900569CBF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BE46790B2D775BA900569CBF /* Project object */; +} diff --git a/macos/coolwsd/.gitignore b/macos/coolwsd/.gitignore new file mode 100644 index 0000000000000..a40ccef413542 --- /dev/null +++ b/macos/coolwsd/.gitignore @@ -0,0 +1,2 @@ +/coolwsd.xcodeproj/project.xcworkspace/ +/coolwsd.xcodeproj/xcuserdata/ diff --git a/macos/coolwsd/coolwsd.xcodeproj/project.pbxproj b/macos/coolwsd/coolwsd.xcodeproj/project.pbxproj new file mode 100644 index 0000000000000..4bea0d03a9852 --- /dev/null +++ b/macos/coolwsd/coolwsd.xcodeproj/project.pbxproj @@ -0,0 +1,589 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXFileReference section */ + BE74B1B52D3F963F009786DF /* Anonymizer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Anonymizer.hpp; path = ../../../common/Anonymizer.hpp; sourceTree = ""; }; + BE74B1B62D3F963F009786DF /* Authorization.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Authorization.hpp; path = ../../../common/Authorization.hpp; sourceTree = ""; }; + BE74B1B72D3F963F009786DF /* Authorization.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Authorization.cpp; path = ../../../common/Authorization.cpp; sourceTree = ""; }; + BE74B1B82D3F963F009786DF /* Clipboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Clipboard.hpp; path = ../../../common/Clipboard.hpp; sourceTree = ""; }; + BE74B1B92D3F963F009786DF /* CommandControl.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = CommandControl.hpp; path = ../../../common/CommandControl.hpp; sourceTree = ""; }; + BE74B1BA2D3F963F009786DF /* CommandControl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = CommandControl.cpp; path = ../../../common/CommandControl.cpp; sourceTree = ""; }; + BE74B1BB2D3F963F009786DF /* Common.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Common.hpp; path = ../../../common/Common.hpp; sourceTree = ""; }; + BE74B1BC2D3F963F009786DF /* ConfigUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ConfigUtil.hpp; path = ../../../common/ConfigUtil.hpp; sourceTree = ""; }; + BE74B1BD2D3F963F009786DF /* ConfigUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ConfigUtil.cpp; path = ../../../common/ConfigUtil.cpp; sourceTree = ""; }; + BE74B1BE2D3F963F009786DF /* CoolMount.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = CoolMount.cpp; path = ../../../common/CoolMount.cpp; sourceTree = ""; }; + BE74B1BF2D3F963F009786DF /* Crypto.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Crypto.hpp; path = ../../../common/Crypto.hpp; sourceTree = ""; }; + BE74B1C02D3F963F009786DF /* Crypto.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Crypto.cpp; path = ../../../common/Crypto.cpp; sourceTree = ""; }; + BE74B1C12D3F963F009786DF /* Crypto-stub.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Crypto-stub.cpp"; path = "../../../common/Crypto-stub.cpp"; sourceTree = ""; }; + BE74B1C22D3F963F009786DF /* DummyTraceEventEmitter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DummyTraceEventEmitter.cpp; path = ../../../common/DummyTraceEventEmitter.cpp; sourceTree = ""; }; + BE74B1C32D3F963F009786DF /* FileUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = FileUtil.hpp; path = ../../../common/FileUtil.hpp; sourceTree = ""; }; + BE74B1C42D3F963F009786DF /* FileUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FileUtil.cpp; path = ../../../common/FileUtil.cpp; sourceTree = ""; }; + BE74B1C52D3F963F009786DF /* FileUtil-apple.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "FileUtil-apple.mm"; path = "../../../common/FileUtil-apple.mm"; sourceTree = ""; }; + BE74B1C62D3F963F009786DF /* FileUtil-unix.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "FileUtil-unix.cpp"; path = "../../../common/FileUtil-unix.cpp"; sourceTree = ""; }; + BE74B1C72D3F963F009786DF /* FileUtil-windows.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "FileUtil-windows.cpp"; path = "../../../common/FileUtil-windows.cpp"; sourceTree = ""; }; + BE74B1C82D3F963F009786DF /* CharacterConverter.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = CharacterConverter.hpp; path = ../../../common/CharacterConverter.hpp; sourceTree = ""; }; + BE74B1C92D3F963F009786DF /* JailUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = JailUtil.hpp; path = ../../../common/JailUtil.hpp; sourceTree = ""; }; + BE74B1CA2D3F963F009786DF /* JailUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = JailUtil.cpp; path = ../../../common/JailUtil.cpp; sourceTree = ""; }; + BE74B1CB2D3F963F009786DF /* JsonUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = JsonUtil.hpp; path = ../../../common/JsonUtil.hpp; sourceTree = ""; }; + BE74B1CC2D3F963F009786DF /* LangUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = LangUtil.hpp; path = ../../../common/LangUtil.hpp; sourceTree = ""; }; + BE74B1CD2D3F963F009786DF /* Log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Log.hpp; path = ../../../common/Log.hpp; sourceTree = ""; }; + BE74B1CE2D3F963F009786DF /* Log.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Log.cpp; path = ../../../common/Log.cpp; sourceTree = ""; }; + BE74B1CF2D3F963F009786DF /* Message.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Message.hpp; path = ../../../common/Message.hpp; sourceTree = ""; }; + BE74B1D02D3F963F009786DF /* MobileApp.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = MobileApp.hpp; path = ../../../common/MobileApp.hpp; sourceTree = ""; }; + BE74B1D12D3F963F009786DF /* MobileApp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MobileApp.cpp; path = ../../../common/MobileApp.cpp; sourceTree = ""; }; + BE74B1D22D3F963F009786DF /* Png.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Png.hpp; path = ../../../common/Png.hpp; sourceTree = ""; }; + BE74B1D32D3F963F009786DF /* Protocol.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Protocol.hpp; path = ../../../common/Protocol.hpp; sourceTree = ""; }; + BE74B1D42D3F963F009786DF /* Protocol.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Protocol.cpp; path = ../../../common/Protocol.cpp; sourceTree = ""; }; + BE74B1D52D3F963F009786DF /* Rectangle.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Rectangle.hpp; path = ../../../common/Rectangle.hpp; sourceTree = ""; }; + BE74B1D62D3F963F009786DF /* RenderTiles.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = RenderTiles.hpp; path = ../../../common/RenderTiles.hpp; sourceTree = ""; }; + BE74B1D72D3F963F009786DF /* Seccomp.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Seccomp.hpp; path = ../../../common/Seccomp.hpp; sourceTree = ""; }; + BE74B1D82D3F963F009786DF /* Seccomp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Seccomp.cpp; path = ../../../common/Seccomp.cpp; sourceTree = ""; }; + BE74B1D92D3F963F009786DF /* security.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = security.h; path = ../../../common/security.h; sourceTree = ""; }; + BE74B1DA2D3F963F009786DF /* Session.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Session.hpp; path = ../../../common/Session.hpp; sourceTree = ""; }; + BE74B1DB2D3F963F009786DF /* Session.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Session.cpp; path = ../../../common/Session.cpp; sourceTree = ""; }; + BE74B1DC2D3F963F009786DF /* SigHandlerTrap.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = SigHandlerTrap.hpp; path = ../../../common/SigHandlerTrap.hpp; sourceTree = ""; }; + BE74B1DD2D3F963F009786DF /* SigUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = SigUtil.hpp; path = ../../../common/SigUtil.hpp; sourceTree = ""; }; + BE74B1DE2D3F963F009786DF /* SigUtil-mobile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "SigUtil-mobile.cpp"; path = "../../../common/SigUtil-mobile.cpp"; sourceTree = ""; }; + BE74B1DF2D3F963F009786DF /* SigUtil-server.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "SigUtil-server.cpp"; path = "../../../common/SigUtil-server.cpp"; sourceTree = ""; }; + BE74B1E02D3F963F009786DF /* Simd.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Simd.hpp; path = ../../../common/Simd.hpp; sourceTree = ""; }; + BE74B1E12D3F963F009786DF /* Simd.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Simd.cpp; path = ../../../common/Simd.cpp; sourceTree = ""; }; + BE74B1E22D3F963F009786DF /* SpookyV2.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SpookyV2.h; path = ../../../common/SpookyV2.h; sourceTree = ""; }; + BE74B1E32D3F963F009786DF /* SpookyV2.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SpookyV2.cpp; path = ../../../common/SpookyV2.cpp; sourceTree = ""; }; + BE74B1E42D3F963F009786DF /* StateEnum.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = StateEnum.hpp; path = ../../../common/StateEnum.hpp; sourceTree = ""; }; + BE74B1E52D3F963F009786DF /* StringVector.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = StringVector.hpp; path = ../../../common/StringVector.hpp; sourceTree = ""; }; + BE74B1E62D3F963F009786DF /* StringVector.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = StringVector.cpp; path = ../../../common/StringVector.cpp; sourceTree = ""; }; + BE74B1E72D3F963F009786DF /* Syscall.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Syscall.hpp; path = ../../../common/Syscall.hpp; sourceTree = ""; }; + BE74B1E82D3F963F009786DF /* Syscall.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Syscall.cpp; path = ../../../common/Syscall.cpp; sourceTree = ""; }; + BE74B1E92D3F963F009786DF /* ThreadPool.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ThreadPool.hpp; path = ../../../common/ThreadPool.hpp; sourceTree = ""; }; + BE74B1EA2D3F963F009786DF /* TraceEvent.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = TraceEvent.hpp; path = ../../../common/TraceEvent.hpp; sourceTree = ""; }; + BE74B1EB2D3F963F009786DF /* TraceEvent.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = TraceEvent.cpp; path = ../../../common/TraceEvent.cpp; sourceTree = ""; }; + BE74B1EC2D3F963F009786DF /* Unit.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Unit.hpp; path = ../../../common/Unit.hpp; sourceTree = ""; }; + BE74B1ED2D3F963F009786DF /* Unit.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Unit.cpp; path = ../../../common/Unit.cpp; sourceTree = ""; }; + BE74B1EE2D3F963F009786DF /* Unit-mobile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Unit-mobile.cpp"; path = "../../../common/Unit-mobile.cpp"; sourceTree = ""; }; + BE74B1EF2D3F963F009786DF /* Unit-server.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Unit-server.cpp"; path = "../../../common/Unit-server.cpp"; sourceTree = ""; }; + BE74B1F02D3F963F009786DF /* Uri.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Uri.hpp; path = ../../../common/Uri.hpp; sourceTree = ""; }; + BE74B1F12D3F963F009786DF /* Uri.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Uri.cpp; path = ../../../common/Uri.cpp; sourceTree = ""; }; + BE74B1F22D3F963F009786DF /* Util.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Util.hpp; path = ../../../common/Util.hpp; sourceTree = ""; }; + BE74B1F32D3F963F009786DF /* Util.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Util.cpp; path = ../../../common/Util.cpp; sourceTree = ""; }; + BE74B1F42D3F963F009786DF /* Util-mobile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Util-mobile.cpp"; path = "../../../common/Util-mobile.cpp"; sourceTree = ""; }; + BE74B1F52D3F963F009786DF /* Util-server.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Util-server.cpp"; path = "../../../common/Util-server.cpp"; sourceTree = ""; }; + BE74B1F62D3F963F009786DF /* Util-unix.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Util-unix.cpp"; path = "../../../common/Util-unix.cpp"; sourceTree = ""; }; + BE74B1F72D3F963F009786DF /* Util-windows.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Util-windows.cpp"; path = "../../../common/Util-windows.cpp"; sourceTree = ""; }; + BE74B1F82D3F963F009786DF /* Watchdog.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Watchdog.hpp; path = ../../../common/Watchdog.hpp; sourceTree = ""; }; + BE74B1F92D3F96CB009786DF /* Delta.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Delta.hpp; path = ../../../kit/Delta.hpp; sourceTree = ""; }; + BE74B1FA2D3F96CB009786DF /* DeltaSimd.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DeltaSimd.h; path = ../../../kit/DeltaSimd.h; sourceTree = ""; }; + BE74B1FB2D3F96CB009786DF /* DeltaSimd.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = DeltaSimd.c; path = ../../../kit/DeltaSimd.c; sourceTree = ""; }; + BE74B1FC2D3F96CB009786DF /* DummyLibreOfficeKit.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DummyLibreOfficeKit.hpp; path = ../../../kit/DummyLibreOfficeKit.hpp; sourceTree = ""; }; + BE74B1FD2D3F96CB009786DF /* DummyLibreOfficeKit.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DummyLibreOfficeKit.cpp; path = ../../../kit/DummyLibreOfficeKit.cpp; sourceTree = ""; }; + BE74B1FE2D3F96CB009786DF /* ForKit.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ForKit.cpp; path = ../../../kit/ForKit.cpp; sourceTree = ""; }; + BE74B1FF2D3F96CB009786DF /* forkit-main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "forkit-main.cpp"; path = "../../../kit/forkit-main.cpp"; sourceTree = ""; }; + BE74B2002D3F96CB009786DF /* ChildSession.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ChildSession.hpp; path = ../../../kit/ChildSession.hpp; sourceTree = ""; }; + BE74B2012D3F96CB009786DF /* ChildSession.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ChildSession.cpp; path = ../../../kit/ChildSession.cpp; sourceTree = ""; }; + BE74B2022D3F96CB009786DF /* Kit.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Kit.hpp; path = ../../../kit/Kit.hpp; sourceTree = ""; }; + BE74B2032D3F96CB009786DF /* Kit.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Kit.cpp; path = ../../../kit/Kit.cpp; sourceTree = ""; }; + BE74B2042D3F96CB009786DF /* KitHelper.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = KitHelper.hpp; path = ../../../kit/KitHelper.hpp; sourceTree = ""; }; + BE74B2052D3F96CB009786DF /* KitQueue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = KitQueue.hpp; path = ../../../kit/KitQueue.hpp; sourceTree = ""; }; + BE74B2062D3F96CB009786DF /* KitQueue.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = KitQueue.cpp; path = ../../../kit/KitQueue.cpp; sourceTree = ""; }; + BE74B2072D3F96CB009786DF /* KitWebSocket.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = KitWebSocket.hpp; path = ../../../kit/KitWebSocket.hpp; sourceTree = ""; }; + BE74B2082D3F96CB009786DF /* KitWebSocket.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = KitWebSocket.cpp; path = ../../../kit/KitWebSocket.cpp; sourceTree = ""; }; + BE74B2092D3F96CB009786DF /* SetupKitEnvironment.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = SetupKitEnvironment.hpp; path = ../../../kit/SetupKitEnvironment.hpp; sourceTree = ""; }; + BE74B20A2D3F96CB009786DF /* StateRecorder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = StateRecorder.hpp; path = ../../../kit/StateRecorder.hpp; sourceTree = ""; }; + BE74B20B2D3F96CB009786DF /* TestStubs.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = TestStubs.cpp; path = ../../../kit/TestStubs.cpp; sourceTree = ""; }; + BE74B20C2D3F96CB009786DF /* Watermark.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Watermark.hpp; path = ../../../kit/Watermark.hpp; sourceTree = ""; }; + BE74B20D2D3F971C009786DF /* AsyncDNS.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = AsyncDNS.hpp; path = ../../../net/AsyncDNS.hpp; sourceTree = ""; }; + BE74B20E2D3F971C009786DF /* Buffer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Buffer.hpp; path = ../../../net/Buffer.hpp; sourceTree = ""; }; + BE74B20F2D3F971C009786DF /* clientnb.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = clientnb.cpp; path = ../../../net/clientnb.cpp; sourceTree = ""; }; + BE74B2102D3F971C009786DF /* DelaySocket.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DelaySocket.hpp; path = ../../../net/DelaySocket.hpp; sourceTree = ""; }; + BE74B2112D3F971C009786DF /* DelaySocket.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DelaySocket.cpp; path = ../../../net/DelaySocket.cpp; sourceTree = ""; }; + BE74B2122D3F971C009786DF /* FakeSocket.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = FakeSocket.hpp; path = ../../../net/FakeSocket.hpp; sourceTree = ""; }; + BE74B2132D3F971C009786DF /* FakeSocket.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FakeSocket.cpp; path = ../../../net/FakeSocket.cpp; sourceTree = ""; }; + BE74B2142D3F971C009786DF /* HttpHelper.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = HttpHelper.hpp; path = ../../../net/HttpHelper.hpp; sourceTree = ""; }; + BE74B2152D3F971C009786DF /* HttpHelper.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = HttpHelper.cpp; path = ../../../net/HttpHelper.cpp; sourceTree = ""; }; + BE74B2162D3F971C009786DF /* HttpRequest.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = HttpRequest.hpp; path = ../../../net/HttpRequest.hpp; sourceTree = ""; }; + BE74B2172D3F971C009786DF /* HttpRequest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = HttpRequest.cpp; path = ../../../net/HttpRequest.cpp; sourceTree = ""; }; + BE74B2182D3F971C009786DF /* NetUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = NetUtil.hpp; path = ../../../net/NetUtil.hpp; sourceTree = ""; }; + BE74B2192D3F971C009786DF /* NetUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = NetUtil.cpp; path = ../../../net/NetUtil.cpp; sourceTree = ""; }; + BE74B21A2D3F971C009786DF /* ServerSocket.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ServerSocket.hpp; path = ../../../net/ServerSocket.hpp; sourceTree = ""; }; + BE74B21B2D3F971C009786DF /* Socket.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Socket.hpp; path = ../../../net/Socket.hpp; sourceTree = ""; }; + BE74B21C2D3F971C009786DF /* Socket.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Socket.cpp; path = ../../../net/Socket.cpp; sourceTree = ""; }; + BE74B21D2D3F971C009786DF /* Ssl.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Ssl.hpp; path = ../../../net/Ssl.hpp; sourceTree = ""; }; + BE74B21E2D3F971C009786DF /* Ssl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Ssl.cpp; path = ../../../net/Ssl.cpp; sourceTree = ""; }; + BE74B21F2D3F971C009786DF /* SslSocket.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = SslSocket.hpp; path = ../../../net/SslSocket.hpp; sourceTree = ""; }; + BE74B2202D3F971C009786DF /* WebSocketHandler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = WebSocketHandler.hpp; path = ../../../net/WebSocketHandler.hpp; sourceTree = ""; }; + BE74B2212D3F97AB009786DF /* Admin.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Admin.hpp; path = ../../../wsd/Admin.hpp; sourceTree = ""; }; + BE74B2222D3F97AB009786DF /* Admin.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Admin.cpp; path = ../../../wsd/Admin.cpp; sourceTree = ""; }; + BE74B2232D3F97AB009786DF /* AdminModel.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = AdminModel.hpp; path = ../../../wsd/AdminModel.hpp; sourceTree = ""; }; + BE74B2242D3F97AB009786DF /* AdminModel.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = AdminModel.cpp; path = ../../../wsd/AdminModel.cpp; sourceTree = ""; }; + BE74B2252D3F97AB009786DF /* Auth.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Auth.hpp; path = ../../../wsd/Auth.hpp; sourceTree = ""; }; + BE74B2262D3F97AB009786DF /* Auth.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Auth.cpp; path = ../../../wsd/Auth.cpp; sourceTree = ""; }; + BE74B2272D3F97AB009786DF /* ClientRequestDispatcher.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ClientRequestDispatcher.hpp; path = ../../../wsd/ClientRequestDispatcher.hpp; sourceTree = ""; }; + BE74B2282D3F97AB009786DF /* ClientRequestDispatcher.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ClientRequestDispatcher.cpp; path = ../../../wsd/ClientRequestDispatcher.cpp; sourceTree = ""; }; + BE74B2292D3F97AB009786DF /* ClientSession.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ClientSession.hpp; path = ../../../wsd/ClientSession.hpp; sourceTree = ""; }; + BE74B22A2D3F97AB009786DF /* ClientSession.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ClientSession.cpp; path = ../../../wsd/ClientSession.cpp; sourceTree = ""; }; + BE74B22B2D3F97AB009786DF /* ContentSecurityPolicy.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ContentSecurityPolicy.hpp; path = ../../../wsd/ContentSecurityPolicy.hpp; sourceTree = ""; }; + BE74B22C2D3F97AB009786DF /* COOLWSD.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = COOLWSD.hpp; path = ../../../wsd/COOLWSD.hpp; sourceTree = ""; }; + BE74B22D2D3F97AB009786DF /* COOLWSD.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = COOLWSD.cpp; path = ../../../wsd/COOLWSD.cpp; sourceTree = ""; }; + BE74B22E2D3F97AB009786DF /* coolwsd-fork.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "coolwsd-fork.cpp"; path = "../../../wsd/coolwsd-fork.cpp"; sourceTree = ""; }; + BE74B22F2D3F97AB009786DF /* coolwsd-inproc.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "coolwsd-inproc.cpp"; path = "../../../wsd/coolwsd-inproc.cpp"; sourceTree = ""; }; + BE74B2302D3F97AB009786DF /* DocumentBroker.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DocumentBroker.hpp; path = ../../../wsd/DocumentBroker.hpp; sourceTree = ""; }; + BE74B2312D3F97AB009786DF /* DocumentBroker.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DocumentBroker.cpp; path = ../../../wsd/DocumentBroker.cpp; sourceTree = ""; }; + BE74B2322D3F97AB009786DF /* Exceptions.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Exceptions.hpp; path = ../../../wsd/Exceptions.hpp; sourceTree = ""; }; + BE74B2332D3F97AB009786DF /* Exceptions.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Exceptions.cpp; path = ../../../wsd/Exceptions.cpp; sourceTree = ""; }; + BE74B2342D3F97AB009786DF /* FileServer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = FileServer.hpp; path = ../../../wsd/FileServer.hpp; sourceTree = ""; }; + BE74B2352D3F97AB009786DF /* FileServer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FileServer.cpp; path = ../../../wsd/FileServer.cpp; sourceTree = ""; }; + BE74B2362D3F97AB009786DF /* FileServerUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FileServerUtil.cpp; path = ../../../wsd/FileServerUtil.cpp; sourceTree = ""; }; + BE74B2372D3F97AB009786DF /* HostUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = HostUtil.hpp; path = ../../../wsd/HostUtil.hpp; sourceTree = ""; }; + BE74B2382D3F97AB009786DF /* HostUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = HostUtil.cpp; path = ../../../wsd/HostUtil.cpp; sourceTree = ""; }; + BE74B2392D3F97AB009786DF /* Process.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Process.hpp; path = ../../../wsd/Process.hpp; sourceTree = ""; }; + BE74B23A2D3F97AB009786DF /* ProofKey.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ProofKey.hpp; path = ../../../wsd/ProofKey.hpp; sourceTree = ""; }; + BE74B23B2D3F97AB009786DF /* ProofKey.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ProofKey.cpp; path = ../../../wsd/ProofKey.cpp; sourceTree = ""; }; + BE74B23C2D3F97AB009786DF /* ProxyProtocol.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ProxyProtocol.hpp; path = ../../../wsd/ProxyProtocol.hpp; sourceTree = ""; }; + BE74B23D2D3F97AB009786DF /* ProxyProtocol.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ProxyProtocol.cpp; path = ../../../wsd/ProxyProtocol.cpp; sourceTree = ""; }; + BE74B23E2D3F97AB009786DF /* ProxyRequestHandler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ProxyRequestHandler.hpp; path = ../../../wsd/ProxyRequestHandler.hpp; sourceTree = ""; }; + BE74B23F2D3F97AB009786DF /* ProxyRequestHandler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ProxyRequestHandler.cpp; path = ../../../wsd/ProxyRequestHandler.cpp; sourceTree = ""; }; + BE74B2402D3F97AB009786DF /* QuarantineUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = QuarantineUtil.hpp; path = ../../../wsd/QuarantineUtil.hpp; sourceTree = ""; }; + BE74B2412D3F97AB009786DF /* QuarantineUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = QuarantineUtil.cpp; path = ../../../wsd/QuarantineUtil.cpp; sourceTree = ""; }; + BE74B2422D3F97AB009786DF /* RemoteConfig.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = RemoteConfig.hpp; path = ../../../wsd/RemoteConfig.hpp; sourceTree = ""; }; + BE74B2432D3F97AB009786DF /* RemoteConfig.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = RemoteConfig.cpp; path = ../../../wsd/RemoteConfig.cpp; sourceTree = ""; }; + BE74B2442D3F97AB009786DF /* RequestDetails.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = RequestDetails.hpp; path = ../../../wsd/RequestDetails.hpp; sourceTree = ""; }; + BE74B2452D3F97AB009786DF /* RequestDetails.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = RequestDetails.cpp; path = ../../../wsd/RequestDetails.cpp; sourceTree = ""; }; + BE74B2462D3F97AB009786DF /* RequestVettingStation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = RequestVettingStation.hpp; path = ../../../wsd/RequestVettingStation.hpp; sourceTree = ""; }; + BE74B2472D3F97AB009786DF /* RequestVettingStation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = RequestVettingStation.cpp; path = ../../../wsd/RequestVettingStation.cpp; sourceTree = ""; }; + BE74B2482D3F97AB009786DF /* SenderQueue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = SenderQueue.hpp; path = ../../../wsd/SenderQueue.hpp; sourceTree = ""; }; + BE74B2492D3F97AB009786DF /* ServerAuditUtil.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ServerAuditUtil.hpp; path = ../../../wsd/ServerAuditUtil.hpp; sourceTree = ""; }; + BE74B24A2D3F97AB009786DF /* ServerAuditUtil.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ServerAuditUtil.cpp; path = ../../../wsd/ServerAuditUtil.cpp; sourceTree = ""; }; + BE74B24B2D3F97AB009786DF /* ServerURL.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ServerURL.hpp; path = ../../../wsd/ServerURL.hpp; sourceTree = ""; }; + BE74B24C2D3F97AB009786DF /* SpecialBrokers.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = SpecialBrokers.hpp; path = ../../../wsd/SpecialBrokers.hpp; sourceTree = ""; }; + BE74B24D2D3F97AB009786DF /* SpecialBrokers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SpecialBrokers.cpp; path = ../../../wsd/SpecialBrokers.cpp; sourceTree = ""; }; + BE74B24E2D3F97AB009786DF /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Storage.hpp; path = ../../../wsd/Storage.hpp; sourceTree = ""; }; + BE74B24F2D3F97AB009786DF /* Storage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Storage.cpp; path = ../../../wsd/Storage.cpp; sourceTree = ""; }; + BE74B2502D3F97AB009786DF /* TestStubs.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = TestStubs.cpp; path = ../../../wsd/TestStubs.cpp; sourceTree = ""; }; + BE74B2512D3F97AB009786DF /* TileCache.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = TileCache.hpp; path = ../../../wsd/TileCache.hpp; sourceTree = ""; }; + BE74B2522D3F97AB009786DF /* TileCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = TileCache.cpp; path = ../../../wsd/TileCache.cpp; sourceTree = ""; }; + BE74B2532D3F97AB009786DF /* TileDesc.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = TileDesc.hpp; path = ../../../wsd/TileDesc.hpp; sourceTree = ""; }; + BE74B2542D3F97AB009786DF /* TraceFile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = TraceFile.hpp; path = ../../../wsd/TraceFile.hpp; sourceTree = ""; }; + BE74B2552D3F97AB009786DF /* UserMessages.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = UserMessages.hpp; path = ../../../wsd/UserMessages.hpp; sourceTree = ""; }; + BEF9E2022D47D312004FCEB5 /* Util-macos.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "Util-macos.cpp"; path = "../../../common/Util-macos.cpp"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXGroup section */ + BE74B1A52D3F80AA009786DF = { + isa = PBXGroup; + children = ( + BE74B1B02D3F950A009786DF /* common */, + BE74B1B22D3F953B009786DF /* kit */, + BE74B1B32D3F9541009786DF /* net */, + BE74B1B42D3F9546009786DF /* wsd */, + ); + sourceTree = ""; + }; + BE74B1B02D3F950A009786DF /* common */ = { + isa = PBXGroup; + children = ( + BE74B1B52D3F963F009786DF /* Anonymizer.hpp */, + BE74B1B62D3F963F009786DF /* Authorization.hpp */, + BE74B1B72D3F963F009786DF /* Authorization.cpp */, + BE74B1B82D3F963F009786DF /* Clipboard.hpp */, + BE74B1B92D3F963F009786DF /* CommandControl.hpp */, + BE74B1BA2D3F963F009786DF /* CommandControl.cpp */, + BE74B1BB2D3F963F009786DF /* Common.hpp */, + BE74B1BC2D3F963F009786DF /* ConfigUtil.hpp */, + BE74B1BD2D3F963F009786DF /* ConfigUtil.cpp */, + BE74B1BE2D3F963F009786DF /* CoolMount.cpp */, + BE74B1BF2D3F963F009786DF /* Crypto.hpp */, + BE74B1C02D3F963F009786DF /* Crypto.cpp */, + BE74B1C12D3F963F009786DF /* Crypto-stub.cpp */, + BE74B1C22D3F963F009786DF /* DummyTraceEventEmitter.cpp */, + BE74B1C32D3F963F009786DF /* FileUtil.hpp */, + BE74B1C42D3F963F009786DF /* FileUtil.cpp */, + BE74B1C52D3F963F009786DF /* FileUtil-apple.mm */, + BE74B1C62D3F963F009786DF /* FileUtil-unix.cpp */, + BE74B1C72D3F963F009786DF /* FileUtil-windows.cpp */, + BE74B1C82D3F963F009786DF /* CharacterConverter.hpp */, + BE74B1C92D3F963F009786DF /* JailUtil.hpp */, + BE74B1CA2D3F963F009786DF /* JailUtil.cpp */, + BE74B1CB2D3F963F009786DF /* JsonUtil.hpp */, + BE74B1CC2D3F963F009786DF /* LangUtil.hpp */, + BE74B1CD2D3F963F009786DF /* Log.hpp */, + BE74B1CE2D3F963F009786DF /* Log.cpp */, + BE74B1CF2D3F963F009786DF /* Message.hpp */, + BE74B1D02D3F963F009786DF /* MobileApp.hpp */, + BE74B1D12D3F963F009786DF /* MobileApp.cpp */, + BE74B1D22D3F963F009786DF /* Png.hpp */, + BE74B1D32D3F963F009786DF /* Protocol.hpp */, + BE74B1D42D3F963F009786DF /* Protocol.cpp */, + BE74B1D52D3F963F009786DF /* Rectangle.hpp */, + BE74B1D62D3F963F009786DF /* RenderTiles.hpp */, + BE74B1D72D3F963F009786DF /* Seccomp.hpp */, + BE74B1D82D3F963F009786DF /* Seccomp.cpp */, + BE74B1D92D3F963F009786DF /* security.h */, + BE74B1DA2D3F963F009786DF /* Session.hpp */, + BE74B1DB2D3F963F009786DF /* Session.cpp */, + BE74B1DC2D3F963F009786DF /* SigHandlerTrap.hpp */, + BE74B1DD2D3F963F009786DF /* SigUtil.hpp */, + BE74B1DE2D3F963F009786DF /* SigUtil-mobile.cpp */, + BE74B1DF2D3F963F009786DF /* SigUtil-server.cpp */, + BE74B1E02D3F963F009786DF /* Simd.hpp */, + BE74B1E12D3F963F009786DF /* Simd.cpp */, + BE74B1E22D3F963F009786DF /* SpookyV2.h */, + BE74B1E32D3F963F009786DF /* SpookyV2.cpp */, + BE74B1E42D3F963F009786DF /* StateEnum.hpp */, + BE74B1E52D3F963F009786DF /* StringVector.hpp */, + BE74B1E62D3F963F009786DF /* StringVector.cpp */, + BE74B1E72D3F963F009786DF /* Syscall.hpp */, + BE74B1E82D3F963F009786DF /* Syscall.cpp */, + BE74B1E92D3F963F009786DF /* ThreadPool.hpp */, + BE74B1EA2D3F963F009786DF /* TraceEvent.hpp */, + BE74B1EB2D3F963F009786DF /* TraceEvent.cpp */, + BE74B1EC2D3F963F009786DF /* Unit.hpp */, + BE74B1ED2D3F963F009786DF /* Unit.cpp */, + BE74B1EE2D3F963F009786DF /* Unit-mobile.cpp */, + BE74B1EF2D3F963F009786DF /* Unit-server.cpp */, + BE74B1F02D3F963F009786DF /* Uri.hpp */, + BE74B1F12D3F963F009786DF /* Uri.cpp */, + BE74B1F22D3F963F009786DF /* Util.hpp */, + BE74B1F32D3F963F009786DF /* Util.cpp */, + BEF9E2022D47D312004FCEB5 /* Util-macos.cpp */, + BE74B1F42D3F963F009786DF /* Util-mobile.cpp */, + BE74B1F52D3F963F009786DF /* Util-server.cpp */, + BE74B1F62D3F963F009786DF /* Util-unix.cpp */, + BE74B1F72D3F963F009786DF /* Util-windows.cpp */, + BE74B1F82D3F963F009786DF /* Watchdog.hpp */, + ); + path = common; + sourceTree = ""; + }; + BE74B1B22D3F953B009786DF /* kit */ = { + isa = PBXGroup; + children = ( + BE74B1F92D3F96CB009786DF /* Delta.hpp */, + BE74B1FA2D3F96CB009786DF /* DeltaSimd.h */, + BE74B1FB2D3F96CB009786DF /* DeltaSimd.c */, + BE74B1FC2D3F96CB009786DF /* DummyLibreOfficeKit.hpp */, + BE74B1FD2D3F96CB009786DF /* DummyLibreOfficeKit.cpp */, + BE74B1FE2D3F96CB009786DF /* ForKit.cpp */, + BE74B1FF2D3F96CB009786DF /* forkit-main.cpp */, + BE74B2002D3F96CB009786DF /* ChildSession.hpp */, + BE74B2012D3F96CB009786DF /* ChildSession.cpp */, + BE74B2022D3F96CB009786DF /* Kit.hpp */, + BE74B2032D3F96CB009786DF /* Kit.cpp */, + BE74B2042D3F96CB009786DF /* KitHelper.hpp */, + BE74B2052D3F96CB009786DF /* KitQueue.hpp */, + BE74B2062D3F96CB009786DF /* KitQueue.cpp */, + BE74B2072D3F96CB009786DF /* KitWebSocket.hpp */, + BE74B2082D3F96CB009786DF /* KitWebSocket.cpp */, + BE74B2092D3F96CB009786DF /* SetupKitEnvironment.hpp */, + BE74B20A2D3F96CB009786DF /* StateRecorder.hpp */, + BE74B20B2D3F96CB009786DF /* TestStubs.cpp */, + BE74B20C2D3F96CB009786DF /* Watermark.hpp */, + ); + path = kit; + sourceTree = ""; + }; + BE74B1B32D3F9541009786DF /* net */ = { + isa = PBXGroup; + children = ( + BE74B20D2D3F971C009786DF /* AsyncDNS.hpp */, + BE74B20E2D3F971C009786DF /* Buffer.hpp */, + BE74B20F2D3F971C009786DF /* clientnb.cpp */, + BE74B2102D3F971C009786DF /* DelaySocket.hpp */, + BE74B2112D3F971C009786DF /* DelaySocket.cpp */, + BE74B2122D3F971C009786DF /* FakeSocket.hpp */, + BE74B2132D3F971C009786DF /* FakeSocket.cpp */, + BE74B2142D3F971C009786DF /* HttpHelper.hpp */, + BE74B2152D3F971C009786DF /* HttpHelper.cpp */, + BE74B2162D3F971C009786DF /* HttpRequest.hpp */, + BE74B2172D3F971C009786DF /* HttpRequest.cpp */, + BE74B2182D3F971C009786DF /* NetUtil.hpp */, + BE74B2192D3F971C009786DF /* NetUtil.cpp */, + BE74B21A2D3F971C009786DF /* ServerSocket.hpp */, + BE74B21B2D3F971C009786DF /* Socket.hpp */, + BE74B21C2D3F971C009786DF /* Socket.cpp */, + BE74B21D2D3F971C009786DF /* Ssl.hpp */, + BE74B21E2D3F971C009786DF /* Ssl.cpp */, + BE74B21F2D3F971C009786DF /* SslSocket.hpp */, + BE74B2202D3F971C009786DF /* WebSocketHandler.hpp */, + ); + path = net; + sourceTree = ""; + }; + BE74B1B42D3F9546009786DF /* wsd */ = { + isa = PBXGroup; + children = ( + BE74B2212D3F97AB009786DF /* Admin.hpp */, + BE74B2222D3F97AB009786DF /* Admin.cpp */, + BE74B2232D3F97AB009786DF /* AdminModel.hpp */, + BE74B2242D3F97AB009786DF /* AdminModel.cpp */, + BE74B2252D3F97AB009786DF /* Auth.hpp */, + BE74B2262D3F97AB009786DF /* Auth.cpp */, + BE74B2272D3F97AB009786DF /* ClientRequestDispatcher.hpp */, + BE74B2282D3F97AB009786DF /* ClientRequestDispatcher.cpp */, + BE74B2292D3F97AB009786DF /* ClientSession.hpp */, + BE74B22A2D3F97AB009786DF /* ClientSession.cpp */, + BE74B22B2D3F97AB009786DF /* ContentSecurityPolicy.hpp */, + BE74B22C2D3F97AB009786DF /* COOLWSD.hpp */, + BE74B22D2D3F97AB009786DF /* COOLWSD.cpp */, + BE74B22E2D3F97AB009786DF /* coolwsd-fork.cpp */, + BE74B22F2D3F97AB009786DF /* coolwsd-inproc.cpp */, + BE74B2302D3F97AB009786DF /* DocumentBroker.hpp */, + BE74B2312D3F97AB009786DF /* DocumentBroker.cpp */, + BE74B2322D3F97AB009786DF /* Exceptions.hpp */, + BE74B2332D3F97AB009786DF /* Exceptions.cpp */, + BE74B2342D3F97AB009786DF /* FileServer.hpp */, + BE74B2352D3F97AB009786DF /* FileServer.cpp */, + BE74B2362D3F97AB009786DF /* FileServerUtil.cpp */, + BE74B2372D3F97AB009786DF /* HostUtil.hpp */, + BE74B2382D3F97AB009786DF /* HostUtil.cpp */, + BE74B2392D3F97AB009786DF /* Process.hpp */, + BE74B23A2D3F97AB009786DF /* ProofKey.hpp */, + BE74B23B2D3F97AB009786DF /* ProofKey.cpp */, + BE74B23C2D3F97AB009786DF /* ProxyProtocol.hpp */, + BE74B23D2D3F97AB009786DF /* ProxyProtocol.cpp */, + BE74B23E2D3F97AB009786DF /* ProxyRequestHandler.hpp */, + BE74B23F2D3F97AB009786DF /* ProxyRequestHandler.cpp */, + BE74B2402D3F97AB009786DF /* QuarantineUtil.hpp */, + BE74B2412D3F97AB009786DF /* QuarantineUtil.cpp */, + BE74B2422D3F97AB009786DF /* RemoteConfig.hpp */, + BE74B2432D3F97AB009786DF /* RemoteConfig.cpp */, + BE74B2442D3F97AB009786DF /* RequestDetails.hpp */, + BE74B2452D3F97AB009786DF /* RequestDetails.cpp */, + BE74B2462D3F97AB009786DF /* RequestVettingStation.hpp */, + BE74B2472D3F97AB009786DF /* RequestVettingStation.cpp */, + BE74B2482D3F97AB009786DF /* SenderQueue.hpp */, + BE74B2492D3F97AB009786DF /* ServerAuditUtil.hpp */, + BE74B24A2D3F97AB009786DF /* ServerAuditUtil.cpp */, + BE74B24B2D3F97AB009786DF /* ServerURL.hpp */, + BE74B24C2D3F97AB009786DF /* SpecialBrokers.hpp */, + BE74B24D2D3F97AB009786DF /* SpecialBrokers.cpp */, + BE74B24E2D3F97AB009786DF /* Storage.hpp */, + BE74B24F2D3F97AB009786DF /* Storage.cpp */, + BE74B2502D3F97AB009786DF /* TestStubs.cpp */, + BE74B2512D3F97AB009786DF /* TileCache.hpp */, + BE74B2522D3F97AB009786DF /* TileCache.cpp */, + BE74B2532D3F97AB009786DF /* TileDesc.hpp */, + BE74B2542D3F97AB009786DF /* TraceFile.hpp */, + BE74B2552D3F97AB009786DF /* UserMessages.hpp */, + ); + path = wsd; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXLegacyTarget section */ + BE74B1AA2D3F80AA009786DF /* coolwsd */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "-j $(ACTION)"; + buildConfigurationList = BE74B1AD2D3F80AA009786DF /* Build configuration list for PBXLegacyTarget "coolwsd" */; + buildPhases = ( + ); + buildToolPath = "$(PROJECT_DIR)/gmake-wrapper.sh"; + buildWorkingDirectory = "$(PROJECT_DIR)/../.."; + dependencies = ( + ); + name = coolwsd; + packageProductDependencies = ( + ); + passBuildSettingsInEnvironment = 1; + productName = coolwsd; + }; +/* End PBXLegacyTarget section */ + +/* Begin PBXProject section */ + BE74B1A62D3F80AA009786DF /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1600; + TargetAttributes = { + BE74B1AA2D3F80AA009786DF = { + CreatedOnToolsVersion = 16.0; + }; + }; + }; + buildConfigurationList = BE74B1A92D3F80AA009786DF /* Build configuration list for PBXProject "coolwsd" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = BE74B1A52D3F80AA009786DF; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BE74B1AA2D3F80AA009786DF /* coolwsd */, + ); + }; +/* End PBXProject section */ + +/* Begin XCBuildConfiguration section */ + BE74B1AB2D3F80AA009786DF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + }; + name = Debug; + }; + BE74B1AC2D3F80AA009786DF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + }; + name = Release; + }; + BE74B1AE2D3F80AA009786DF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEBUGGING_SYMBOLS = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = J4FQ687VJK; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + BE74B1AF2D3F80AA009786DF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = J4FQ687VJK; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BE74B1A92D3F80AA009786DF /* Build configuration list for PBXProject "coolwsd" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BE74B1AB2D3F80AA009786DF /* Debug */, + BE74B1AC2D3F80AA009786DF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BE74B1AD2D3F80AA009786DF /* Build configuration list for PBXLegacyTarget "coolwsd" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BE74B1AE2D3F80AA009786DF /* Debug */, + BE74B1AF2D3F80AA009786DF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BE74B1A62D3F80AA009786DF /* Project object */; +} diff --git a/macos/coolwsd/coolwsd.xcodeproj/xcshareddata/xcschemes/coolwsd-inproc.xcscheme b/macos/coolwsd/coolwsd.xcodeproj/xcshareddata/xcschemes/coolwsd-inproc.xcscheme new file mode 100644 index 0000000000000..ecf637057e06e --- /dev/null +++ b/macos/coolwsd/coolwsd.xcodeproj/xcshareddata/xcschemes/coolwsd-inproc.xcscheme @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/coolwsd/coolwsd.xcodeproj/xcshareddata/xcschemes/coolwsd.xcscheme b/macos/coolwsd/coolwsd.xcodeproj/xcshareddata/xcschemes/coolwsd.xcscheme new file mode 100644 index 0000000000000..b38260466ef66 --- /dev/null +++ b/macos/coolwsd/coolwsd.xcodeproj/xcshareddata/xcschemes/coolwsd.xcscheme @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/coolwsd/gmake-wrapper.sh.in b/macos/coolwsd/gmake-wrapper.sh.in new file mode 100644 index 0000000000000..29cc4ebb09c92 --- /dev/null +++ b/macos/coolwsd/gmake-wrapper.sh.in @@ -0,0 +1,6 @@ +#!/bin/sh + +HOMEBREW_PATH="@HOMEBREW_PATH@" +eval "$(@HOMEBREW_PATH@/bin/brew shellenv)" + +exec "@HOMEBREW_PATH@/bin/gmake" "$@" diff --git a/net/DelaySocket.cpp b/net/DelaySocket.cpp index ddc8a384d9ace..17863d1bd836a 100644 --- a/net/DelaySocket.cpp +++ b/net/DelaySocket.cpp @@ -11,6 +11,7 @@ #include +#include #include #include @@ -252,7 +253,7 @@ int Delay::create(int delayMs, int physicalFd) if (delayPoll && delayPoll->isAlive()) { int pair[2]; - int rc = socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, pair); + int rc = Syscall::socketpair_cloexec_nonblock(AF_UNIX, SOCK_STREAM /*| SOCK_NONBLOCK | SOCK_CLOEXEC*/, 0, pair); assert(rc == 0); (void)rc; int internalFd = pair[0]; diff --git a/net/FakeSocket.cpp b/net/FakeSocket.cpp index 0acb5c593cb1d..7252532890ce7 100644 --- a/net/FakeSocket.cpp +++ b/net/FakeSocket.cpp @@ -12,7 +12,9 @@ #include "config.h" #include +#ifndef _WIN32 #include +#endif #include #include @@ -105,6 +107,7 @@ static std::string flush() #define FAKESOCKET_LOG(arg) do { if (fakeSocketLogLevel > 0) { loggingBuffer << arg; } } while (false) #endif +EXPORT void fakeSocketSetLoggingCallback(void (*callback)(const std::string&)) { loggingCallback = callback; @@ -138,6 +141,7 @@ static FakeSocketPair& fakeSocketAllocate() return *(fds[i]); } +EXPORT int fakeSocketSocket() { const int result = fakeSocketAllocate().fd[0]; @@ -147,6 +151,7 @@ int fakeSocketSocket() return result; } +EXPORT int fakeSocketPipe2(int pipefd[2]) { FakeSocketPair& pair = fakeSocketAllocate(); @@ -336,6 +341,7 @@ static bool fakeSocketHasAnyPendingActivityGlobal() /** * Wait for any event on any of the fake sockets (theCV is notified on write/close/connect/etc.) */ +EXPORT void fakeSocketWaitAny(int timeoutUs) { if (timeoutUs == 0) @@ -361,6 +367,7 @@ void fakeSocketWaitAny(int timeoutUs) theCV.wait_until(lock, deadline, [](){ return fakeSocketHasAnyPendingActivityGlobal(); }); } +EXPORT int fakeSocketPoll(struct pollfd *pollfds, int nfds, int timeout) { FAKESOCKET_LOG("FakeSocket Poll "); @@ -415,6 +422,7 @@ int fakeSocketPoll(struct pollfd *pollfds, int nfds, int timeout) return result; } +EXPORT int fakeSocketListen(int fd) { std::unique_lock lock(theMutex); @@ -449,6 +457,7 @@ int fakeSocketListen(int fd) return 0; } +EXPORT int fakeSocketConnect(int fd1, int fd2) { std::unique_lock lock(theMutex); @@ -495,6 +504,7 @@ int fakeSocketConnect(int fd1, int fd2) return 0; } +EXPORT int fakeSocketAccept4(int fd) { std::unique_lock lock(theMutex); @@ -547,6 +557,7 @@ int fakeSocketAccept4(int fd) return pair2.fd[1]; } +EXPORT int fakeSocketPeer(int fd) { std::unique_lock lock(theMutex); @@ -567,6 +578,7 @@ int fakeSocketPeer(int fd) return pair.fd[N]; } +EXPORT ssize_t fakeSocketAvailableDataLength(int fd) { std::unique_lock lock(theMutex); @@ -597,6 +609,7 @@ ssize_t fakeSocketAvailableDataLength(int fd) return result; } +EXPORT ssize_t fakeSocketRead(int fd, void *buf, size_t nbytes) { std::unique_lock lock(theMutex); @@ -662,6 +675,7 @@ ssize_t fakeSocketRead(int fd, void *buf, size_t nbytes) return result; } +EXPORT ssize_t fakeSocketWrite(int fd, const void *buf, size_t nbytes) { std::unique_lock lock(theMutex); @@ -704,6 +718,7 @@ ssize_t fakeSocketWrite(int fd, const void *buf, size_t nbytes) return nbytes; } +EXPORT int fakeSocketShutdown(int fd) { std::unique_lock lock(theMutex); @@ -743,6 +758,7 @@ int fakeSocketShutdown(int fd) return 0; } +EXPORT int fakeSocketClose(int fd) { std::unique_lock lock(theMutex); @@ -810,6 +826,7 @@ static void fakeSocketDumpStateImpl() } } +EXPORT void fakeSocketDumpState() { std::unique_lock lock(theMutex); diff --git a/net/FakeSocket.hpp b/net/FakeSocket.hpp index b0047abf8837c..b833b727f17bf 100644 --- a/net/FakeSocket.hpp +++ b/net/FakeSocket.hpp @@ -17,36 +17,53 @@ #include +#ifndef _WIN32 #include +#endif +EXTERNC void fakeSocketSetLoggingCallback(void (*)(const std::string&)); +EXTERNC int fakeSocketSocket(); +EXTERNC int fakeSocketPipe2(int pipefd[2]); +EXTERNC void fakeSocketWaitAny(int timeoutUs); +EXTERNC int fakeSocketPoll(struct pollfd *fds, int nfds, int timeout); +EXTERNC int fakeSocketListen(int fd); +EXTERNC int fakeSocketConnect(int fd1, int fd2); +EXTERNC int fakeSocketAccept4(int fd); +EXTERNC int fakeSocketPeer(int fd); +EXTERNC ssize_t fakeSocketAvailableDataLength(int fd); +EXTERNC ssize_t fakeSocketRead(int fd, void *buf, size_t nbytes); +EXTERNC ssize_t fakeSocketWrite(int fd, const void *buf, size_t nbytes); +EXTERNC int fakeSocketShutdown(int fd); +EXTERNC int fakeSocketClose(int fd); +EXTERNC void fakeSocketDumpState(); #else diff --git a/net/NetUtil.cpp b/net/NetUtil.cpp index 0ee20814f704f..fc515173d4322 100644 --- a/net/NetUtil.cpp +++ b/net/NetUtil.cpp @@ -13,6 +13,7 @@ #include "NetUtil.hpp" #include "AsyncDNS.hpp" +#include #include #include #include @@ -480,7 +481,7 @@ asyncConnect(const std::string& host, const std::string& port, const bool isSSL, { if (ai->ai_addrlen && ai->ai_addr) { - int fd = ::socket(ai->ai_addr->sa_family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + int fd = Syscall::socket_cloexec_nonblock(ai->ai_addr->sa_family, SOCK_STREAM /*| SOCK_NONBLOCK | SOCK_CLOEXEC*/, 0); if (fd < 0) { result = AsyncConnectResult::SocketError; @@ -574,7 +575,7 @@ connect(const std::string& host, const std::string& port, const bool isSSL, { if (ai->ai_addrlen && ai->ai_addr) { - int fd = ::socket(ai->ai_addr->sa_family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + int fd = Syscall::socket_cloexec_nonblock(ai->ai_addr->sa_family, SOCK_STREAM /*| SOCK_NONBLOCK | SOCK_CLOEXEC*/, 0); if (fd < 0) { LOG_SYS("Failed to create socket"); diff --git a/net/ServerSocket.hpp b/net/ServerSocket.hpp index 771f794982e85..d352fdd2ae0f4 100644 --- a/net/ServerSocket.hpp +++ b/net/ServerSocket.hpp @@ -11,6 +11,10 @@ #pragma once +#ifndef _WIN32 +#include +#endif + #include "NetUtil.hpp" #include "memory" @@ -128,6 +132,46 @@ class ServerSocket : public Socket #if !MOBILEAPP +/// Class to remember the socket name, and abstract the fact if it's an abstract unix socket, or real one (backed by a file). +class UnxSocketPath { + +private: + std::string _name; + +public: + UnxSocketPath() : _name() {} + UnxSocketPath(const std::string& name) : _name(name) {} + + /// Make sure we construct the name correctly, based on whether we create the + /// abstract socket, or a normal one. + void fillInto(struct sockaddr_un& addrunix) const { + assert(!_name.empty() && "Trying to use an invalid unx socket"); + +#ifdef HAVE_ABSTRACT_UNIX_SOCKETS + std::memcpy(&addrunix.sun_path[1], _name.c_str(), _name.length()); + addrunix.sun_path[0] = '\0'; // this is an abstract name +#else + std::memcpy(addrunix.sun_path, _name.c_str(), _name.length()); +#endif + LOG_ASSERT_MSG(addrunix.sun_path[sizeof(addrunix.sun_path) - 1] == '\0', + "addrunix.sun_path is not null terminated"); + } + + /// Is this a valid path? + bool isValid() const { + return !_name.empty(); + } + + /// Return the name - either for debugging printount, or for passing as --masterport= + const std::string& getName() const { + return _name; + } +}; + +inline std::ostream& operator<<(std::ostream& os, const UnxSocketPath &s) { + return os << s.getName(); +} + /// A non-blocking, streaming Unix Domain Socket for local use class LocalServerSocket : public ServerSocket { @@ -141,13 +185,13 @@ class LocalServerSocket : public ServerSocket bool bind(Type, int) override { assert(false); return false; } std::shared_ptr accept() override; - std::string bind(); -#ifndef HAVE_ABSTRACT_UNIX_SOCKETS - bool link(std::string to); -#endif + UnxSocketPath bind(); + + /// Links the socket to the location 'toPath', in case it is not an abstract socket. For abstract sockets, it return 'true' right away. + bool linkTo(std::string toPath); private: - std::string _name; + UnxSocketPath _id; #ifndef HAVE_ABSTRACT_UNIX_SOCKETS std::string _linkName; #endif diff --git a/net/Socket.cpp b/net/Socket.cpp index b10a1a7a9e154..c250a26952f4d 100644 --- a/net/Socket.cpp +++ b/net/Socket.cpp @@ -17,6 +17,9 @@ #include #include #include +#if !MOBILEAPP +#include +#endif #include #include #include @@ -50,9 +53,11 @@ #include #include +#ifndef _WIN32 #include #include #include +#endif #ifdef __FreeBSD__ #include @@ -85,7 +90,11 @@ namespace ThreadChecks std::unique_ptr SocketPoll::PollWatchdog; -#define SOCKET_ABSTRACT_UNIX_NAME "0coolwsd-" +#ifndef __APPLE__ +#define SOCKET_ABSTRACT_UNIX_NAME "coolwsd-" +#else +#define SOCKET_ABSTRACT_UNIX_NAME "/tmp/coolwsd-" +#endif #endif @@ -124,7 +133,7 @@ int Socket::createSocket([[maybe_unused]] Socket::Type type) default: assert(!"Unknown Socket::Type"); break; } - return ::socket(domain, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + return Syscall::socket_cloexec_nonblock(domain, SOCK_STREAM /*| SOCK_NONBLOCK | SOCK_CLOEXEC*/, 0); #else return fakeSocketSocket(); #endif @@ -187,7 +196,7 @@ bool StreamSocket::socketpair(const std::chrono::steady_clock::time_point creati std::shared_ptr& child) { int pair[2]; - int rc = ::socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, pair); + int rc = Syscall::socketpair_cloexec_nonblock(AF_UNIX, SOCK_STREAM /*| SOCK_NONBLOCK | SOCK_CLOEXEC*/, 0, pair); if (rc != 0) return false; child = std::make_shared("save-child", pair[0], Socket::Type::Unix, true, HostType::Other, ReadType::NormalRead, creationTime); @@ -861,7 +870,7 @@ void SocketPoll::createWakeups() // Create the wakeup fd. if ( #if !MOBILEAPP - ::pipe2(_wakeup, O_CLOEXEC | O_NONBLOCK) == -1 + Syscall::pipe2(_wakeup, O_CLOEXEC | O_NONBLOCK) == -1 #else fakeSocketPipe2(_wakeup) == -1 #endif @@ -941,13 +950,13 @@ void SocketPoll::insertNewWebSocketSync(const Poco::URI& uri, } bool SocketPoll::insertNewUnixSocket( - const std::string &location, + const UnxSocketPath &location, const std::string &pathAndQuery, const std::shared_ptr& websocketHandler, const std::vector* shareFDs) { LOG_DBG("Connecting to local UDS " << location); - const int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + const int fd = Syscall::socket_cloexec_nonblock(AF_UNIX, SOCK_STREAM /*| SOCK_NONBLOCK | SOCK_CLOEXEC*/, 0); if (fd < 0) { LOG_SYS("Failed to connect to unix socket at " << location); @@ -957,12 +966,7 @@ bool SocketPoll::insertNewUnixSocket( struct sockaddr_un addrunix; std::memset(&addrunix, 0, sizeof(addrunix)); addrunix.sun_family = AF_UNIX; -#ifdef HAVE_ABSTRACT_UNIX_SOCKETS - addrunix.sun_path[0] = '\0'; // abstract name -#else - addrunix.sun_path[0] = '0'; -#endif - std::memcpy(&addrunix.sun_path[1], location.c_str(), location.length()); + location.fillInto(addrunix); const int res = connect(fd, (const struct sockaddr*)&addrunix, sizeof(addrunix)); if (res < 0 && errno != EINPROGRESS) @@ -1307,7 +1311,7 @@ std::shared_ptr ServerSocket::accept() struct sockaddr_in6 clientInfo; socklen_t addrlen = sizeof(clientInfo); - const int rc = ::accept4(getFD(), (struct sockaddr *)&clientInfo, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC); + const int rc = Syscall::accept_cloexec_nonblock(getFD(), (struct sockaddr *)&clientInfo, &addrlen); if (rc < 0) { if (isUnrecoverableAcceptError(errno)) @@ -1375,27 +1379,11 @@ std::shared_ptr ServerSocket::accept() int Socket::getPid() const { -#ifdef __linux__ - struct ucred creds; - socklen_t credSize = sizeof(struct ucred); - if (getsockopt(_fd, SOL_SOCKET, SO_PEERCRED, &creds, &credSize) < 0) - { + int pid = Syscall::get_peer_pid(_fd); + if (pid < 0) LOG_SYS("Failed to get pid via peer creds on " << _fd); - return -1; - } - return creds.pid; -#elif defined(__FreeBSD__) - struct xucred creds; - socklen_t credSize = sizeof(struct xucred); - if (getsockopt(_fd, SOL_LOCAL, LOCAL_PEERCRED, &creds, &credSize) < 0) - { - LOG_SYS("Failed to get pid via peer creds on " << _fd); - return -1; - } - return creds.cr_pid; -#else -#error Implement for your platform -#endif + + return pid; } // Does this socket come from the localhost ? @@ -1413,7 +1401,7 @@ bool Socket::isLocal() const std::shared_ptr LocalServerSocket::accept() { - const int rc = ::accept4(getFD(), nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC); + const int rc = Syscall::accept_cloexec_nonblock(getFD(), nullptr, nullptr); if (rc < 0) { if (isUnrecoverableAcceptError(errno)) @@ -1426,7 +1414,7 @@ std::shared_ptr LocalServerSocket::accept() std::shared_ptr _socket = createSocketFromAccept(rc, Socket::Type::Unix); // Sanity check this incoming socket -#ifndef __FreeBSD__ +#ifdef __linux__ #define CREDS_UID(c) c.uid #define CREDS_GID(c) c.gid #define CREDS_PID(c) c.pid @@ -1438,7 +1426,7 @@ std::shared_ptr LocalServerSocket::accept() ::close(rc); return std::shared_ptr(nullptr); } -#else +#elif defined(__FreeBSD__) #define CREDS_UID(c) c.cr_uid #define CREDS_GID(c) c.cr_groups[0] #define CREDS_PID(c) c.cr_pid @@ -1450,6 +1438,39 @@ std::shared_ptr LocalServerSocket::accept() ::close(rc); return std::shared_ptr(nullptr); } +#elif defined(__APPLE__) + + // On macOS, there's no single struct for all three, + // so define our own 'apple_creds' combining UID/GID/PID. + struct apple_creds { + uid_t uid; + gid_t gid; + pid_t pid; + } creds; + + // Macros to unify usage in the rest of the code: + #define CREDS_UID(c) ((c).uid) + #define CREDS_GID(c) ((c).gid) + #define CREDS_PID(c) ((c).pid) + + // Get the effective UID/GID via getpeereid(): + if (getpeereid(rc, &creds.uid, &creds.gid) != 0) + { + LOG_SYS("Failed to get peer creds (uid/gid) on " << rc); + ::close(rc); + return std::shared_ptr(nullptr); + } + + // Get the peer PID via LOCAL_PEERPID: + socklen_t pidLen = sizeof(creds.pid); + if (getsockopt(rc, SOL_LOCAL, LOCAL_PEERPID, &creds.pid, &pidLen) < 0) + { + LOG_SYS("Failed to get peer pid on " << rc); + ::close(rc); + return std::shared_ptr(nullptr); + } +#else +#error Implement for your platform #endif uid_t uid = getuid(); @@ -1477,7 +1498,7 @@ std::shared_ptr LocalServerSocket::accept() } /// Returns true on success only. -std::string LocalServerSocket::bind() +UnxSocketPath LocalServerSocket::bind() { int rc; struct sockaddr_un addrunix; @@ -1486,13 +1507,13 @@ std::string LocalServerSocket::bind() std::string socketAbstractUnixName(SOCKET_ABSTRACT_UNIX_NAME); const char* snapInstanceName = std::getenv("SNAP_INSTANCE_NAME"); if (snapInstanceName && snapInstanceName[0]) - socketAbstractUnixName = std::string("0snap.") + snapInstanceName + ".coolwsd-"; + socketAbstractUnixName = std::string("snap.") + snapInstanceName + ".coolwsd-"; LOG_INF("Binding to Unix socket for local server with base name: " << socketAbstractUnixName); constexpr auto RandomSuffixLength = 8; constexpr auto MaxSocketAbstractUnixNameLength = - sizeof(addrunix.sun_path) - RandomSuffixLength - 1; // 1 byte for null termination. + sizeof(addrunix.sun_path) - RandomSuffixLength - 2; // 1 byte for null termination, 1 byte for abstract's leading \0 LOG_ASSERT_MSG(socketAbstractUnixName.size() < MaxSocketAbstractUnixNameLength, "SocketAbstractUnixName is too long. Max: " << MaxSocketAbstractUnixNameLength << ", actual: " @@ -1503,47 +1524,43 @@ std::string LocalServerSocket::bind() { std::memset(&addrunix, 0, sizeof(addrunix)); addrunix.sun_family = AF_UNIX; - std::memcpy(addrunix.sun_path, socketAbstractUnixName.c_str(), socketAbstractUnixName.length()); -#ifdef HAVE_ABSTRACT_UNIX_SOCKETS - addrunix.sun_path[0] = '\0'; // abstract name -#endif - const std::string rand = Util::rng::getFilename(RandomSuffixLength); - std::memcpy(addrunix.sun_path + socketAbstractUnixName.size(), rand.c_str(), RandomSuffixLength); - LOG_ASSERT_MSG(addrunix.sun_path[sizeof(addrunix.sun_path) - 1] == '\0', - "addrunix.sun_path is not null terminated"); + const std::string socketName = socketAbstractUnixName + Util::rng::getFilename(RandomSuffixLength); + UnxSocketPath socketPath(socketName); + socketPath.fillInto(addrunix); rc = ::bind(getFD(), (const sockaddr *)&addrunix, sizeof(struct sockaddr_un)); last_errno = errno; LOG_TRC("Binding to Unix socket location [" - << &addrunix.sun_path[1] << "], result: " << rc + << socketPath << "], result: " << rc << ((rc >= 0) ? std::string() : '\t' + Util::symbolicErrno(last_errno) + ": " + std::strerror(last_errno))); + if (rc >= 0) + { + _id = socketPath; + return socketPath; + } } while (rc < 0 && errno == EADDRINUSE); - if (rc >= 0) - { - _name = std::string(&addrunix.sun_path[0]); - return std::string(&addrunix.sun_path[1]); - } - - LOG_ERR_ERRNO(last_errno, "Failed to bind to Unix socket at [" << &addrunix.sun_path[1] << ']'); + LOG_ERR_ERRNO(last_errno, "Failed to bind to Unix socket"); return std::string(); } -#ifndef HAVE_ABSTRACT_UNIX_SOCKETS -bool LocalServerSocket::link(std::string to) +bool LocalServerSocket::linkTo([[maybe_unused]] std::string toPath) { - _linkName = std::move(to); - return ::link(_name.c_str(), _linkName.c_str()) == 0; -} +#ifndef HAVE_ABSTRACT_UNIX_SOCKETS + _linkName = toPath + "/" + _id.getName(); + return 0 == ::link(_id.getName().c_str(), _linkName.c_str()); +#else + return true; #endif +} LocalServerSocket::~LocalServerSocket() { #ifndef HAVE_ABSTRACT_UNIX_SOCKETS - ::unlink(_name.c_str()); + ::unlink(_id.getName().c_str()); if (!_linkName.empty()) ::unlink(_linkName.c_str()); #endif diff --git a/net/Socket.hpp b/net/Socket.hpp index 99bc631aa8bdf..9e112769d6834 100644 --- a/net/Socket.hpp +++ b/net/Socket.hpp @@ -73,6 +73,7 @@ std::ostream& operator<<(std::ostream& os, const Socket &s); class Watchdog; class SocketPoll; +class UnxSocketPath; /// Helper to allow us to easily defer the movement of a socket /// between polls to clarify thread ownership. @@ -982,7 +983,7 @@ class SocketPoll const std::shared_ptr& websocketHandler); bool insertNewUnixSocket( - const std::string &location, + const UnxSocketPath &location, const std::string &pathAndQuery, const std::shared_ptr& websocketHandler, const std::vector* shareFDs = nullptr); diff --git a/net/Ssl.cpp b/net/Ssl.cpp index 6336a23af4843..db38578f650c8 100644 --- a/net/Ssl.cpp +++ b/net/Ssl.cpp @@ -235,6 +235,10 @@ unsigned long SslContext::id() return syscall(SYS_gettid); #elif defined(__FreeBSD__) return pthread_getthreadid_np(); +#elif defined(__APPLE__) + uint64_t tid = 0; + pthread_threadid_np(NULL, &tid); + return tid; #else #error Implement for your platform #endif diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp index 2cf8879c2c76d..8592b231d22a0 100644 --- a/net/WebSocketHandler.hpp +++ b/net/WebSocketHandler.hpp @@ -817,7 +817,7 @@ class WebSocketHandler : public ProtocolHandlerInterface ssize_t i = 0, toSend; while (true) { - toSend = std::min(sizeof(copy), len - i); + toSend = std::min(static_cast(sizeof(copy)), len - i); if (toSend == 0) break; for (ssize_t j = 0; j < toSend; ++j, ++i) diff --git a/test/HttpWhiteBoxTests.cpp b/test/HttpWhiteBoxTests.cpp index 6eb7490a90708..01e86e300df63 100644 --- a/test/HttpWhiteBoxTests.cpp +++ b/test/HttpWhiteBoxTests.cpp @@ -180,7 +180,7 @@ void HttpWhiteBoxTests::testHeader() http::Header header; const std::string data = "\r\na=\r\n\r\n"; - LOK_ASSERT_EQUAL(8L, header.parse(data.c_str(), data.size())); + LOK_ASSERT_EQUAL(static_cast(8), header.parse(data.c_str(), data.size())); LOK_ASSERT_EQUAL(0UL, header.size()); } @@ -248,7 +248,7 @@ void HttpWhiteBoxTests::testRequestParserValidIncomplete() for (std::size_t i = 0; i < 33; ++i) { // Should return 0 to signify that data is incomplete. - LOK_ASSERT_EQUAL_MESSAGE("i = " << i << " of " << data.size() - 1, 0L, + LOK_ASSERT_EQUAL_MESSAGE("i = " << i << " of " << data.size() - 1, static_cast(0), req.readData(data.c_str(), i)); } @@ -264,7 +264,7 @@ void HttpWhiteBoxTests::testRequestParserValidIncomplete() for (std::size_t i = off; i < data.size(); ++i) { // Should return 0 to signify that data is incomplete. - LOK_ASSERT_EQUAL_MESSAGE("i = " << i << " of " << data.size() - 1, 0L, + LOK_ASSERT_EQUAL_MESSAGE("i = " << i << " of " << data.size() - 1, static_cast(0), req.readData(data.c_str() + off, i - off)); } diff --git a/test/Makefile.am b/test/Makefile.am index bd3570dacdda4..84ee7dcc5d260 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -23,6 +23,12 @@ if ENABLE_SSL include_paths += ${OPENSSL_CFLAGS} endif +if ENABLE_MACOS +util_platform_cpp = ../common/Util-macos.cpp +else +util_platform_cpp = ../common/Util-linux.cpp +endif + AM_CXXFLAGS = $(CPPUNIT_CFLAGS) -DTDOC=\"$(abs_top_srcdir)/test/data\" -DTDIST=\"$(DIST_FOLDER)\" \ -I${top_srcdir}/common -I${top_srcdir}/net -I${top_srcdir}/wsd -I${top_srcdir}/kit \ -I${top_srcdir} -I${top_srcdir}/test \ @@ -173,12 +179,14 @@ common_sources = \ ../common/SigUtil-server.cpp \ ../common/Simd.cpp \ ../common/SpookyV2.cpp \ + ../common/Syscall.cpp \ ../common/StringVector.cpp \ ../common/TraceEvent.cpp \ ../common/Unit.cpp \ ../common/Unit-server.cpp \ ../common/Uri.cpp \ ../common/Util-server.cpp \ + $(util_platform_cpp) \ ../common/Util-unix.cpp \ ../common/Util.cpp \ ../kit/KitQueue.cpp \ @@ -213,7 +221,7 @@ unithttplib_LDADD += -lssl -lcrypto endif fakesockettest_CPPFLAGS = -g -fakesockettest_SOURCES = fakesockettest.cpp ../net/FakeSocket.cpp ../common/DummyTraceEventEmitter.cpp ../common/Log.cpp ../common/Util.cpp ../common/Util-server.cpp ../common/Util-unix.cpp ../common/Globals.cpp +fakesockettest_SOURCES = fakesockettest.cpp ../net/FakeSocket.cpp ../common/DummyTraceEventEmitter.cpp ../common/Log.cpp ../common/Util.cpp ../common/Util-server.cpp $(util_platform_cpp) ../common/Util-unix.cpp ../common/Globals.cpp fakesockettest_LDADD = $(CPPUNIT_LIBS) # old-style unit tests - bootstrapped via UnitClient diff --git a/test/UtilTests.cpp b/test/UtilTests.cpp index 6241762a16557..85aede8bd3ae7 100644 --- a/test/UtilTests.cpp +++ b/test/UtilTests.cpp @@ -164,7 +164,12 @@ void UtilTests::testCharacterConverter() constexpr std::string_view testname = __func__; const std::string utf8 = "Ḽơᶉëᶆ ȋṕšᶙṁ ḍỡḽǭᵳ ʂǐť"; +#ifndef __APPLE__ const std::string utf7 = "+HjwBoR2JAOsdhg +AgseVQFhHZkeQQ +Hg0e4R49Ae0dcw +AoIB0AFl-"; +#else + // The macOS iconv gives slightly different results + const std::string utf7 = "+HjwBoR2JAOsdhg +AgseVQFhHZkeQQ +Hg0e4R49Ae0dcw +AoIB0AFl"; +#endif { Util::CharacterConverter utf8_to_7("UTF-8", "UTF-7"); LOK_ASSERT_EQUAL_STR(utf7, utf8_to_7.convert(utf8)); @@ -178,8 +183,14 @@ void UtilTests::testCharacterConverter() { const std::string utf8l = R"xxx(ăѣ𝔠ծềſģȟᎥ𝒋ǩľḿꞑȯ𝘱𝑞𝗋𝘴ȶ𝞄𝜈ψ𝒙𝘆𝚣1234567890!@#$%^&*()-_=+[{]};:'",<.>/?~𝘈Ḇ𝖢𝕯٤ḞԍНǏ𝙅ƘԸⲘ𝙉০Ρ𝗤Ɍ𝓢ȚЦ𝒱Ѡ𝓧ƳȤѧᖯć𝗱ễ𝑓𝙜Ⴙ𝞲𝑗𝒌ļṃʼnо𝞎𝒒ᵲꜱ𝙩ừ𝗏ŵ𝒙𝒚ź1234567890!@#$%^&*()-_=+[{]};:'",<.>/?~АḂⲤ𝗗𝖤𝗙ꞠꓧȊ𝐉𝜥ꓡ𝑀𝑵Ǭ𝙿𝑄Ŗ𝑆𝒯𝖴𝘝𝘞ꓫŸ𝜡ả𝘢ƀ𝖼ḋếᵮℊ𝙝Ꭵ𝕛кιṃդⱺ𝓅𝘲𝕣𝖘ŧ𝑢ṽẉ𝘅ყž1234567890!@#$%^&*()-_=+[{]};:'",<.>/?~Ѧ𝙱ƇᗞΣℱԍҤ١𝔍К𝓛𝓜ƝȎ𝚸𝑄Ṛ𝓢ṮṺƲᏔꓫ𝚈𝚭𝜶Ꮟçძ𝑒𝖿𝗀ḧ𝗂𝐣ҝɭḿ𝕟𝐨𝝔𝕢ṛ𝓼тú𝔳ẃ⤬𝝲𝗓1234567890!@#$%^&*()-_=+[{]};:'",<.>/?~𝖠Β𝒞𝘋𝙴𝓕ĢȞỈ𝕵ꓗʟ𝙼ℕ০𝚸𝗤ՀꓢṰǓⅤ𝔚Ⲭ𝑌𝙕𝘢𝕤)xxx"; +#ifndef __APPLE__ const std::string utf7l = R"xxx(+AQMEY9g13SAFbh7BAX8BIwIfE6XYNdyLAekBPh4/p5ECL9g13jHYNdxe2DXdy9g13jQCNtg134TYNd8IA8jYNdyZ2DXeBtg13qM-1234567890+ACEAQAAjACQAJQBeACYAKg()-+AF8APQArAFsAewBdAH0AOw:'+ACI,+ADw.+AD4-/?+AH7YNd4IHgbYNd2i2DXdbwZkHh4FDQQdAc/YNd5FAZgFOCyY2DXeSQnmA6HYNd3kAkzYNdziAhoEJtg13LEEYNg13OcBswIkBGcVrwEH2DXd8R7F2DXcU9g13lwQudg137LYNdxX2DXcjAE8HkMBSQQ+2DXfjtg13JIdcqcx2DXeaR7r2DXdzwF12DXcmdg13JoBeg-1234567890+ACEAQAAjACQAJQBeACYAKg()-+AF8APQArAFsAewBdAH0AOw:'+ACI,+ADw.+AD4-/?+AH4EEB4CLKTYNd3X2DXdpNg13dmnoKTnAgrYNdwJ2DXfJaTh2DXcQNg13HUB7Ng13n/YNdxEAVbYNdxG2DXcr9g13bTYNd4d2DXeHqTrAXjYNd8hHqPYNd4iAYDYNd28Hgsevx1uIQrYNd5dE6XYNd1bBDoDuR5DBWQsetg13MXYNd4y2DXdY9g13ZgBZ9g13GIefR6J2DXeBRDnAX4-1234567890+ACEAQAAjACQAJQBeACYAKg()-+AF8APQArAFsAewBdAH0AOw:'+ACI,+ADw.+AD4-/?+AH4EZtg13nEBhxXeA6MhMQUNBKQGYdg13Q0EGtg13NvYNdzcAZ0CDtg13rjYNdxEHlrYNdziHm4eegGyE9Sk69g13ojYNd6t2DXfNhPPAOcQ69g13FLYNd2/2DXdwB4n2DXdwtg13CMEnQJtHj/YNd1f2DXcKNg131TYNd1iHlvYNdz8BEIA+tg13TMegyks2DXfctg13dM-1234567890+ACEAQAAjACQAJQBeACYAKg()-+AF8APQArAFsAewBdAH0AOw:'+ACI,+ADw.+AD4-/?+AH7YNd2gA5LYNdye2DXeC9g13nTYNdzVASICHh7I2DXddaTXAp/YNd58IRUJ5tg13rjYNd3kBUCk4h5wAdMhZNg13RosrNg13EzYNd5V2DXeItg13WQ-)xxx"; +#else + // The macOS iconv gives slightly different results + const std::string utf7l = + R"xxx(+AQMEY9g13SAFbh7BAX8BIwIfE6XYNdyLAekBPh4/p5ECL9g13jHYNdxe2DXdy9g13jQCNtg134TYNd8IA8jYNdyZ2DXeBtg13qM-1234567890+ACEAQAAjACQAJQBeACYAKg()-+AF8APQ-+-+AFsAewBdAH0AOw:'+ACI,+ADw.+AD4-/?+AH7YNd4IHgbYNd2i2DXdbwZkHh4FDQQdAc/YNd5FAZgFOCyY2DXeSQnmA6HYNd3kAkzYNdziAhoEJtg13LEEYNg13OcBswIkBGcVrwEH2DXd8R7F2DXcU9g13lwQudg137LYNdxX2DXcjAE8HkMBSQQ+2DXfjtg13JIdcqcx2DXeaR7r2DXdzwF12DXcmdg13JoBeg-1234567890+ACEAQAAjACQAJQBeACYAKg()-+AF8APQ-+-+AFsAewBdAH0AOw:'+ACI,+ADw.+AD4-/?+AH4EEB4CLKTYNd3X2DXdpNg13dmnoKTnAgrYNdwJ2DXfJaTh2DXcQNg13HUB7Ng13n/YNdxEAVbYNdxG2DXcr9g13bTYNd4d2DXeHqTrAXjYNd8hHqPYNd4iAYDYNd28Hgsevx1uIQrYNd5dE6XYNd1bBDoDuR5DBWQsetg13MXYNd4y2DXdY9g13ZgBZ9g13GIefR6J2DXeBRDnAX4-1234567890+ACEAQAAjACQAJQBeACYAKg()-+AF8APQ-+-+AFsAewBdAH0AOw:'+ACI,+ADw.+AD4-/?+AH4EZtg13nEBhxXeA6MhMQUNBKQGYdg13Q0EGtg13NvYNdzcAZ0CDtg13rjYNdxEHlrYNdziHm4eegGyE9Sk69g13ojYNd6t2DXfNhPPAOcQ69g13FLYNd2/2DXdwB4n2DXdwtg13CMEnQJtHj/YNd1f2DXcKNg131TYNd1iHlvYNdz8BEIA+tg13TMegyks2DXfctg13dM-1234567890+ACEAQAAjACQAJQBeACYAKg()-+AF8APQ-+-+AFsAewBdAH0AOw:'+ACI,+ADw.+AD4-/?+AH7YNd2gA5LYNdye2DXeC9g13nTYNdzVASICHh7I2DXddaTXAp/YNd58IRUJ5tg13rjYNd3kBUCk4h5wAdMhZNg13RosrNg13EzYNd5V2DXeItg13WQ-)xxx"; +#endif Util::CharacterConverter utf8_to_7("UTF-8", "UTF-7"); LOK_ASSERT_EQUAL_STR(utf7, utf8_to_7.convert(utf8)); @@ -189,9 +200,13 @@ void UtilTests::testCharacterConverter() Util::CharacterConverter utf7_to_8("UTF-7", "UTF-8"); LOK_ASSERT_EQUAL_STR(utf8, utf7_to_8.convert(utf7)); +#ifndef __APPLE__ LOK_ASSERT_EQUAL_STR(utf8l, utf7_to_8.convert(utf7l)); +#endif LOK_ASSERT_EQUAL_STR(utf8, utf7_to_8.convert(utf7)); +#ifndef __APPLE__ LOK_ASSERT_EQUAL_STR(utf8l, utf7_to_8.convert(utf7l)); +#endif } } diff --git a/test/helpers.hpp b/test/helpers.hpp index 25e98af1d6fc6..cf18385247d07 100644 --- a/test/helpers.hpp +++ b/test/helpers.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -352,7 +353,7 @@ inline int connectToLocalServer(int portNumber, int socketTimeOutMS, bool blocki int socketFD = 0; struct sockaddr_in serv_addr; - if ((socketFD = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0) + if ((socketFD = Syscall::socket_cloexec_nonblock(AF_INET, 0 /*SOCK_STREAM | SOCK_CLOEXEC*/, 0)) < 0) { LOG_ERR("helpers::connectToLocalServer: Server client could not be created."); return -1; diff --git a/tools/map.cpp b/tools/map.cpp index 2c01253621cf7..531cf6b7bc75f 100644 --- a/tools/map.cpp +++ b/tools/map.cpp @@ -24,7 +24,7 @@ #include #include #include -#ifndef __FreeBSD__ +#ifdef __linux__ #include #endif #include @@ -38,7 +38,7 @@ #include #include -#ifdef __FreeBSD__ +#ifndef __linux__ void error(int status, int errnum, const char *format, ...) { va_list args; @@ -517,7 +517,7 @@ static std::vector compressBitmap(const std::vector &bitmap) { char num[16]; output.push_back('['); - sprintf(num, "%d", cnt); + snprintf(num, sizeof(num), "%d", cnt); for (int cpy = 0; num[cpy] != '\0'; ++cpy) output.push_back(num[cpy]); output.push_back(']'); diff --git a/wasm/wasmapp.cpp b/wasm/wasmapp.cpp index 7994c8020cc78..3fbd5c1ee424b 100644 --- a/wasm/wasmapp.cpp +++ b/wasm/wasmapp.cpp @@ -196,7 +196,7 @@ int main(int argc, char* argv_main[]) assert(argc == 3); - Log::initialize("WASM", "error", false, false, {}, false, {}); + Log::initialize("WASM", "error"); Util::setThreadName("main"); fakeSocketSetLoggingCallback([](const std::string& line) diff --git a/windows/coda/CODA.sln b/windows/coda/CODA.sln new file mode 100644 index 0000000000000..a3da614b3af4f --- /dev/null +++ b/windows/coda/CODA.sln @@ -0,0 +1,40 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35521.163 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CODA", "CODA\CODA.csproj", "{A9AD4426-1615-46E6-AC61-248F9133D6AF}" + ProjectSection(ProjectDependencies) = postProject + {B0E65DEC-1CB0-4DAA-B3ED-709F8169BC40} = {B0E65DEC-1CB0-4DAA-B3ED-709F8169BC40} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CODALib", "CODALib\CODALib.vcxproj", "{B0E65DEC-1CB0-4DAA-B3ED-709F8169BC40}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A9AD4426-1615-46E6-AC61-248F9133D6AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9AD4426-1615-46E6-AC61-248F9133D6AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9AD4426-1615-46E6-AC61-248F9133D6AF}.Debug|x64.ActiveCfg = Debug|Any CPU + {A9AD4426-1615-46E6-AC61-248F9133D6AF}.Debug|x64.Build.0 = Debug|Any CPU + {A9AD4426-1615-46E6-AC61-248F9133D6AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9AD4426-1615-46E6-AC61-248F9133D6AF}.Release|Any CPU.Build.0 = Release|Any CPU + {A9AD4426-1615-46E6-AC61-248F9133D6AF}.Release|x64.ActiveCfg = Release|Any CPU + {A9AD4426-1615-46E6-AC61-248F9133D6AF}.Release|x64.Build.0 = Release|Any CPU + {B0E65DEC-1CB0-4DAA-B3ED-709F8169BC40}.Debug|Any CPU.ActiveCfg = Debug|x64 + {B0E65DEC-1CB0-4DAA-B3ED-709F8169BC40}.Debug|Any CPU.Build.0 = Debug|x64 + {B0E65DEC-1CB0-4DAA-B3ED-709F8169BC40}.Debug|x64.ActiveCfg = Debug|x64 + {B0E65DEC-1CB0-4DAA-B3ED-709F8169BC40}.Debug|x64.Build.0 = Debug|x64 + {B0E65DEC-1CB0-4DAA-B3ED-709F8169BC40}.Release|Any CPU.ActiveCfg = Release|x64 + {B0E65DEC-1CB0-4DAA-B3ED-709F8169BC40}.Release|Any CPU.Build.0 = Release|x64 + {B0E65DEC-1CB0-4DAA-B3ED-709F8169BC40}.Release|x64.ActiveCfg = Release|x64 + {B0E65DEC-1CB0-4DAA-B3ED-709F8169BC40}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/windows/coda/CODA/App.config b/windows/coda/CODA/App.config new file mode 100644 index 0000000000000..26f16c9fafbda --- /dev/null +++ b/windows/coda/CODA/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/windows/coda/CODA/App.xaml b/windows/coda/CODA/App.xaml new file mode 100644 index 0000000000000..dc06bd9b060cd --- /dev/null +++ b/windows/coda/CODA/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/windows/coda/CODA/App.xaml.cs b/windows/coda/CODA/App.xaml.cs new file mode 100644 index 0000000000000..9f408b112e570 --- /dev/null +++ b/windows/coda/CODA/App.xaml.cs @@ -0,0 +1,11 @@ +using System.Configuration; +using System.Data; +using System.Windows; + +namespace CODA +{ + public partial class App : System.Windows.Application + { + } + +} diff --git a/windows/coda/CODA/CODA.csproj b/windows/coda/CODA/CODA.csproj new file mode 100644 index 0000000000000..937da0261c237 --- /dev/null +++ b/windows/coda/CODA/CODA.csproj @@ -0,0 +1,25 @@ + + + + + + + + WinExe + net8.0-windows10.0.19041.0 + enable + enable + true + CODA.App + True + + + + + + + + + + + diff --git a/windows/coda/CODA/CODA.csproj.user b/windows/coda/CODA/CODA.csproj.user new file mode 100644 index 0000000000000..466ebea68c370 --- /dev/null +++ b/windows/coda/CODA/CODA.csproj.user @@ -0,0 +1,14 @@ + + + + + + Designer + + + + + Designer + + + \ No newline at end of file diff --git a/windows/coda/CODA/MainWindow.xaml b/windows/coda/CODA/MainWindow.xaml new file mode 100644 index 0000000000000..38ba5797e55ea --- /dev/null +++ b/windows/coda/CODA/MainWindow.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + Use the File menu to open a document + + + + + + + + diff --git a/windows/coda/CODA/MainWindow.xaml.cs b/windows/coda/CODA/MainWindow.xaml.cs new file mode 100644 index 0000000000000..b219e4cd6f166 --- /dev/null +++ b/windows/coda/CODA/MainWindow.xaml.cs @@ -0,0 +1,358 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; fill-column: 100 -*- + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing.Printing; +using System.Runtime.InteropServices; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Threading; +using Microsoft.Web.WebView2.Core; +using Microsoft.Web.WebView2.Wpf; +using Microsoft.Win32; + +namespace CODA +{ + public partial class MainWindow : Window + { + private IWebView2 _iWebView2; + + public delegate void Send2JSDelegate(IntPtr buffer, int length); + public delegate void ReplyWithStringDelegate(string s); + + [DllImport("CODALib.dll")] + public static extern void set_app_installation_path(string path); + + [DllImport("CODALib.dll")] + public static extern void set_app_installation_uri(string uri); + + [DllImport("CODALib.dll")] + public static extern int generate_new_app_doc_id(); + + [DllImport("CODALib.dll")] + public static extern void initialize_cpp_things(); + + [DllImport("CODALib.dll")] + public static extern void set_send2JS_function(Send2JSDelegate f); + + [DllImport("CODALib.dll")] + public static extern void do_hullo_handling_things(string fileURL, int appDocId); + + [DllImport("CODALib.dll")] + public static extern void do_bye_handling_things(); + + [DllImport("CODALib.dll")] + public static extern void do_convert_to(string type, int appDocId, ReplyWithStringDelegate response); + + [DllImport("CODALib.dll")] + public static extern void do_other_message_handling_things([MarshalAs(UnmanagedType.LPStr)] string message); + + // Keep a static reference so that the delegate doesn't get garbage collected. Or something + // like that. + // private static Send2JSDelegate _reference; + private static GCHandle _gch; + + private int _appDocId = -1; + + private string _fileURL; + + private string _exeLocation; + + public MainWindow() + { + Loaded += MainWindow_Loaded; + InitializeComponent(); + Send2JSDelegate fp = new Send2JSDelegate(send2JS); + _gch = GCHandle.Alloc(fp); + set_send2JS_function(fp); + _exeLocation = System.IO.Path.GetDirectoryName(typeof(MainWindow).Assembly.Location); + if (!_exeLocation.EndsWith('\\')) + _exeLocation += "\\"; + set_app_installation_path(_exeLocation); + set_app_installation_uri(new System.Uri(_exeLocation).AbsoluteUri); + initialize_cpp_things(); + } + + private async void MainWindow_Loaded(object sender, RoutedEventArgs e) + { + SetWebView(webView2XamlElement); + await InitializeWebView(webView2XamlElement); + _iWebView2.CoreWebView2.WebMessageReceived += WebView_WebMessageReceived; + } + + private void MainWindow_FileOpen(object sender, RoutedEventArgs e) + { + var openFileDialog = new Microsoft.Win32.OpenFileDialog(); + openFileDialog.Filter = + "Text documents|*.odt;*.docx;*.doc|" + + "Spreadsheets|*.ods;*.xlsx;*.xls|" + + "Presentations|*.odp;*.pptx;*.ppt|" + + "All files|*.*"; + + if (openFileDialog.ShowDialog() == true) + { + _appDocId = generate_new_app_doc_id(); + _fileURL = new Uri(openFileDialog.FileName).AbsoluteUri; + openCOOL(); + } + } + + private void MainWindow_Exit(object sender, RoutedEventArgs e) + { + System.Windows.Application.Current.Shutdown(); + } + + void WebView_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs args) + { + string s = args.WebMessageAsJson; + Debug.WriteLine($"WebView_WebMessageReceived: {s}"); + + if (s.StartsWith("\"MSG ")) + { + s = s.Substring(5); + if (s == "HULLO\"") + { + do_hullo_handling_things(_fileURL, _appDocId); + } + else if (s == "BYE\"") + { + do_bye_handling_things(); + } + else if (s == "PRINT\"") + { + do_convert_to("pdf", _appDocId, s => + { + PrintPdfDocument(s); + }); + } + else if (s.StartsWith("downloadas ")) + { + Debug.WriteLine("Not yet implemented: Save As"); + } + else + { + string message = JsonSerializer.Deserialize(args.WebMessageAsJson); + message = message.Substring(4); + do_other_message_handling_things(message); + } + } + else if (s.StartsWith("\"ERR ")) + { + string message = JsonSerializer.Deserialize(args.WebMessageAsJson); + message = message.Substring(4); + Debug.WriteLine($"Error: {message}"); + } + else if (s.StartsWith("\"DBG ")) + { + string message = JsonSerializer.Deserialize(args.WebMessageAsJson); + message = message.Substring(4); + Debug.WriteLine($"Debug: {message}"); + } + } + + private void SetWebView(IWebView2 newWebView2) + { + _iWebView2 = newWebView2; + + // We display the type of control in the window title, so update that now. + UpdateTitle(); + } + + async Task InitializeWebView(IWebView2 webView2) + { + AttachControlEventHandlers(webView2); + webView2.DefaultBackgroundColor = System.Drawing.Color.Transparent; + await webView2.EnsureCoreWebView2Async(); + } + + void AttachControlEventHandlers(IWebView2 control) + { + control.CoreWebView2InitializationCompleted += WebView_CoreWebView2InitializationCompleted; + } + + private void openCOOL() + { + // Hide the helpful note + useFileDialogXamlElement.Visibility = Visibility.Collapsed; + // Also hide the initial menu as COOL has its own + menuXamlElement.Visibility = Visibility.Collapsed; + + _iWebView2.CoreWebView2.Navigate(new System.Uri(_exeLocation).AbsoluteUri + "cool/cool.html?file_path=" + _fileURL + "&closebutton=1&permission=edit&lang=en-US&appdocid=" + _appDocId + "&userinterfacemode=notebookbar&dir=ltr"); + + _iWebView2.CoreWebView2.NewWindowRequested += delegate ( + object webview2, CoreWebView2NewWindowRequestedEventArgs args) + { + ProcessStartInfo startInfo = new ProcessStartInfo + { + FileName = args.Uri, + // Open the URI in the default browser. + UseShellExecute = true + }; + Process.Start(startInfo); + args.Handled = true; + }; + } + + private async void PrintPdfDocument(string path) + { + try + { + var images = await PdfToImage(path); + int index = 0; + + PrintDocument printDocument = new PrintDocument(); + + printDocument.PrintPage += (sender, e) => + { + System.Drawing.Image image = images[index]; + + // Calculate the scaling factor to fit the image within the page size + float X = (float)e.PageSettings.PaperSize.Width / image.Width; + float Y = (float)e.PageSettings.PaperSize.Height / image.Height; + float scaleFactor = Math.Min(X, Y); + + // Draw the image on the page + e.Graphics.DrawImage(image, 0, 0, image.Width * scaleFactor, image.Height * scaleFactor); + + index++; + if (index < images.Count) + { + e.HasMorePages = true; + image.Dispose(); + return; + } + + e.HasMorePages = false; + image.Dispose(); + }; + + PrintDialog printDialog = new PrintDialog(); + printDialog.AllowCurrentPage = true; + printDialog.AllowSomePages = true; + printDialog.Document = printDocument; + if (printDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + printDocument.Print(); + } + } + catch (System.Exception ex) + { + System.Windows.MessageBox.Show("Error : " + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + private async Task> PdfToImage(string path) + { + var storagePdfFile = await Windows.Storage.StorageFile.GetFileFromPathAsync(path); + Windows.Data.Pdf.PdfDocument pdfDocument = await Windows.Data.Pdf.PdfDocument.LoadFromFileAsync(storagePdfFile); + + uint index = 0; + List images = new List(); + + while (index < pdfDocument.PageCount) + { + using (Windows.Data.Pdf.PdfPage pdfPage = pdfDocument.GetPage(index)) + { + using (Windows.Storage.Streams.InMemoryRandomAccessStream memStream = new Windows.Storage.Streams.InMemoryRandomAccessStream()) + { + // Windows.Data.Pdf.PdfPageRenderOptions pdfPageRenderOptions = new Windows.Data.Pdf.PdfPageRenderOptions(); + await pdfPage.RenderToStreamAsync(memStream); + System.Drawing.Image image = System.Drawing.Image.FromStream(memStream.AsStream()); + images.Add(image); + } + } + index++; + } + + return images; + } + + void WebView_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e) + { + if (e.IsSuccess) + _iWebView2.CoreWebView2.Navigate("about:blank"); + else + System.Windows.MessageBox.Show($"WebView2 creation failed with exception = {e.InitializationException}"); + } + + private bool _isMessageOfType(byte[] message, string type, int lengthOfMessage) + { + if (message.Length < type.Length + 2) + return false; + for (int i = 0; i < type.Length; i++) + if (message[i] != type[i]) + return false; + return true; + } + + void send2JS(IntPtr buffer, int length) + { + byte[] s = new byte[length]; + Marshal.Copy(buffer, s, 0, length); + bool binaryMessage = (_isMessageOfType(s, "tile:", length) || + _isMessageOfType(s, "tilecombine:", length) || + _isMessageOfType(s, "delta:", length) || + _isMessageOfType(s, "renderfont:", length) || + _isMessageOfType(s, "rendersearchlist:", length) || + _isMessageOfType(s, "windowpaint:", length)); + + string pretext = binaryMessage + ? "window.TheFakeWebSocket.onmessage({'data': window.atob('" + : "window.TheFakeWebSocket.onmessage({'data': window.b64d('"; + const string posttext = "')});"; + + if (!binaryMessage) + { + StringBuilder sb = new StringBuilder(s.Length*2); + + for (int i = 0; i < (s.Length > 100 ? 100 : s.Length); i++) + { + if (s[i] >= ' ' && s[i] < 127 && s[i] != '\\') + sb.Append((Char)s[i]); + else if (s[i] == '\\') + sb.Append("\\\\"); + else if (s[i] == '\n') + sb.Append("\\n"); + else + { + string hex = "0123456789abcdef"; + sb.Append("\\" + hex[s[i] >> 4] + hex[s[i] & 0x0F]); + } + } + string subs = sb.ToString(); + if (sb.Length > 100) + subs += "..."; + Debug.WriteLine($"Evaluating JavaScript: {subs}"); + } + + string js = pretext + System.Convert.ToBase64String(s) + posttext; + + if (binaryMessage) + { + string subjs = js.Substring(0, (js.Length > 100 ? 100 : js.Length)); + if (js.Length > 100) + subjs += "..."; + Debug.WriteLine($"Evaluating JavaScript: {subjs}"); + } + + System.Windows.Application.Current.Dispatcher.Invoke(new Action(() => { + _iWebView2.ExecuteScriptAsync(js); + })); + } + + void UpdateTitle() + { + this.Title = $"{GetDocumentTitle()}"; + } + + string GetDocumentTitle() + { + return _iWebView2?.CoreWebView2?.DocumentTitle ?? string.Empty; + } + + } +} diff --git a/windows/coda/CODA/Properties/launchSettings.json b/windows/coda/CODA/Properties/launchSettings.json new file mode 100644 index 0000000000000..ceb2f7cb014a0 --- /dev/null +++ b/windows/coda/CODA/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "CODA": { + "commandName": "Project", + "environmentVariables": { + "xSAL_LOG": "+INFO+WARN-INFO.sal.bootstrap-INFO.i18nlangtag-INFO.vcl.fonts-INFO.vcl.gdi.fontmetric-INFO.cppu-INFO.salhelper.thread-INFO.drawinglayer-INFO.vcl.schedule-WARN.xmloff-WARN.unotools.config-WARN.legacy.tools" + }, + "nativeDebugging": true, + "jsWebView2Debugging": false, + "hotReloadEnabled": false + } + } +} \ No newline at end of file diff --git a/windows/coda/CODALib/CODALib.vcxproj b/windows/coda/CODALib/CODALib.vcxproj new file mode 100644 index 0000000000000..0655c66274ef5 --- /dev/null +++ b/windows/coda/CODALib/CODALib.vcxproj @@ -0,0 +1,144 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {b0e65dec-1cb0-4daa-b3ed-709f8169bc40} + CODALib + 10.0 + + + + DynamicLibrary + true + ClangCL + Unicode + + + DynamicLibrary + false + ClangCL + true + Unicode + + + + + + + + + + + + + + + + + $(SolutionDir)CODA\bin\$(Configuration)\net8.0-windows10.0.19041.0\ + + + $(SolutionDir)CODA\bin\$(Configuration)\net8.0-windows10.0.19041.0\ + + + + Level3 + true + _DEBUG;_USRDLL;%(PreprocessorDefinitions);NOMINMAX;_CRT_SECURE_NO_WARNINGS;COOLWSD_LOGLEVEL="debug" + true + NotUsing + pch.h + stdcpp20 + stdc11 + ..;..\..\..;..\..\..\common;..\..\..\net;..\..\..\kit;..\..\..\wsd;%(AdditionalIncludeDirectories) + false + true + -Wno-vla-cxx-extension + + + Windows + true + false + libpng.lib;zlib.lib;libzstd_static.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_USRDLL;%(PreprocessorDefinitions);NOMINMAX;_CRT_SECURE_NO_WARNINGS;COOLWSD_LOGLEVEL="warning" + true + NotUsing + pch.h + stdcpp20 + stdc11 + ..;..\..\..;..\..\..\common;..\..\..\net;..\..\..\kit;..\..\..\wsd;%(AdditionalIncludeDirectories) + false + -Wno-vla-cxx-extension + + + Windows + true + true + true + false + libpng.lib;zlib.lib;libzstd_static.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/windows/coda/CODALib/CODALib.vcxproj.filters b/windows/coda/CODALib/CODALib.vcxproj.filters new file mode 100644 index 0000000000000..7b927841f12d5 --- /dev/null +++ b/windows/coda/CODALib/CODALib.vcxproj.filters @@ -0,0 +1,139 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + diff --git a/windows/coda/README.md b/windows/coda/README.md new file mode 100644 index 0000000000000..ec0c1af87d6e0 --- /dev/null +++ b/windows/coda/README.md @@ -0,0 +1,176 @@ +# Building CODA-W + +## Requirements + +For starters, the same requirements as for building LibreOffice, see +https://wiki.documentfoundation.org/Development/BuildingOnWindows as +you will do that as part of building CODA-W. Make sure to use Visual +Studio 2022. Using other Visual Studio versions for building the +online bits has not been tested. + +In Visual Studio 2022, also install the .NET desktop development +components. + +## Setup + +Turn on WSL, the Windows Subsystem for Linux, and install a distro. +I use the default, Ubuntu, others presumably work, too. In an +administrator Command Prompt: + + wsl --install Ubuntu + +In that Ubuntu, install various things that will be needed later. Most +of this is needed just to run the configure script in online. That +configure script checks for tons of things that are completely +irrelevant for CODA, but oh well. Patches welcome. + +This is not necessarily a comprehensive list, you might notice more +missing things as you go along. + + sudo apt install libtool python3-lxml python3-polib g++ pkg-config + sudo apt install libpng-dev libzstd-dev libcppunit-dev libpam-dev + +The following things are more essential, for bulding the JavaScript +stuff. This is the main reason we are using WSL. + + sudo apt install nodejs npm + +## Build LibreOffice + +Clone the git@gitlab.collabora.com:productivity/libreoffice/core.git +repo, the coda-25.04 branch. + +Using an autogen.input like this is known to work: + +``` +--with-distro=CODAWindows +--with-visual-studio=2022 +--enable-debug +--enable-msvc-debug-runtime +--enable-headless +--disable-ccache +--disable-opencl +--disable-pch +--without-doxygen +--without-lxml +--without-lang +``` + +The --with-distro, --with-visual-studio, and --enable-headless options +are essential. The other ones you can play with. Note that if you +build the CODA project in Visual Studio in the Debug configuration, +you *must* use a LibreOffice build with either --enable-dbgutil or +--enable-msvc-debug-runtime. + +The LibreOffice build should proceed fairly normally. Note that you +will not end up with a runnable normal desktop LibreOffice. Attempting +to run instdir/program/soffice.exe will just produce the message "no +suitable windowing system found, exiting". + +You can attempt to run "make check" but that will probably run into +some false positives. + +## Build direct dependencies of CODA-W + +Version numbers below are current at the time of writing this. Newer +versions will probably work, too. Except that online might not compile +against Poco 1.14, so use the newest 1.13.*. For zlib and libpng we +use the unpacked sources in the LibreOffice core build directory, and +the static libraries already built there. + +## zstd + +Download and unpack the zstd-1.5.7 tarball. Open the Visual Studio +solution zstd-1.5.7/build/VS2010/zstd.sln. Let Visual Studio retarget +the projects. Build the solution. + +The binary that you are interested in is +zstd-1.5.7/build/VS2010/bin/x64\_Debug/libzstd\_static.lib. + +## Poco + +Download and unpack the poco-poco-1.13.3-release.tar.gz tarball. + +Then build it. Only a subset of it is needed. In a Ubuntu shell +window, run: + + powershell.exe -ExecutionPolicy Bypass -File buildwin.ps1 -action build -config both -linkmode static\_md -platform x64 -components Foundation,Util,JSON,Net,XML + +Then move all the headers into one place: + + mkdir -p include/Poco + cp -a Foundation/include/Poco/* include/Poco + cp -a Util/include/Poco/* include/Poco + cp -a JSON/include/Poco/* include/Poco + cp -a Net/include/Poco/* include/Poco + cp -a XML/include/Poco/* include/Poco + +Then apply this patch to get the proper names for the automatically +imported static Poco libraries: + +``` +--- Foundation/include/Poco/Foundation.h 2024-10-15 13:18:14.020114300 +0300 ++++ include/Poco/Foundation.h 2025-04-15 17:19:54.163644800 +0300 +@@ -29,11 +29,23 @@ + // + // Ensure that POCO_DLL is default unless POCO_STATIC is defined + // ++ ++// TML: For some reason POCO_STATIC is not defined, even though I ++// build statically, and also the POCO_LIB_SUFFIX seems to go wrong ++// compared to what my build produces, so bypass this and just do what I want... ++// I build it like this: ++// powershell.exe -ExecutionPolicy Bypass -File buildwin.ps1 -action build -config both -linkmode static\_md -platform x64 -components Foundation,Util,JSON,Net,XML ++ ++#define POCO_STATIC 1 ++#define POCO_LIB_SUFFIX "mdd.lib" ++ ++#if 0 + #if defined(_WIN32) && defined(_DLL) + #if !defined(POCO_DLL) && !defined(POCO_STATIC) + #define POCO_DLL + #endif + #endif ++#endif + + + // +@@ -66,6 +78,7 @@ + // Automatically link Foundation library. + // + #if defined(_MSC_VER) ++ #if 0 // --tml + #if defined(POCO_DLL) + #if defined(_DEBUG) + #define POCO_LIB_SUFFIX "d.lib" +@@ -85,6 +98,7 @@ + #define POCO_LIB_SUFFIX "mt.lib" + #endif + #endif ++ #endif // --tml + + #if !defined(POCO_NO_AUTOMATIC_LIBS) && !defined(Foundation_EXPORTS) + #pragma comment(lib, "PocoFoundation" POCO_LIB_SUFFIX) +``` + +## Build CODA-W itself + +Clone the git@gitlab.collabora.com:productivity/libreoffice/online.git +repo, the coda-25.04 branch. + +In an Ubuntu shell, run + + ./autogen.sh + +then run the configure script: + + ./configure --enable-windowsapp --enable-debug --with-app-name=CODA --with-lo-builddir=/mnt/c/cygwin64/home/tml/lo/core-gitlab-coda25-coda-debug --with-lo-path=c:/cygwin64/home/tml/lo/core-gitlab-coda25-coda-debug/instdir --with-poco-includes=/mnt/c/Users/tml/poco-poco-1.13.3-release/include --with-poco-libs=/mnt/c/Users/tml/poco-poco-1.13.3-release/lib64 --with-zstd-includes=/mnt/c/Users/tml/zstd-1.5.7/lib --with-zstd-libs=/mnt/c/Users/tml/zstd-1.5.7/build/VS2010/bin/x64\_Debug --with-libpng-includes=/mnt/c/cygwin64/home/tml/lo/core-gitlab-coda25-coda-debug/workdir/UnpackedTarball/libpng --with-libpng-libs=/mnt/c/cygwin64/home/tml/lo/core-gitlab-coda25-coda-debug/workdir/LinkTarget/StaticLibrary --with-zlib-includes=/mnt/c/cygwin64/home/tml/lo/core-gitlab-coda25-coda-debug/workdir/UnpackedTarball/zlib + +Obviously, adapt as necessary to match your username and where you +built LibreOffice, zstd, and Poco. + +Now you can build the JavaScript bits: + + (cd browser && make) + +And then finally, open the windows/coda/CODA/CODA.sln solution in Visual Studio and build it. diff --git a/windows/coda/config.h.in b/windows/coda/config.h.in new file mode 100755 index 0000000000000..689662b16d4f0 --- /dev/null +++ b/windows/coda/config.h.in @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* config.h. Manually edited from config.h.in. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +#pragma once + +/* Variables that usually don't change once configured */ + +/* The user-visible name of the app you build. */ +#define APP_NAME "@APP_NAME@" + +/* Default value of feature_lock.calc_unlock_highlights */ +#define CALC_UNLOCK_HIGHLIGHTS "" + +/* Define to 1 if this is a code-coverage build. */ +#define CODE_COVERAGE 0 + +/* Enable permanent anonymization in logs */ +#define COOLWSD_ANONYMIZE_USER_DATA false + +/* Options passed to configure script */ +#undef COOLWSD_BUILDCONFIG + +/* Directory that contains configuration of coolwsd */ +#define COOLWSD_CONFIGDIR "." + +/* Default coolwsd loglevel */ +/* Set in CODALib.vcxproj differently for Release and Debug */ + +/* Destination for Trace Event output */ +#undef COOLWSD_TRACEEVENTFILE + +/* The user-name which is allowed to run coolwsd and its tools */ +#undef COOL_USER_ID + +/* Default number of pre-spawn children */ +#define NUM_PRESPAWN_CHILDREN "1" + +/* Absolute path to the top-level source directory */ +#undef DEBUG_ABSSRCDIR + +/* Whether to disable SECCOMP */ +#define DISABLE_SECCOMP 1 + +/* Default value of feature_lock.draw_unlock_highlights */ +#undef DRAW_UNLOCK_HIGHLIGHTS + +/* Whether to compile in some extra debugging support code and disable some + security pieces */ +#undef ENABLE_DEBUG + +/* Whether to compile and enable feature locking */ +#undef ENABLE_FEATURE_LOCK + +/* Whether to compile and enable feature restrictions */ +#undef ENABLE_FEATURE_RESTRICTION + +/* Whether to enable SSL */ +#define ENABLE_SSL 0 + +/* Whether to default to using SSL_VERIFY_PEER */ +#undef SSL_VERIFY + +/* Whether to enable support key */ +#define ENABLE_SUPPORT_KEY 0 + +/* Should the Release notes message be shown on upgrade */ +#undef ENABLE_WELCOME_MESSAGE + +/* User feedback URL. */ +#undef FEEDBACK_URL + +/* Define to 1 libcap (cap_get_proc) is available, otherwise 0 */ +#define HAVE_LIBCAP 0 + +/* Whether OpenSSL has PKCS5_PBKDF2_HMAC() */ +#define HAVE_PKCS5_PBKDF2_HMAC 0 + +/* Define to 1 if the `ppoll' function is available, otherwise 0. */ +#define HAVE_PPOLL 0 + +/* Define to 1 if you have the `memrchr' function. */ +#define HAVE_MEMRCHR 0 + +/* Define to 1 if you have the `pipe2' function. */ +#define HAVE_PIPE2 0 + +/* Default value of help root URL */ +#undef HELP_URL + +/* Default value of feature_lock.impress_unlock_highlights */ +#undef IMPRESS_UNLOCK_HIGHLIGHTS + +/* Infobar URL. */ +#undef INFOBAR_URL + +/* Define to 1 if this is a libfuzzer build. */ +#undef LIBFUZZER + +/* Default value of feature_lock.locked_commands */ +#undef LOCKED_COMMANDS + +/* Enable logging of test assertions */ +#undef LOK_LOG_ASSERTIONS + +/* Path to LibreOffice installation */ +#define LO_PATH "@LO_PATH@" + +/* Limit the maximum number of open connections */ +#define MAX_CONNECTIONS 100 + +/* Limit the maximum number of open documents */ +#define MAX_DOCUMENTS 100 + +/* Define to 1 if this is a mobileapp (eg. Android) build. */ +#define MOBILEAPP 1 + +/* Define to 1 when we should use systemplate and jails (eg. normal coolwsd server), otherwise 0 (eg. CODA). */ +#define ENABLE_CHILDROOTS 0 + +/* 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). */ +#define ENABLE_CODA 1 + +/* Default value of feature_lock.unlock_description */ +#define UNLOCK_DESCRIPTION "" + +/* Default value of feature_lock.unlock_link */ +#define UNLOCK_LINK "" + +/* Default value of feature_lock.unlock_title */ +#define UNLOCK_TITLE "" + +/* The welcome url of the build. */ +#define WELCOME_URL "" + +/* Default value of feature_lock.writer_unlock_highlights */ +#define WRITER_UNLOCK_HIGHLIGHTS "" + +/* Should we enable SIMD acceleration */ +#define ENABLE_SIMD 0 + +/* Define to 1 if this is the WASM app build. */ +#define WASMAPP 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. */ +#ifdef ENABLE_DEBUG +#define CONFIG_STATIC +#else +#define CONFIG_STATIC static +#endif + +/* Stuff manually added just for CODA for Windows */ + +typedef __int64 ssize_t; +typedef int pid_t; + +// MSVC's _putenv_s doesn't do removal of environment variables, but we don't need that anyway +#define setenv(variable, value, one) _putenv_s(variable, value) + +#define strcasecmp _stricmp + +#define POLLIN 0x001 +#define POLLPRI 0x002 +#define POLLOUT 0x004 +#define POLLERR 0x008 +#define POLLHUP 0x010 +#define POLLNVAL 0x020 + +struct pollfd { + int fd; + short events; + short revents; +}; + +// I give up, let's define these here +#define EX_OK 0 +#define EX_SOFTWARE 1 + +#define MOBILEAPP 1 + +#define EXTERNC extern "C" +#define EXPORT EXTERNC __declspec(dllexport) + +#define ENABLE_LOCAL_FILESYSTEM 1 diff --git a/windows/coda/config.props.in b/windows/coda/config.props.in new file mode 100644 index 0000000000000..9654100ed41bc --- /dev/null +++ b/windows/coda/config.props.in @@ -0,0 +1,7 @@ + + + @POCOINCLUDE_WINDOWS@;@ZSTDINCLUDE_WINDOWS@;@LIBPNG_INCLUDES_WINDOWS@;@ZLIB_INCLUDES_WINDOWS@;@LOBUILDDIR_WINDOWS@\include;$(IncludePath) + @POCOLIB_WINDOWS@;@ZSTDLIB_WINDOWS@;@LIBPNG_LIBS_WINDOWS@;@ZLIB_LIBS_WINDOWS@;$(LibraryPath) + @LO_PATH@ + + diff --git a/windows/coda/testheader.cpp b/windows/coda/testheader.cpp new file mode 100755 index 0000000000000..f9259081e8b18 --- /dev/null +++ b/windows/coda/testheader.cpp @@ -0,0 +1,9 @@ +// This is just a temporary hack to make it easier to check one header +// at a time whether it compiles for Windows without having to compile +// a substantial C++ file. + +#include + +#include // Header to check + +int foobar = 42; diff --git a/windows/coda/windows.cpp b/windows/coda/windows.cpp new file mode 100755 index 0000000000000..a07ee7ea3b532 --- /dev/null +++ b/windows/coda/windows.cpp @@ -0,0 +1,234 @@ +// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- +// +// 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 "windows.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include + +const char *user_name = nullptr; +int coolwsd_server_socket_fd = -1; +std::string app_installation_path; +std::string app_installation_uri; + +static COOLWSD *coolwsd = nullptr; +static int fakeClientFd; +static int closeNotificationPipeForForwardingThread[2]; + +typedef void (*send2JS_t)(char *buffer, long length); +typedef void(*replyWithString_t)(const char *s); + +static send2JS_t send2JSfunction; + +EXPORT +int get_coolwsd_server_socket_fd() +{ + return coolwsd_server_socket_fd; +} + +EXPORT +int set_coolwsd_server_socket_fd(int fd) +{ + coolwsd_server_socket_fd = fd; + return fd; +} + +EXPORT +void set_app_installation_path(const char* path) +{ + app_installation_path = path; +} + +EXPORT +void set_app_installation_uri(const char* uri) +{ + app_installation_uri = uri; +} + +EXPORT +int generate_new_app_doc_id() +{ + // Start with a random document id to catch code that might assume it to be some fixed value, + // like 0 or 1. Also make it obvious that this numeric "app doc id", used by the mobile apps and + // CODA, is not related to the string document ids (usually with several leading zeroes) used in + // the C++ bits of normal COOL. + static int appDocId = 42 + (std::time(nullptr) % 100); + + DocumentData::allocate(appDocId); + return appDocId++; +} + +EXPORT +void initialize_cpp_things() +{ + // FIXME: Code snippet shared with gtk/mobile.cpp, factor out into separate file. + + Log::initialize("Mobile", "trace"); + Util::setThreadName("main"); + + fakeSocketSetLoggingCallback([](const std::string& line) + { + LOG_TRC_NOFILE(line); + }); + + std::thread([] + { + assert(coolwsd == nullptr); + char *argv[2]; + // Yes, strdup() is apparently not standard, so MS wants you to call it as + // _strdup(), and warns if you call strdup(). Sure, we could just silence such + // warnings, but let's try to do as they want. + argv[0] = _strdup("mobile"); + argv[1] = nullptr; + Util::setThreadName("app"); + while (true) + { + coolwsd = new COOLWSD(); + coolwsd->run(1, argv); + delete coolwsd; + LOG_TRC("One run of COOLWSD completed"); + } + }).detach(); + + fakeClientFd = fakeSocketSocket(); +} + +EXPORT +void set_send2JS_function(send2JS_t f) +{ + send2JSfunction = f; +} + +EXPORT +void do_hullo_handling_things(const char *fileURL, int appDocId) +{ + // FIXME: Code snippet shared with gtk/mobile.cpp, factor out into separate file. + + // Now we know that the JS has started completely + + // Contact the permanently (during app lifetime) listening COOLWSD server "public" socket + assert(coolwsd_server_socket_fd != -1); + int rc = fakeSocketConnect(fakeClientFd, coolwsd_server_socket_fd); + assert(rc != -1); + + // Create a socket pair to notify the below thread when the document has been closed + fakeSocketPipe2(closeNotificationPipeForForwardingThread); + + // Start another thread to read responses and forward them to the JavaScript + std::thread([] + { + Util::setThreadName("app2js"); + while (true) + { + struct pollfd pollfd[2]; + pollfd[0].fd = fakeClientFd; + pollfd[0].events = POLLIN; + pollfd[1].fd = closeNotificationPipeForForwardingThread[1]; + pollfd[1].events = POLLIN; + if (fakeSocketPoll(pollfd, 2, -1) > 0) + { + if (pollfd[1].revents == POLLIN) + { + // The code below handling the "BYE" fake Websocket message has closed the other + // end of the closeNotificationPipeForForwardingThread. Let's close the other + // end too just for cleanliness, even if a FakeSocket as such is not a system + // resource so nothing is saved by closing it. + fakeSocketClose(closeNotificationPipeForForwardingThread[1]); + + // Close our end of the fake socket connection to the ClientSession thread, so + // that it terminates. + fakeSocketClose(fakeClientFd); + + return; + } + if (pollfd[0].revents == POLLIN) + { + int n = fakeSocketAvailableDataLength(fakeClientFd); + // I don't want to check for n being -1 here, even if that will lead to a crash, + // as n being -1 is a sign of something being wrong elsewhere anyway, and I + // prefer to fix the root cause. Let's see how well this works out. + if (n == 0) + return; + std::vector buf(n); + n = fakeSocketRead(fakeClientFd, buf.data(), n); + send2JSfunction(buf.data(), n); + } + } + else + { + break; + } + } + assert(false); + }).detach(); + + // First we simply send it the URL. This corresponds to the GET request with Upgrade to + // WebSocket. + LOG_TRC_NOFILE("Actually sending to Online:" << fileURL); + + // Must do this in a thread, too, so that we can return to the main loop + // Must duplicate fileURL as it exists only while this function is called from C#. + char *fileURLcopy = _strdup(fileURL); + std::thread([fileURLcopy, appDocId] + { + struct pollfd pollfd; + pollfd.fd = fakeClientFd; + pollfd.events = POLLOUT; + fakeSocketPoll(&pollfd, 1, -1); + std::string message(fileURLcopy + (" " + std::to_string(appDocId))); + fakeSocketWrite(fakeClientFd, message.c_str(), message.size()); + std::free(fileURLcopy); + }).detach(); +} + +EXPORT +void do_bye_handling_things() +{ + LOG_TRC_NOFILE("Document window terminating on JavaScript side. Closing our end of the socket."); + + // Close one end of the socket pair, that will wake up the forwarding thread above + fakeSocketClose(closeNotificationPipeForForwardingThread[0]); +} + +EXPORT +void do_convert_to(const char *type, int appDocId, replyWithString_t response) +{ + const std::string tempFile = FileUtil::createRandomTmpDir() + "/haha." + std::string(type); + const std::string tempFileUri = Poco::URI(Poco::Path(tempFile)).toString(); + + DocumentData::get(appDocId).loKitDocument->saveAs(tempFileUri.c_str(), type, nullptr); + + response(tempFileUri.c_str()); +} + +EXPORT +void do_other_message_handling_things(const char *message) +{ + LOG_TRC_NOFILE("Handling other message:'" << message << "'"); + + char *string_copy = _strdup(message); + // As above, must do this in a thread + std::thread([=] + { + struct pollfd pollfd; + pollfd.fd = fakeClientFd; + pollfd.events = POLLOUT; + fakeSocketPoll(&pollfd, 1, -1); + fakeSocketWrite(fakeClientFd, string_copy, strlen(string_copy)); + free(string_copy); + }).detach(); +} + +// vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/windows/coda/windows.hpp b/windows/coda/windows.hpp new file mode 100755 index 0000000000000..8b794b9b5b2c7 --- /dev/null +++ b/windows/coda/windows.hpp @@ -0,0 +1,17 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 + +extern int coolwsd_server_socket_fd; +extern const char *user_name; +extern std::string app_installation_path; +extern std::string app_installation_uri; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/COOLWSD.cpp b/wsd/COOLWSD.cpp index 0dc0bc8d7081d..5ee52807acdf9 100644 --- a/wsd/COOLWSD.cpp +++ b/wsd/COOLWSD.cpp @@ -33,10 +33,12 @@ // parent process that listens on the TCP port and accepts connections from COOL clients, and a // number of child processes, each which handles a viewing (editing) session for one document. +#ifndef _WIN32 #include #include #include #include +#endif #include @@ -115,7 +117,7 @@ int ClientPortNumber = 0; #if !MOBILEAPP /// UDS address for kits to connect to. -std::string MasterLocation; +UnxSocketPath MasterLocation; std::string COOLWSD::BuyProductUrl; std::string COOLWSD::LatestVersion; @@ -782,7 +784,12 @@ std::string COOLWSD::FileServerRoot; std::string COOLWSD::ServiceRoot; std::string COOLWSD::TmpFontDir; std::string COOLWSD::LOKitVersion; -std::string COOLWSD::ConfigFile = COOLWSD_CONFIGDIR "/coolwsd.xml"; +std::string COOLWSD::ConfigFile = +#if defined(MACOS) && ENABLE_CODA + getResourcePath("coolwsd", "xml"); +#else + COOLWSD_CONFIGDIR "/coolwsd.xml"; +#endif std::string COOLWSD::ConfigDir = COOLWSD_CONFIGDIR "/conf.d"; bool COOLWSD::EnableTraceEventLogging = false; bool COOLWSD::EnableAccessibility = false; @@ -1225,6 +1232,7 @@ void COOLWSD::setupChildRoot(const bool UseMountNamespaces) JailUtil::disableBindMounting(); // Default to assume failure JailUtil::disableMountNamespaces(); +#if ENABLE_CHILDROOTS Log::preFork(); pid_t pid = fork(); @@ -1293,6 +1301,9 @@ void COOLWSD::setupChildRoot(const bool UseMountNamespaces) JailUtil::enableBindMounting(); if (EnableMountNamespaces) JailUtil::enableMountNamespaces(); +#else + (void) UseMountNamespaces; +#endif } #endif @@ -1695,6 +1706,14 @@ void COOLWSD::innerInitialize(Poco::Util::Application& self) #endif // !MOBILEAPP +#if defined(DEBUG) + // Enable if you need more logging from core + //setenv("SAL_LOG", "+INFO+WARN", 0); + + // Enable if you need to see the top left corner of tile that was rendered + //setenv("LOK_DEBUG_TILES", "1", 0); +#endif + int pdfResolution = ConfigUtil::getConfigValue(conf, "per_document.pdf_resolution_dpi", 96); if (pdfResolution > 0) @@ -2067,7 +2086,7 @@ void COOLWSD::innerInitialize(Poco::Util::Application& self) COOLWSD::MaxDocuments = COOLWSD::MaxConnections; } -#if !WASMAPP +#if !WASMAPP && !defined(_WIN32) struct rlimit rlim; ::getrlimit(RLIMIT_NOFILE, &rlim); LOG_INF("Maximum file descriptor supported by the system: " << rlim.rlim_cur - 1); @@ -2736,7 +2755,7 @@ bool COOLWSD::createForKit() args.push_back("--lotemplate=" + LoTemplate); args.push_back("--childroot=" + ChildRoot); args.push_back("--clientport=" + std::to_string(ClientPortNumber)); - args.push_back("--masterport=" + MasterLocation); + args.push_back("--masterport=" + MasterLocation.getName()); const DocProcSettings& docProcSettings = Admin::instance().getDefDocProcSettings(); std::ostringstream ossRLimits; @@ -3324,7 +3343,7 @@ void COOLWSDServer::dumpState(std::ostream& os) const << "\n IsProxyPrefixEnabled: " << (COOLWSD::IsProxyPrefixEnabled ? "yes" : "no") << "\n OverrideWatermark: " << COOLWSD::OverrideWatermark << "\n UserInterface: " << COOLWSD::UserInterface - << "\n Total PSS: " << Util::getProcessTreePss(getpid()) << " KB" + << "\n Total PSS: " << Util::getProcessTreePss(Util::getProcessId()) << " KB" << "\n Config: " << LoggableConfigEntries ; THREAD_UNSAFE_DUMP_END @@ -3398,8 +3417,8 @@ std::shared_ptr COOLWSDServer::findPrisonerServerPort() auto socket = std::make_shared( std::chrono::steady_clock::now(), *PrisonerPoll, factory); - std::string location = socket->bind(); - if (!location.length()) + const UnxSocketPath location = socket->bind(); + if (!location.isValid()) { LOG_FTL("Failed to create local unix domain socket. Exiting."); Util::forcedExit(EX_SOFTWARE); @@ -3414,8 +3433,8 @@ std::shared_ptr COOLWSDServer::findPrisonerServerPort() LOG_INF("Listening to prisoner connections on " << location); MasterLocation = std::move(location); -#ifndef HAVE_ABSTRACT_UNIX_SOCKETS - if(!socket->link(COOLWSD::SysTemplate + "/0" + MasterLocation)) +#if ENABLE_CHILDROOTS + if(!socket->linkTo(COOLWSD::SysTemplate)) { LOG_FTL("Failed to hardlink local unix domain socket into a jail. Exiting."); Util::forcedExit(EX_SOFTWARE); @@ -4269,14 +4288,14 @@ static void forwardSignal(int signum); void dump_state() { std::ostringstream oss(Util::makeDumpStateStream()); - oss << "Start WSD " << getpid() << " Dump State:\n"; + oss << "Start WSD " << Util::getProcessId() << " Dump State:\n"; if (COOLWSDServer::Instance) COOLWSDServer::Instance->dumpState(oss); - oss << "\nMalloc info [" << getpid() << "]: \n\t" + oss << "\nMalloc info [" << Util::getProcessId() << "]: \n\t" << Util::replace(Util::getMallocInfo(), "\n", "\n\t") << '\n'; - oss << "\nEnd WSD " << getpid() << " Dump State.\n"; + oss << "\nEnd WSD " << Util::getProcessId() << " Dump State.\n"; const std::string msg = oss.str(); fprintf(stderr, "%s", msg.c_str()); // Log in the journal. @@ -4355,7 +4374,7 @@ void forwardSignal(const int signum) #endif // Avoid this in the Util::isFuzzing() case because libfuzzer defines its own main(). -#if !MOBILEAPP && !LIBFUZZER +#if !MOBILEAPP && !LIBFUZZER && !ENABLE_CODA int main(int argc, char** argv) { diff --git a/wsd/ClientRequestDispatcher.cpp b/wsd/ClientRequestDispatcher.cpp index ce0b1497dab05..b85e7d066cc76 100644 --- a/wsd/ClientRequestDispatcher.cpp +++ b/wsd/ClientRequestDispatcher.cpp @@ -861,36 +861,39 @@ void ClientRequestDispatcher::handleIncomingMessage(SocketDisposition& dispositi #else // !MOBILEAPP Poco::Net::HTTPRequest request; -#ifdef IOS // The URL of the document is sent over the FakeSocket by the code in // -[DocumentViewController userContentController:didReceiveScriptMessage:] when it gets the // HULLO message from the JavaScript in global.js. - // The "app document id", the numeric id of the document, from the appDocIdCounter in CODocument.mm. + // The "app document id", the numeric id of the document, from the appDocIdCounter + // It's currently relevant only for iOS, macOS, and Windows, so fallback if it is not found char* space = strchr(socket->getInBuffer().data(), ' '); - assert(space != nullptr); - - // The socket buffer is not nul-terminated so we can't just call strtoull() on the number at - // its end, it might be followed in memory by more digits. Is there really no better way to - // parse the number at the end of the buffer than to copy the bytes into a nul-terminated - // buffer? - const size_t appDocIdLen = + if (space != nullptr) + { + // The socket buffer is not nul-terminated so we can't just call strtoull() on the number at + // its end, it might be followed in memory by more digits. Is there really no better way to + // parse the number at the end of the buffer than to copy the bytes into a nul-terminated + // buffer? + const size_t appDocIdLen = (socket->getInBuffer().data() + socket->getInBuffer().size()) - (space + 1); - char* appDocIdBuffer = (char*)malloc(appDocIdLen + 1); - memcpy(appDocIdBuffer, space + 1, appDocIdLen); - appDocIdBuffer[appDocIdLen] = '\0'; - unsigned appDocId = std::strtoul(appDocIdBuffer, nullptr, 10); - free(appDocIdBuffer); - - handleClientWsUpgrade( - request, std::string(socket->getInBuffer().data(), space - socket->getInBuffer().data()), - disposition, socket, appDocId); -#else // IOS - handleClientWsUpgrade( - request, - RequestDetails(std::string(socket->getInBuffer().data(), socket->getInBuffer().size())), - disposition, socket); -#endif // !IOS + char* appDocIdBuffer = (char*)malloc(appDocIdLen + 1); + memcpy(appDocIdBuffer, space + 1, appDocIdLen); + appDocIdBuffer[appDocIdLen] = '\0'; + unsigned appDocId = static_cast(std::strtoul(appDocIdBuffer, nullptr, 10)); + free(appDocIdBuffer); + + handleClientWsUpgrade( + request, std::string(socket->getInBuffer().data(), space - socket->getInBuffer().data()), + disposition, socket, appDocId); + } + else + { + // no appDocId provided + handleClientWsUpgrade( + request, + RequestDetails(std::string(socket->getInBuffer().data(), socket->getInBuffer().size())), + disposition, socket); + } socket->getInBuffer().clear(); #endif // MOBILEAPP } diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp index 1cd185e0c459a..5865199469314 100644 --- a/wsd/ClientSession.cpp +++ b/wsd/ClientSession.cpp @@ -1689,9 +1689,17 @@ bool ClientSession::loadDocument(const char* /*buffer*/, int /*length*/, parseDocOptions(tokens, loadPart, timestamp); overrideDocOption(); + auto publicUri = docBroker->getPublicUri(); +#ifdef _WIN32 + // See comment in RequestDetails::sanitizeURI() + auto p = publicUri.getPath(); + if (p.length() > 3 && isalpha(p[0]) && p[1] == ':' && p[2] == '/') + publicUri.setPath("/" + p); +#endif + std::ostringstream oss; oss << std::boolalpha; - oss << "load url=" << docBroker->getPublicUri().toString(); + oss << "load url=" << publicUri.toString(); #if ENABLE_SSL // if ssl client verification was disabled in online for the wopi server, diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp index 700476c9b9176..104e3eaff8517 100644 --- a/wsd/DocumentBroker.cpp +++ b/wsd/DocumentBroker.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -61,7 +60,9 @@ #include #include #include +#ifndef _WIN32 #include +#endif #include using namespace std::literals; @@ -1317,14 +1318,14 @@ bool DocumentBroker::doDownloadDocument(const Authorization& auth, std::string localPathEncoded; Poco::URI::encode(localPath, "#?", localPathEncoded); - _uriJailed = Poco::URI(Poco::URI("file://"), localPathEncoded).toString(); + _uriJailed = Poco::URI(Poco::Path(localPath)).toString(); _uriJailedAnonym = - Poco::URI(Poco::URI("file://"), COOLWSD::anonymizeUrl(localPathEncoded)).toString(); + Poco::URI(Poco::Path(COOLWSD::anonymizeUrl(localPathEncoded))).toString(); for (const auto& it : additionalFileLocalPaths) { std::string additionalFileLocalPathEncoded; Poco::URI::encode(it.second, "#?", additionalFileLocalPathEncoded); - _additionalFileUrisJailed[it.first] = Poco::URI(Poco::URI("file://"), additionalFileLocalPathEncoded).toString(); + _additionalFileUrisJailed[it.first] = Poco::URI(Poco::Path(additionalFileLocalPathEncoded)).toString(); } _filename = filename; diff --git a/wsd/PlatformMobile.hpp b/wsd/PlatformMobile.hpp index c82e071d70067..4040b1c384222 100644 --- a/wsd/PlatformMobile.hpp +++ b/wsd/PlatformMobile.hpp @@ -14,6 +14,10 @@ #include #ifdef IOS #include "ios.h" +#elif defined(MACOS) +#include "macos.h" +#elif defined(_WIN32) +#include "windows.hpp" #elif defined(GTKAPP) #include "gtk.hpp" #elif defined(__ANDROID__) diff --git a/wsd/PlatformUnix.hpp b/wsd/PlatformUnix.hpp index 52a1ad46c1645..90625f628b4d8 100644 --- a/wsd/PlatformUnix.hpp +++ b/wsd/PlatformUnix.hpp @@ -9,6 +9,11 @@ */ #pragma once +// macOS can be both server and mobile, so let's include it here, too +#if defined(MACOS) +#include "macos.h" +#endif + #ifdef __linux__ #if !MOBILEAPP diff --git a/wsd/RequestDetails.cpp b/wsd/RequestDetails.cpp index ec2457c589853..9dc4811b88689 100644 --- a/wsd/RequestDetails.cpp +++ b/wsd/RequestDetails.cpp @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -274,13 +275,22 @@ void RequestDetails::processURI() Poco::URI RequestDetails::sanitizeURI(const std::string& uri) { - // The URI of the document should be url-encoded. + // The URI of the document is url-encoded, except that in a mobile app it isn't? Poco::URI uriPublic((Util::isMobileApp() ? uri : Uri::decode(uri))); if (uriPublic.isRelative() || uriPublic.getScheme() == "file") { // TODO: Validate and limit access to local paths! uriPublic.normalize(); +#ifdef _WIN32 + // Change a bogus path like /C:/Users/tml/foo.odt to C:/Users/tml/foo.odt. If this path then + // later is changed back into a file: URI, as in ClientSession::loadDocument(), we can't + // just prefix "file://" but need one more slash. So maybe it would in fact be simpler to + // just keep the seemingly bogus /C:/Users/tml/foo.odt? + std::string p = uriPublic.getPath(); + if (p.length() > 4 && p[0] == '/' && std::isalpha(p[1]) && p[2] == ':' && p[3] == '/') + uriPublic.setPath(p.substr(1)); +#endif } if (uriPublic.getPath().empty()) diff --git a/wsd/RequestVettingStation.cpp b/wsd/RequestVettingStation.cpp index ddd9984f06cbb..dc7b0e4528566 100644 --- a/wsd/RequestVettingStation.cpp +++ b/wsd/RequestVettingStation.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -29,6 +28,7 @@ #include #if !MOBILEAPP +#include #include #endif // !MOBILEAPP diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp index 574037811d821..f3a7aa00a5d64 100644 --- a/wsd/Storage.cpp +++ b/wsd/Storage.cpp @@ -48,13 +48,21 @@ #include #include +#if MOBILEAPP #ifdef IOS #include +#elif defined(MACOS) +#include #elif defined(__ANDROID__) #include "androidapp.hpp" +#elif defined(_WIN32) +#include "windows.hpp" #elif defined(GTKAPP) #include "gtk.hpp" -#endif // IOS +#elif WASMAPP +#include "wasmapp.hpp" +#endif +#endif // MOBILEAPP #if ENABLE_LOCAL_FILESYSTEM bool StorageBase::FilesystemEnabled;