Mobile/Mobile.xcodeproj/project.pbxproj | 101 ++++++++++------- Mobile/Mobile/AppDelegate.mm | 32 ++++- Mobile/Mobile/Document.h | 7 - Mobile/Mobile/Document.mm | 109 +++++++++--------- Mobile/Mobile/DocumentViewController.mm | 80 ++++++++++--- common/FileUtil.cpp | 2 common/Log.cpp | 20 +-- common/Session.cpp | 2 common/Unit.cpp | 4 common/Unit.hpp | 4 ios/config.h | 8 - ios/ios.h | 4 ios/ios.mm | 4 kit/ChildSession.cpp | 2 kit/ChildSession.hpp | 8 - kit/Kit.cpp | 37 +----- kit/Kit.hpp | 34 ----- net/ServerSocket.hpp | 14 ++ net/Socket.cpp | 102 +++++++++-------- net/Socket.hpp | 127 +++++++++++++++------ net/WebSocketHandler.hpp | 32 +++++ wsd/ClientSession.cpp | 10 - wsd/DocumentBroker.cpp | 24 +--- wsd/DocumentBroker.hpp | 24 ---- wsd/LOOLWSD.cpp | 185 ++++++++++++++++++++++++++------ wsd/LOOLWSD.hpp | 14 ++ wsd/Storage.cpp | 24 +++- wsd/TileCache.cpp | 2 28 files changed, 631 insertions(+), 385 deletions(-)
New commits: commit 95eb84921728dec5b6f6c97e62974120b6fd1e3d Author: Tor Lillqvist <[email protected]> AuthorDate: Thu Sep 13 19:16:00 2018 +0300 Commit: Tor Lillqvist <[email protected]> CommitDate: Wed Sep 19 11:31:18 2018 +0300 Still more iOS app and related Online C++ code hacking Re-think the plumbing between the different parts of the C++ Online code. Do try to have it work more like in real Online on all but the lowest socket level. Except that we don't have multiple processes, but threads inside the same process. And instead of using actual system sockets for WebSocket traffic between the threads, we use our own FakeSocket things, with no WebSocket framing of messages. Reduce the amount of #ifdef MOBILEAPP a bit also by compiling in the UnitFoo things. Hardcode that so that no unit testing is ever attempted, though. We don't try to dlopen any library. Corresponding changes in the app Objective-C code. Plus fixes and functionality improvements. Now it gets so far that the JavaScript code thinks it has the document tiles presented, and doesn't crash. But it hangs occasionally. And all tiles show up blank. Anyway, progress. Change-Id: I769497c9a46ddb74984bc7af36d132b7b43895d4 diff --git a/Mobile/Mobile.xcodeproj/project.pbxproj b/Mobile/Mobile.xcodeproj/project.pbxproj index 0d2062dff..da86e716c 100644 --- a/Mobile/Mobile.xcodeproj/project.pbxproj +++ b/Mobile/Mobile.xcodeproj/project.pbxproj @@ -57,6 +57,8 @@ BEA283582146945500848631 /* ChildSession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA283572146945500848631 /* ChildSession.cpp */; }; BEA2835A21470A1C00848631 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA2835921470A1C00848631 /* WebKit.framework */; }; BEA2835D21498AD400848631 /* Socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA2835C21498AD400848631 /* Socket.cpp */; }; + BEA28360214ACA8500848631 /* FakeSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA2835F214ACA8500848631 /* FakeSocket.cpp */; }; + BEA28377214FFD8C00848631 /* Unit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA28376214FFD8C00848631 /* Unit.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -84,6 +86,7 @@ BE5EB5D521401E0F00E0826C /* Storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Storage.cpp; sourceTree = "<group>"; }; BE5EB5D92140363100E0826C /* ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios.mm; path = ../../ios/ios.mm; sourceTree = "<group>"; }; BE5EB5DB2140480B00E0826C /* icudt62l.dat */ = {isa = PBXFileReference; lastKnownFileType = file; name = icudt62l.dat; path = "$(LOBUILDDIR)/workdir/CustomTarget/ios/resources/icudt62l.dat"; sourceTree = "<group>"; }; + BE636210215101D000F4237E /* WebSocketHandler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WebSocketHandler.hpp; sourceTree = "<group>"; }; BE8D77272136762500AC58EA /* Mobile.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Mobile.app; sourceTree = BUILT_PRODUCTS_DIR; }; BE8D772A2136762500AC58EA /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; BE8D772B2136762500AC58EA /* AppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate.mm; sourceTree = "<group>"; }; @@ -116,6 +119,10 @@ BEA283572146945500848631 /* ChildSession.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ChildSession.cpp; sourceTree = "<group>"; }; BEA2835921470A1C00848631 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; BEA2835C21498AD400848631 /* Socket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Socket.cpp; path = ../net/Socket.cpp; sourceTree = "<group>"; }; + BEA2835E214A8E2000848631 /* Socket.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Socket.hpp; sourceTree = "<group>"; }; + BEA2835F214ACA8500848631 /* FakeSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FakeSocket.cpp; sourceTree = "<group>"; }; + BEA28376214FFD8C00848631 /* Unit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Unit.cpp; sourceTree = "<group>"; }; + BEA283782150172600848631 /* Unit.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Unit.hpp; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -185,6 +192,8 @@ BE5EB5B6213FE21000E0826C /* common */ = { isa = PBXGroup; children = ( + BEA283782150172600848631 /* Unit.hpp */, + BEA28376214FFD8C00848631 /* Unit.cpp */, BE5EB5C0213FE29900E0826C /* FileUtil.cpp */, BE5EB5B9213FE29900E0826C /* Log.cpp */, BE5EB5BD213FE29900E0826C /* MessageQueue.cpp */, @@ -264,7 +273,10 @@ BEA2835B21498ABF00848631 /* net */ = { isa = PBXGroup; children = ( + BEA2835F214ACA8500848631 /* FakeSocket.cpp */, + BEA2835E214A8E2000848631 /* Socket.hpp */, BEA2835C21498AD400848631 /* Socket.cpp */, + BE636210215101D000F4237E /* WebSocketHandler.hpp */, ); name = net; path = ../net; @@ -371,10 +383,12 @@ BE5EB5C1213FE29900E0826C /* Log.cpp in Sources */, BE5EB5DA2140363100E0826C /* ios.mm in Sources */, BE5EB5D421400DC100E0826C /* DocumentBroker.cpp in Sources */, + BEA28377214FFD8C00848631 /* Unit.cpp in Sources */, BE5EB5CF213FE2D000E0826C /* ClientSession.cpp in Sources */, BE5EB5C2213FE29900E0826C /* SpookyV2.cpp in Sources */, BE8D77402136762600AC58EA /* main.m in Sources */, BE5EB5C8213FE29900E0826C /* FileUtil.cpp in Sources */, + BEA28360214ACA8500848631 /* FakeSocket.cpp in Sources */, BE8D77352136762500AC58EA /* Document.mm in Sources */, BE5EB5C7213FE29900E0826C /* Protocol.cpp in Sources */, BE8D772F2136762500AC58EA /* DocumentBrowserViewController.mm in Sources */, @@ -531,6 +545,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = J4FQ687VJK; ENABLE_BITCODE = NO; @@ -539,7 +554,9 @@ "IOS=IOS", "DISABLE_DYNLOADING=1", "DEBUG=1", + "LOOLWSD_CONFIGDIR='\".\"'", ); + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; HEADER_SEARCH_PATHS = ( "$(SOURCE_ROOT)/..", "$(SOURCE_ROOT)/../ios", @@ -554,6 +571,7 @@ "$(POCOINSTDIR)/include", ); INFOPLIST_FILE = Mobile/Info.plist; + LD_GENERATE_MAP_FILE = YES; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -570,11 +588,11 @@ "$(PNGINSTDIR)/lib", "-L", "$(POCOINSTDIR)/lib", - "-lPocoFoundation", - "-lPocoUtil", - "-lPocoXML", - "-lPocoJSON", - "-lPocoNet", + "-lPocoFoundationd", + "-lPocoUtild", + "-lPocoXMLd", + "-lPocoJSONd", + "-lPocoNetd", ); PRODUCT_BUNDLE_IDENTIFIER = com.collabora.office.Mobile; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -586,6 +604,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_DOCUMENTATION_COMMENTS = NO; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = J4FQ687VJK; ENABLE_BITCODE = NO; @@ -594,6 +613,7 @@ "IOS=IOS", "DISABLE_DYNLOADING=1", ); + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; HEADER_SEARCH_PATHS = ( "$(SOURCE_ROOT)/..", "$(SOURCE_ROOT)/../ios", @@ -608,6 +628,7 @@ "$(POCOINSTDIR)/include", ); INFOPLIST_FILE = Mobile/Info.plist; + LD_GENERATE_MAP_FILE = YES; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/Mobile/Mobile/AppDelegate.mm b/Mobile/Mobile/AppDelegate.mm index bb0fefe4e..6fbd25d75 100644 --- a/Mobile/Mobile/AppDelegate.mm +++ b/Mobile/Mobile/AppDelegate.mm @@ -8,14 +8,21 @@ #import "config.h" -//#define LOK_USE_UNSTABLE_API -//#import <LibreOfficeKit/LibreOfficeKitInit.h> +#import <cassert> +#import <cstring> #import "AppDelegate.h" #import "DocumentBrowserViewController.h" #import "DocumentViewController.h" #import "Document.h" +#import "FakeSocket.hpp" +#import "Log.hpp" +#import "LOOLWSD.hpp" +#import "Util.hpp" + +static LOOLWSD *loolwsd = nullptr; + @interface AppDelegate () @end @@ -25,8 +32,25 @@ } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { -// _kit = lok_init_2(nullptr, nullptr); - + Log::initialize("app", "trace", false, false, {}); + fakeSocketSetLoggingCallback([](const std::string& line) + { + LOG_TRC_NOFILE(line); + }); + + dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^{ + if (loolwsd == nullptr) + { + loolwsd = new LOOLWSD(); + Util::setThreadName("app"); + } + char *argv[2]; + argv[0] = strdup([[NSBundle mainBundle].executablePath UTF8String]); + argv[1] = nullptr; + loolwsd->run(1, argv); + assert(false && "????? LOOLWSD::main() returned?"); + }); return YES; } diff --git a/Mobile/Mobile/Document.h b/Mobile/Mobile/Document.h index 875ec2e80..f51cb3bac 100644 --- a/Mobile/Mobile/Document.h +++ b/Mobile/Mobile/Document.h @@ -6,18 +6,19 @@ // 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 <string> + #import <UIKit/UIKit.h> #define LOK_USE_UNSTABLE_API #import <LibreOfficeKit/LibreOfficeKit.h> -#import "Kit.hpp" - @class DocumentViewController; @interface Document : UIDocument { @public - JS2OnlineBridge bridge; + std::string uri; + int fakeClientFd; } @property (strong) DocumentViewController *viewController; diff --git a/Mobile/Mobile/Document.mm b/Mobile/Mobile/Document.mm index 14229777d..d6f0500b5 100644 --- a/Mobile/Mobile/Document.mm +++ b/Mobile/Mobile/Document.mm @@ -8,65 +8,39 @@ #import "config.h" +#import <algorithm> + +#import "ios.h" #import "AppDelegate.h" #import "Document.h" #import "DocumentViewController.h" +#import "ClientSession.hpp" +#import "DocumentBroker.hpp" +#import "FakeSocket.hpp" #import "Kit.hpp" #import "KitHelper.hpp" #import "Log.hpp" +#import "LOOLWSD.hpp" #import "Protocol.hpp" @implementation Document -// https://github.com/google/google-api-cpp-client/blob/master/src/googleapis/strings/memutil.cc, -// Apache licensed - -static size_t memspn(const char* s, size_t slen, const char* accept) { - const char* p = s; - const char* spanp; - char c, sc; - -cont: - c = *p++; - if (slen-- == 0) return p - 1 - s; - for (spanp = accept; (sc = *spanp++) != '\0';) - if (sc == c) goto cont; - return p - 1 - s; -} - -static const char* setupSafeNonBinaryCharSpan() -{ - static char result[256]; - - memset(result, 0, 256); - char *p = result; - for (char c = ' '; c <= '~'; c++) - if (c != '\'' && c != '\\') - *p++ = c; - return result; -} - -static void send2JS(const char *buffer, int length, void *data) -{ - Document *doc = (__bridge Document*) data; - [doc send2JS:buffer length:length]; -} - - (id)contentsForType:(NSString*)typeName error:(NSError **)errorPtr { // Encode your document with an instance of NSData or NSFileWrapper return [[NSData alloc] init]; } - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)errorPtr { - Log::initialize("", "trace", false, false, {}); + fakeClientFd = fakeSocketSocket(); + uri = [[[self fileURL] absoluteString] UTF8String]; - bridge.registerJSSender(send2JS, (__bridge void*) self); - - dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), - ^{ - lokit_main(std::string([[[self fileURL] absoluteString] UTF8String]), self->bridge); - }); + NSURL *url = [[NSBundle mainBundle] URLForResource:@"loleaflet" withExtension:@"html"]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; + components.queryItems = @[ [NSURLQueryItem queryItemWithName:@"file_path" value:[NSString stringWithUTF8String:uri.c_str()]], + [NSURLQueryItem queryItemWithName:@"debug" value:@"true"]]; + NSURLRequest *request = [[NSURLRequest alloc]initWithURL:components.URL]; + [self.viewController.webView loadRequest:request]; return YES; } @@ -74,38 +48,61 @@ static void send2JS(const char *buffer, int length, void *data) - (void)send2JS:(const char *)buffer length:(int)length { NSLog(@"send to JS: %s", LOOLProtocol::getAbbreviatedMessage(buffer, length).c_str()); - static const char * const safeChar = setupSafeNonBinaryCharSpan(); - NSString *js; - // Check if the message is non-binary and doesn't contain any single quotes or backslashes either. - // Yes, this doesn't accept non-ASCII UTF-8 sequences even if they would be perfectly fine. I - // think we don't send any such to this, though? + // Check if the message is binary. We say that any message that isn't just a single line is + // "binary" even if that strictly speaking isn't the case; for instance the commandvalues: + // message has a long bunch of non-binary JSON on multiple lines. But _onMessage() in Socket.js + // handles it fine even if such a message, too, comes in as an ArrayBuffer. (Look for the + // "textMsg = String.fromCharCode.apply(null, imgBytes);".) + + const char *newline = (const char *)memchr(buffer, '\n', length); + if (newline != nullptr) { + // The data needs to be an ArrayBuffer + js = @"window.TheFakeWebSocket.onmessage({'data': Base64ToArrayBuffer('"; + js = [js stringByAppendingString: [[NSData dataWithBytes:buffer length:length] base64EncodedStringWithOptions:0]]; + js = [js stringByAppendingString:@"')});"]; + NSString *subjs = [js substringToIndex:std::min(40ul, js.length)]; + + // NSLog(@"Evaluating JavaScript: %@ (length %lu)", subjs, (unsigned long)js.length); - if (memspn(buffer, length, safeChar) == length) { - js = @"window.TheFakeWebSocket.onmessage({'data': '"; - js = [js stringByAppendingString: [NSString stringWithCString:buffer encoding:NSASCIIStringEncoding]]; - js = [js stringByAppendingString:@"'});"]; dispatch_async(dispatch_get_main_queue(), ^{ [self.viewController.webView evaluateJavaScript:js completionHandler:^(id _Nullable obj, NSError * _Nullable error) { if (error) { - NSLog(@"name = %@ error = %@",@"", error.localizedDescription); + NSLog(@"error after %@ (length %lu): %@", subjs, (unsigned long)js.length, error.localizedDescription); } } ]; - }); + }); } else { - js = @"window.TheFakeWebSocket.onmessage({'data': atob('"; - js = [js stringByAppendingString: [[NSData dataWithBytes:buffer length:length] base64EncodedStringWithOptions:0]]; - js = [js stringByAppendingString:@"')});"]; + const unsigned char *ubufp = (const unsigned char *)buffer; + std::vector<char> data; + for (int i = 0; i < length; i++) { + if (ubufp[i] < ' ' || ubufp[i] == '\'' || ubufp[i] == '\\') { + data.push_back('\\'); + data.push_back('x'); + data.push_back("0123456789abcdef"[(ubufp[i] >> 4) & 0x0F]); + data.push_back("0123456789abcdef"[ubufp[i] & 0x0F]); + } else { + data.push_back(ubufp[i]); + } + } + data.push_back(0); + + js = @"window.TheFakeWebSocket.onmessage({'data': '"; + js = [js stringByAppendingString:[NSString stringWithUTF8String:data.data()]]; + js = [js stringByAppendingString:@"'});"]; + + // NSLog(@"Evaluating JavaScript: %@", js); + dispatch_async(dispatch_get_main_queue(), ^{ [self.viewController.webView evaluateJavaScript:js completionHandler:^(id _Nullable obj, NSError * _Nullable error) { if (error) { - NSLog(@"name = %@ error = %@",@"", error.localizedDescription); + NSLog(@"error after %@: %@: %@", js, error.localizedDescription, error.userInfo[@"WKJavaScriptExceptionMessage"]); } } ]; diff --git a/Mobile/Mobile/DocumentViewController.mm b/Mobile/Mobile/DocumentViewController.mm index 83b344850..c3da9f6d6 100644 --- a/Mobile/Mobile/DocumentViewController.mm +++ b/Mobile/Mobile/DocumentViewController.mm @@ -8,6 +8,14 @@ #import "config.h" +#import <string> +#import <vector> + +#import <poll.h> + +#import "ios.h" +#import "FakeSocket.hpp" + #import "DocumentViewController.h" @interface DocumentViewController() <WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler> { @@ -47,11 +55,6 @@ options:0 metrics:nil views:views]]; - - NSURL *loleafletURL = [[NSBundle mainBundle] URLForResource:@"loleaflet" withExtension:@"html"]; - NSURLRequest * myNSURLRequest = [[NSURLRequest alloc]initWithURL:loleafletURL]; - waitingForInitialLoad = YES; - [self.webView loadRequest:myNSURLRequest]; } - (void)viewWillAppear:(BOOL)animated { @@ -88,22 +91,6 @@ - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { NSLog(@"didFinishNavigation: %@", navigation); - - // Huh, this is horrible. - if (waitingForInitialLoad) { - waitingForInitialLoad = NO; - NSString *js; - - js = @"window.postMessage(JSON.stringify({'MessageId': 'Host_PostmessageReady'}), '*');"; - [webView evaluateJavaScript:js - completionHandler:^(id _Nullable obj, NSError * _Nullable error) - { - if (error) { - NSLog(@"name = %@ error = %@",@"", error.localizedDescription); - } - } - ]; - } } - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation { @@ -151,13 +138,62 @@ } - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { + int rc; + struct pollfd p; + if ([message.name isEqualToString:@"error"]) { NSLog(@"Error from WebView: %@", message.body); } else if ([message.name isEqualToString:@"debug"]) { NSLog(@"===== %@", message.body); } else if ([message.name isEqualToString:@"lool"]) { NSLog(@"===== To Online: %@", message.body); - self.document->bridge.sendToOnline([[@"child-0001 " stringByAppendingString:(NSString*)message.body] UTF8String]); + + if ([message.body isEqualToString:@"HULLO"]) { + // Now we know that the JS has started completely + + // Contact the permanently (during app lifetime) listening LOOLWSD server + // "public" socket + assert(loolwsd_server_socket_fd != -1); + rc = fakeSocketConnect(self.document->fakeClientFd, loolwsd_server_socket_fd); + assert(rc != -1); + + // Start another thread to read responses and forward them to the JavaScript + dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^{ + while (true) { + struct pollfd p; + p.fd = self.document->fakeClientFd; + p.events = POLLIN; + if (fakeSocketPoll(&p, 1, 0) == 1) { + int n = fakeSocketAvailableDataLength(self.document->fakeClientFd); + if (n == 0) + return; + std::vector<char> buf(n); + n = fakeSocketRead(self.document->fakeClientFd, buf.data(), n); + [self.document send2JS:buf.data() length:n]; + } + else + break; + } + assert(false); + }); + + // First we simply send it the URL. This corresponds to the GET request with Upgrade to + // WebSocket. + std::string url([[[self.document fileURL] absoluteString] UTF8String]); + p.fd = self.document->fakeClientFd; + p.events = POLLOUT; + fakeSocketPoll(&p, 1, 0); + fakeSocketWrite(self.document->fakeClientFd, url.c_str(), url.size()); + + return; + } + + const char *buf = [message.body UTF8String]; + p.fd = self.document->fakeClientFd; + p.events = POLLOUT; + fakeSocketPoll(&p, 1, 0); + fakeSocketWrite(self.document->fakeClientFd, buf, strlen(buf)); } else { NSLog(@"Unrecognized kind of message received from WebView: %@: %@", message.name, message.body); } diff --git a/common/FileUtil.cpp b/common/FileUtil.cpp index cae138ed8..0ba6256d0 100644 --- a/common/FileUtil.cpp +++ b/common/FileUtil.cpp @@ -190,6 +190,7 @@ namespace namespace FileUtil { +#ifndef MOBILEAPP void registerFileSystemForDiskSpaceChecks(const std::string& path) { const std::string::size_type lastSlash = path.rfind('/'); @@ -233,6 +234,7 @@ namespace FileUtil return std::string(); } +#endif bool checkDiskSpace(const std::string& path) { diff --git a/common/Log.cpp b/common/Log.cpp index 38e93c2cb..a8e654d70 100644 --- a/common/Log.cpp +++ b/common/Log.cpp @@ -111,7 +111,10 @@ namespace Log osTid = Util::getThreadId(); threadName = Util::getThreadName(); } - +#elif defined IOS + const char *threadName = Util::getThreadName(); + auto osTid = pthread_mach_thread_np(pthread_self()); +#endif Poco::DateTime time; snprintf(buffer, 1023, "%s-%.05lu %.4u-%.2u-%.2u %.2u:%.2u:%.2u.%.6u [ %s ] %s ", (Source.inited ? Source.id.c_str() : "<shutdown>"), @@ -120,16 +123,6 @@ namespace Log time.hour(), time.minute(), time.second(), time.millisecond() * 1000 + time.microsecond(), threadName, level); -#else - Poco::DateTime time; - const char *threadName = Util::getThreadName(); - snprintf(buffer, 1023, "%s %.4u-%.2u-%.2u %.2u:%.2u:%.2u.%.6u [ %s ] %s ", - (Source.inited ? Source.id.c_str() : "<shutdown>"), - time.year(), time.month(), time.day(), - time.hour(), time.minute(), time.second(), - time.millisecond() * 1000 + time.microsecond(), - threadName, level); -#endif return buffer; } @@ -148,8 +141,11 @@ namespace Log { Source.name = name; std::ostringstream oss; - oss << Source.name << '-' + oss << Source.name; +#ifndef MOBILEAPP // Just one process in a mobile app, the pid is uninteresting. + oss << '-' << std::setw(5) << std::setfill('0') << Poco::Process::id(); +#endif Source.id = oss.str(); // Configure the logger. diff --git a/common/Session.cpp b/common/Session.cpp index adbbbb857..4dd9d2902 100644 --- a/common/Session.cpp +++ b/common/Session.cpp @@ -187,7 +187,6 @@ void Session::handleMessage(bool /*fin*/, WSOpCode /*code*/, std::vector<char> & { try { -#ifndef MOBILEAPP std::unique_ptr< std::vector<char> > replace; if (UnitBase::get().filterSessionInput(this, &data[0], data.size(), replace)) { @@ -195,7 +194,6 @@ void Session::handleMessage(bool /*fin*/, WSOpCode /*code*/, std::vector<char> & _handleInput(replace->data(), replace->size()); return; } -#endif if (!data.empty()) _handleInput(&data[0], data.size()); } diff --git a/common/Unit.cpp b/common/Unit.cpp index 6391a5d9d..7eba4fc2a 100644 --- a/common/Unit.cpp +++ b/common/Unit.cpp @@ -30,6 +30,7 @@ static Poco::Thread TimeoutThread("unit timeout"); UnitBase *UnitBase::linkAndCreateUnit(UnitType type, const std::string &unitLibPath) { +#ifndef MOBILEAPP void *dlHandle = dlopen(unitLibPath.c_str(), RTLD_GLOBAL|RTLD_NOW); if (!dlHandle) { @@ -60,6 +61,9 @@ UnitBase *UnitBase::linkAndCreateUnit(UnitType type, const std::string &unitLibP hooks->setHandle(dlHandle); return hooks; +#else + return nullptr; +#endif } bool UnitBase::init(UnitType type, const std::string &unitLibPath) diff --git a/common/Unit.hpp b/common/Unit.hpp index eaf393611..5844cc3bd 100644 --- a/common/Unit.hpp +++ b/common/Unit.hpp @@ -9,8 +9,6 @@ #ifndef INCLUDED_UNIT_HPP #define INCLUDED_UNIT_HPP -#ifndef MOBILEAPP - #include <atomic> #include <cassert> #include <memory> @@ -293,6 +291,4 @@ public: #endif -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ios/config.h b/ios/config.h index b9090b03e..9f4751035 100644 --- a/ios/config.h +++ b/ios/config.h @@ -67,7 +67,7 @@ #define HAVE_UNISTD_H 1 /* Cache folder */ -#define LOOLWSD_CACHEDIR getCacheDir() +#define LOOLWSD_CACHEDIR lo_ios_app_getCacheDir() /* LibreOffice Online WebSocket server version */ #define LOOLWSD_VERSION "master" // ??? @@ -76,16 +76,16 @@ #define LOOLWSD_VERSION_HASH "xxxxxx" // ??? /* Path to LibreOffice installation */ -#undef LO_PATH +#define LO_PATH "." /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Limit the maximum number of open connections */ -#undef MAX_CONNECTIONS +#define MAX_CONNECTIONS 3 /* Limit the maximum number of open documents */ -#undef MAX_DOCUMENTS +#define MAX_DOCUMENTS 1 /* Name of package */ #undef PACKAGE diff --git a/ios/ios.h b/ios/ios.h index fb7a68dc7..2fe488c9e 100644 --- a/ios/ios.h +++ b/ios/ios.h @@ -7,6 +7,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -extern const char* getCacheDir(); +extern const char* lo_ios_app_getCacheDir(); + +extern int loolwsd_server_socket_fd; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ios/ios.mm b/ios/ios.mm index e608627c6..cc0c0f902 100644 --- a/ios/ios.mm +++ b/ios/ios.mm @@ -14,7 +14,9 @@ extern "C" { #import <native-code.h> } -const char* getCacheDir() +int loolwsd_server_socket_fd = -1; + +const char* lo_ios_app_getCacheDir() { static NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]; static const char* result = strdup([cachePath UTF8String]); diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp index a8380fc81..2cb733059 100644 --- a/kit/ChildSession.cpp +++ b/kit/ChildSession.cpp @@ -213,14 +213,12 @@ bool ChildSession::_handleInput(const char *buffer, int length) { assert(false && "Tile traffic should go through the DocumentBroker-LoKit WS."); } -#ifdef MOBILEAPP else if (tokens[0] == "requestloksession" || tokens[0] == "canceltiles") { // Just ignore these. // FIXME: We probably should do something for "canceltiles" at least? } -#endif else { // All other commands are such that they always require a LibreOfficeKitDocument session, diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp index 4071ee466..e426f2ede 100644 --- a/kit/ChildSession.hpp +++ b/kit/ChildSession.hpp @@ -158,24 +158,16 @@ public: bool sendTextFrame(const char* buffer, int length) override { -#ifndef MOBILEAPP const auto msg = "client-" + getId() + ' ' + std::string(buffer, length); const std::unique_lock<std::mutex> lock = getLock(); return _docManager.sendFrame(msg.data(), msg.size(), WSOpCode::Text); -#else - return _docManager.sendFrame(buffer, length, WSOpCode::Text); -#endif } bool sendBinaryFrame(const char* buffer, int length) override { -#ifndef MOBILEAPP const auto msg = "client-" + getId() + ' ' + std::string(buffer, length); const std::unique_lock<std::mutex> lock = getLock(); return _docManager.sendFrame(msg.data(), msg.size(), WSOpCode::Binary); -#else - return _docManager.sendFrame(buffer, length, WSOpCode::Text); -#endif } using Session::sendTextFrame; diff --git a/kit/Kit.cpp b/kit/Kit.cpp index 1161507c2..0e96f78ac 100644 --- a/kit/Kit.cpp +++ b/kit/Kit.cpp @@ -78,6 +78,10 @@ #include <wsd/LOOLWSD.hpp> #endif +#ifdef MOBILEAPP +#include "LOOLWSD.hpp" +#endif + #define LIB_SOFFICEAPP "lib" "sofficeapp" ".so" #define LIB_MERGED "lib" "mergedlo" ".so" @@ -104,10 +108,6 @@ using namespace LOOLProtocol; class Document; static std::shared_ptr<Document> document; -#ifdef MOBILEAPP -static JS2OnlineBridge *theBridge; -#endif - #if ENABLE_DEBUG # define ADD_DEBUG_RENDERID(s) ((s)+ " renderid=" + Util::UniqueId()) #else @@ -1145,17 +1145,11 @@ public: bool sendTextFrame(const std::string& message) { -#ifndef MOBILEAPP return sendFrame(message.data(), message.size()); -#else - theBridge->sendToJS(message.data(), message.size()); - return true; -#endif } bool sendFrame(const char* buffer, int length, WSOpCode opCode = WSOpCode::Text) override { -#ifndef MOBILEAPP try { std::shared_ptr<std::vector<char>> message = std::make_shared<std::vector<char>>(); @@ -1170,10 +1164,6 @@ public: (exc.nested() ? "( " + exc.nested()->displayText() + ")" : "")); } return false; -#else - theBridge->sendToJS(buffer, length); - return true; -#endif } static void GlobalCallback(const int type, const char* p, void* data) @@ -2152,7 +2142,7 @@ void lokit_main( bool displayVersion #else const std::string& documentUri, - JS2OnlineBridge& bridge + int docBrokerSocket #endif ) { @@ -2409,6 +2399,8 @@ void lokit_main( std::_Exit(Application::EXIT_SOFTWARE); } + LOOLWSD::LOKitVersion = loKit->getVersionInfo(); + // Dummies Poco::URI uri; const std::string jailId; @@ -2418,19 +2410,12 @@ void lokit_main( SocketPoll mainKit("kit"); -#ifdef MOBILEAPP - // Ugly, but not ay uglier than the singleton 'document' variable. - theBridge = &bridge; - theBridge->registerSocket(mainKit); - std::thread([&] { - theBridge->sendToOnline("session 0001 foo 0001"); - // No jailing in the mobile app, just use the same URI for the "jail" parameter to - // the "load" command, too. - theBridge->sendToOnline("child-0001 load url=" + documentUri + " jail=" + documentUri); - }).detach(); +#ifndef MOBILEAPP + mainKit.insertNewWebSocketSync(uri, std::make_shared<KitWebSocketHandler>("child_ws_" + pid, loKit, jailId, mainKit)); +#else + mainKit.insertNewWebSocketSync(docBrokerSocket, std::make_shared<KitWebSocketHandler>("child_ws_" + pid, loKit, jailId, mainKit)); #endif - mainKit.insertNewWebSocketSync(uri, std::make_shared<KitWebSocketHandler>("child_ws_" + pid, loKit, jailId, mainKit)); LOG_INF("New kit client websocket inserted."); while (!TerminationFlag) diff --git a/kit/Kit.hpp b/kit/Kit.hpp index 19543e733..3056ffc6a 100644 --- a/kit/Kit.hpp +++ b/kit/Kit.hpp @@ -16,38 +16,10 @@ #ifdef MOBILEAPP +#include "ClientSession.hpp" +#include "DocumentBroker.hpp" #include "Socket.hpp" -class JS2OnlineBridge -{ -public: - void registerSocket(SocketPoll& socketPoll) - { - _SocketPoll = &socketPoll; - } - - void registerJSSender(void (*function)(const char *, int, void *), void *data) - { - _JSCallbackFunction = function; - _JSCallbackData = data; - } - - void sendToOnline(const std::string& payload) - { - _SocketPoll->feed(payload); - } - - void sendToJS(const char *data, int length) - { - _JSCallbackFunction(data, length, _JSCallbackData); - } - -private: - SocketPoll *_SocketPoll; - void (*_JSCallbackFunction)(const char *, int, void *); - void *_JSCallbackData; -}; - #endif void lokit_main( @@ -63,7 +35,7 @@ void lokit_main( bool displayVersion #else const std::string& documentUri, - JS2OnlineBridge& bridge + int docBrokerSocket #endif ); diff --git a/net/ServerSocket.hpp b/net/ServerSocket.hpp index f0ad38ad3..314b56f2d 100644 --- a/net/ServerSocket.hpp +++ b/net/ServerSocket.hpp @@ -47,8 +47,11 @@ public: /// Returns true on success only. bool listen(const int backlog = 64) { +#ifndef MOBILEAPP const int rc = ::listen(getFD(), backlog); - +#else + const int rc = fakeSocketListen(getFD()); +#endif if (rc) LOG_SYS("Failed to listen"); return rc == 0; @@ -61,15 +64,20 @@ public: { // Accept a connection (if any) and set it to non-blocking. // There still need the client's address to filter request from POST(call from REST) here. +#ifndef MOBILEAPP struct sockaddr_in6 clientInfo; socklen_t addrlen = sizeof(clientInfo); const int rc = ::accept4(getFD(), (struct sockaddr *)&clientInfo, &addrlen, SOCK_NONBLOCK); +#else + const int rc = fakeSocketAccept4(getFD(), SOCK_NONBLOCK); +#endif LOG_DBG("Accepted socket #" << rc << ", creating socket object."); try { // Create a socket object using the factory. if (rc != -1) { +#ifndef MOBILEAPP char addrstr[INET6_ADDRSTRLEN]; const void *inAddr; @@ -89,6 +97,10 @@ public: _socket->_clientAddress = addrstr; LOG_DBG("Accepted socket has family " << clientInfo.sin6_family << " address " << _socket->_clientAddress); +#else + std::shared_ptr<Socket> _socket = _sockFactory->create(rc); + _socket->_clientAddress = "dummy"; +#endif return _socket; } return std::shared_ptr<Socket>(nullptr); diff --git a/net/Socket.cpp b/net/Socket.cpp index 5c133b33a..91006f069 100644 --- a/net/Socket.cpp +++ b/net/Socket.cpp @@ -24,8 +24,8 @@ #include <SigUtil.hpp> #include "Socket.hpp" -#ifndef MOBILEAPP #include "ServerSocket.hpp" +#ifndef MOBILEAPP #include "SslSocket.hpp" #endif #include "WebSocketHandler.hpp" @@ -34,12 +34,14 @@ int SocketPoll::DefaultPollTimeoutMs = 5000; std::atomic<bool> SocketPoll::InhibitThreadChecks(false); std::atomic<bool> Socket::InhibitThreadChecks(false); -#ifndef MOBILEAPP - int Socket::createSocket(Socket::Type type) { +#ifndef MOBILEAPP int domain = type == Type::IPv4 ? AF_INET : AF_INET6; return socket(domain, SOCK_STREAM | SOCK_NONBLOCK, 0); +#else + return fakeSocketSocket(); +#endif } // help with initialization order @@ -56,8 +58,6 @@ namespace { } } -#endif - SocketPoll::SocketPoll(const std::string& threadName) : _name(threadName), _stop(false), @@ -65,23 +65,24 @@ SocketPoll::SocketPoll(const std::string& threadName) _threadFinished(false), _owner(std::this_thread::get_id()) { -#ifndef MOBILEAPP // Create the wakeup fd. - if (::pipe2(_wakeup, O_CLOEXEC | O_NONBLOCK) == -1) + if ( +#ifndef MOBILEAPP + ::pipe2(_wakeup, O_CLOEXEC | O_NONBLOCK) == -1 +#else + fakeSocketPipe2(_wakeup) == -1 +#endif + ) { throw std::runtime_error("Failed to allocate pipe for SocketPoll [" + threadName + "] waking."); } std::lock_guard<std::mutex> lock(getPollWakeupsMutex()); getWakeupsArray().push_back(_wakeup[1]); -#else - _bufferEmpty = true; -#endif } SocketPoll::~SocketPoll() { -#ifndef MOBILEAPP joinThread(); { @@ -94,11 +95,15 @@ SocketPoll::~SocketPoll() getWakeupsArray().erase(it); } +#ifndef MOBILEAPP ::close(_wakeup[0]); ::close(_wakeup[1]); +#else + fakeSocketClose(_wakeup[0]); + fakeSocketClose(_wakeup[1]); +#endif _wakeup[0] = -1; _wakeup[1] = -1; -#endif } void SocketPoll::startThread() @@ -118,8 +123,6 @@ void SocketPoll::startThread() } } -#ifndef MOBILEAPP - void SocketPoll::joinThread() { addCallback([this](){ removeSockets(); }); @@ -136,23 +139,6 @@ void SocketPoll::joinThread() } } -#endif - -#ifdef MOBILEAPP - -void SocketPoll::feed(const std::string& payload) -{ - std::unique_lock<std::mutex> lock(_bufferMutex); - if (!_bufferEmpty) - _bufferCV.wait(lock, [&]{return _bufferEmpty;}); - _buffer = payload; - _bufferEmpty = false; - lock.unlock(); - _bufferCV.notify_one(); -} - -#endif - void SocketPoll::pollingThreadEntry() { try @@ -181,13 +167,17 @@ void SocketPoll::pollingThreadEntry() void SocketPoll::wakeupWorld() { -#ifndef MOBILEAPP for (const auto& fd : getWakeupsArray()) wakeup(fd); -#endif } -void SocketPoll::insertNewWebSocketSync(const Poco::URI &uri, const std::shared_ptr<SocketHandlerInterface>& websocketHandler) +void SocketPoll::insertNewWebSocketSync( +#ifndef MOBILEAPP + const Poco::URI &uri, +#else + int peerSocket, +#endif + const std::shared_ptr<SocketHandlerInterface>& websocketHandler) { #ifndef MOBILEAPP LOG_INF("Connecting to " << uri.getHost() << " : " << uri.getPort() << " : " << uri.getPath()); @@ -275,12 +265,33 @@ void SocketPoll::insertNewWebSocketSync(const Poco::URI &uri, const std::shared_ else LOG_ERR("Failed to lookup client websocket host '" << uri.getHost() << "' skipping"); #else - _socketHandler = websocketHandler; + LOG_INF("Connecting to " << peerSocket); + int fd = fakeSocketSocket(); + int res = fakeSocketConnect(fd, peerSocket); + if (fd < 0 || (res < 0 && errno != EINPROGRESS)) + { + LOG_ERR("Failed to connect to the 'wsd' socket"); + fakeSocketClose(fd); + } + else + { + std::shared_ptr<StreamSocket> socket; + socket = StreamSocket::create<StreamSocket>(fd, true, websocketHandler); + if (socket) + { + LOG_TRC("Sending 'hello' instead of HTTP GET for now"); + socket->send("hello"); + insertNewSocket(socket); + } + else + { + LOG_ERR("Failed to allocate socket for client websocket"); + fakeSocketClose(fd); + } + } #endif } -#ifndef MOBILEAPP - void ServerSocket::dumpState(std::ostream& os) { os << "\t" << getFD() << "\t<accept>\n"; @@ -293,14 +304,12 @@ void SocketDisposition::execute() if (_socketMove) { // Drop pretentions of ownership before _socketMove. - _socket->setThreadOwner(std::thread::id(0)); + _socket->setThreadOwner(std::thread::id()); _socketMove(_socket); } _socketMove = nullptr; } -#endif - const int WebSocketHandler::InitialPingDelayMs = 25; const int WebSocketHandler::PingFrequencyMs = 18 * 1000; @@ -340,7 +349,6 @@ void StreamSocket::send(Poco::Net::HTTPResponse& response) void SocketPoll::dumpState(std::ostream& os) { -#ifndef MOBILEAPP // FIXME: NOT thread-safe! _pollSockets is modified from the polling thread! os << " Poll [" << _pollSockets.size() << "] - wakeup r: " << _wakeup[0] << " w: " << _wakeup[1] << "\n"; @@ -349,14 +357,12 @@ void SocketPoll::dumpState(std::ostream& os) os << "\tfd\tevents\trsize\twsize\n"; for (auto &i : _pollSockets) i->dumpState(os); -#endif } -#ifndef MOBILEAPP - /// Returns true on success only. bool ServerSocket::bind(Type type, int port) { +#ifndef MOBILEAPP // Enable address reuse to avoid stalling after // recycling, when previous socket is TIME_WAIT. //TODO: Might be worth refactoring out. @@ -401,9 +407,12 @@ bool ServerSocket::bind(Type type, int port) LOG_SYS("Failed to bind to: " << (_type == Socket::Type::IPv4 ? "IPv4" : "IPv6") << " port: " << port); return rc == 0; +#else + return true; +#endif } -#endif +#ifndef MOBILEAPP bool StreamSocket::parseHeader(const char *clientName, Poco::MemoryInputStream &message, @@ -477,7 +486,6 @@ bool StreamSocket::parseHeader(const char *clientName, return true; } - namespace HttpHelper { void sendUncompressedFileContent(const std::shared_ptr<StreamSocket>& socket, @@ -585,4 +593,6 @@ namespace HttpHelper } } +#endif // !MOBILEAPP + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/net/Socket.hpp b/net/Socket.hpp index 3a081d526..7bb442b41 100644 --- a/net/Socket.hpp +++ b/net/Socket.hpp @@ -22,6 +22,7 @@ #include <cassert> #include <cerrno> #include <chrono> +#include <climits> #include <cstdlib> #include <cstring> #include <fstream> @@ -33,8 +34,10 @@ #include <thread> #include "Common.hpp" +#include "FakeSocket.hpp" #include "Log.hpp" #include "Util.hpp" +#include "Protocol.hpp" #include "SigUtil.hpp" namespace Poco @@ -114,7 +117,11 @@ public: LOG_TRC("#" << getFD() << " Socket dtor."); // Doesn't block on sockets; no error handling needed. - close(_fd); +#ifndef MOBILEAPP + ::close(_fd); +#else + fakeSocketClose(_fd); +#endif } /// Create socket of the given type. @@ -128,7 +135,11 @@ public: virtual void shutdown() { LOG_TRC("#" << _fd << ": socket shutdown RDWR."); +#ifndef MOBILEAPP ::shutdown(_fd, SHUT_RDWR); +#else + fakeSocketShutdown(_fd); +#endif } /// Prepare our poll record; adjust @timeoutMaxMs downwards @@ -145,11 +156,14 @@ public: /// manage latency issues around packet aggregation void setNoDelay() { +#ifndef MOBILEAPP const int val = 1; ::setsockopt(_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &val, sizeof(val)); +#endif } +#ifndef MOBILEAPP /// Sets the kernel socket send buffer in size bytes. /// Note: TCP will allocate twice this size for admin purposes, /// so a subsequent call to getSendBufferSize will return @@ -190,13 +204,19 @@ public: const int rc = ::getsockopt(_fd, SOL_SOCKET, SO_SNDBUF, &size, &len); return rc == 0 ? size : -1; } +#endif /// Gets our fast cache of the socket buffer size int getSendBufferSize() const { +#ifndef MOBILEAPP return _sendBufferSize; +#else + return INT_MAX; // We want to always send a single record in one go +#endif } +#ifndef MOBILEAPP /// Sets the receive buffer size in bytes. /// Note: TCP will allocate twice this size for admin purposes, /// so a subsequent call to getReceieveBufferSize will return @@ -237,6 +257,7 @@ public: return rc; } +#endif virtual void dumpState(std::ostream&) {} @@ -288,6 +309,7 @@ protected: _owner = std::this_thread::get_id(); LOG_DBG("#" << _fd << " Thread affinity set to " << Log::to_string(_owner) << "."); +#ifndef MOBILEAPP #if ENABLE_DEBUG if (std::getenv("LOOL_ZERO_BUFFER_SIZE")) { @@ -297,6 +319,7 @@ protected: " (was " << oldSize << ")"); } #endif +#endif } private: @@ -428,7 +451,6 @@ public: /// Poll the sockets for available data to read or buffer to write. void poll(int timeoutMaxMs) { -#ifndef MOBILEAPP assertCorrectThread(); std::chrono::steady_clock::time_point now = @@ -441,7 +463,12 @@ public: int rc; do { +#ifndef MOBILEAPP rc = ::poll(&_pollFds[0], size + 1, std::max(timeoutMaxMs,0)); +#else + LOG_TRC("SocketPoll Poll"); + rc = fakeSocketPoll(&_pollFds[0], size + 1, std::max(timeoutMaxMs,0)); +#endif } while (rc < 0 && errno == EINTR); LOG_TRC("Poll completed with " << rc << " live polls max (" << @@ -455,8 +482,12 @@ public: std::lock_guard<std::mutex> lock(_mutex); // Clear the data. +#ifndef MOBILEAPP int dump = ::read(_wakeup[0], &dump, sizeof(dump)); - +#else + LOG_TRC("Wakeup pipe read"); + int dump = fakeSocketRead(_wakeup[0], &dump, sizeof(dump)); +#endif // Copy the new sockets over and clear. _pollSockets.insert(_pollSockets.end(), _newSockets.begin(), _newSockets.end()); @@ -527,42 +558,48 @@ public: disposition.execute(); } -#else - std::unique_lock<std::mutex> lock(_bufferMutex); - if (_bufferEmpty) - _bufferCV.wait(lock, [&]{return !_bufferEmpty;}); - _socketHandler->handleMessage(_buffer); - _bufferEmpty = true; - lock.unlock(); - _bufferCV.notify_one(); -#endif } -#ifndef MOBILEAPP /// Write to a wakeup descriptor static void wakeup (int fd) { // wakeup the main-loop. int rc; do { +#ifndef MOBILEAPP rc = ::write(fd, "w", 1); +#else +#if 0 + // Our fake sockets are record-oriented with a single record buffer, so as we write one + // byte at a time to the wakeup pipe, we can't write another one before the previous one + // has been read. + // + // FIXME: Still, explicitly waiting here with fakeSocketPoll() for it to be writable + // doesn't seem to be any improvement. On the contrary, with this fakeSocketPoll(), we + // hang every time we run the app. Without it, we only hang occasionally. + + LOG_TRC("Wakeup pipe write"); + struct pollfd p; + p.fd = fd; + p.events = POLLOUT; + fakeSocketPoll(&p, 1, 0); +#endif + rc = fakeSocketWrite(fd, "w", 1); +#endif } while (rc == -1 && errno == EINTR); if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) LOG_SYS("wakeup socket #" << fd << " is closed at wakeup?"); } -#endif /// Wakeup the main polling loop in another thread void wakeup() { -#ifndef MOBILEAPP if (!isAlive()) LOG_WRN("Waking up dead poll thread [" << _name << "], started: " << _threadStarted << ", finished: " << _threadFinished); wakeup(_wakeup[1]); -#endif } /// Global wakeup - signal safe: wakeup all socket polls. @@ -585,7 +622,13 @@ public: /// Inserts a new websocket to be polled. /// NOTE: The DNS lookup is synchronous. - void insertNewWebSocketSync(const Poco::URI &uri, const std::shared_ptr<SocketHandlerInterface>& websocketHandler); + void insertNewWebSocketSync( +#ifndef MOBILEAPP + const Poco::URI &uri, +#else + int peerSocket, +#endif + const std::shared_ptr<SocketHandlerInterface>& websocketHandler); typedef std::function<void()> CallbackFn; @@ -629,15 +672,7 @@ public: /// Stop and join the polling thread before returning (if active) void joinThread(); -#ifdef MOBILEAPP - // In the mobile app(s), a SocketPoll doesn't actually "poll" any "sockets" but simply acts as a - // conduit for sending messages that correspond to what we would receive as WebSocket messages - // in the server online from the app code into the shared online code. - void feed(const std::string& payload); -#endif - private: -#ifndef MOBILEAPP /// Initialize the poll fds array with the right events void setupPollFds(std::chrono::steady_clock::time_point now, int &timeoutMaxMs) @@ -667,7 +702,6 @@ private: _pollFds[size].events = POLLIN; _pollFds[size].revents = 0; } -#endif /// The polling thread entry. /// Used to set the thread name and mark the thread as stopped when done. @@ -677,10 +711,8 @@ private: /// Debug name used for logging. const std::string _name; -#ifndef MOBILEAPP /// main-loop wakeup pipe int _wakeup[2]; -#endif /// The sockets we're controlling std::vector<std::shared_ptr<Socket>> _pollSockets; /// Protects _newSockets @@ -698,14 +730,6 @@ protected: std::atomic<bool> _threadStarted; std::atomic<bool> _threadFinished; std::thread::id _owner; - -#ifdef MOBILEAPP - std::mutex _bufferMutex; - std::condition_variable _bufferCV; - bool _bufferEmpty; - std::string _buffer; - std::shared_ptr<SocketHandlerInterface> _socketHandler; -#endif }; /// A plain, non-blocking, data streaming socket. @@ -803,6 +827,7 @@ public: { assertCorrectThread(); +#ifndef MOBILEAPP // SSL decodes blocks of 16Kb, so for efficiency we use the same. char buf[16 * 1024]; ssize_t len; @@ -825,6 +850,22 @@ public: // else poll will handle errors. } while (len == (sizeof(buf))); +#else + LOG_TRC("readIncomingData #" << getFD()); + ssize_t available = fakeSocketAvailableDataLength(getFD()); + ssize_t len; + if (available == -1) + len = -1; + else + { + std::vector<char>buf(available); + len = readData(buf.data(), available); + assert(len == available); + _bytesRecvd += len; + assert(_inBuffer.size() == 0); + _inBuffer.insert(_inBuffer.end(), buf.data(), buf.data() + len); + } +#endif return len != 0; // zero is eof / clean socket close. } @@ -899,7 +940,7 @@ protected: if (log.trace()) { LOG_TRC("#" << getFD() << ": Incoming data buffer " << _inBuffer.size() << " bytes, closeSocket? " << closed); - // log.dump("", &_inBuffer[0], _inBuffer.size()); + log.dump("", &_inBuffer[0], _inBuffer.size()); } // If we have data, allow the app to consume. @@ -991,14 +1032,26 @@ protected: virtual int readData(char* buf, int len) { assertCorrectThread(); +#ifndef MOBILEAPP return ::read(getFD(), buf, len); +#else + return fakeSocketRead(getFD(), buf, len); +#endif } /// Override to handle writing data to socket differently. virtual int writeData(const char* buf, const int len) { assertCorrectThread(); +#ifndef MOBILEAPP return ::write(getFD(), buf, len); +#else + struct pollfd p; + p.fd = getFD(); + p.events = POLLOUT; + fakeSocketPoll(&p, 1, 0); + return fakeSocketWrite(getFD(), buf, len); +#endif } void dumpState(std::ostream& os) override; diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp index 104c9b879..f9abbae42 100644 --- a/net/WebSocketHandler.hpp +++ b/net/WebSocketHandler.hpp @@ -112,6 +112,7 @@ public: static_cast<unsigned>(statusCode) << ", message: " << statusMessage); _shuttingDown = true; +#ifndef MOBILEAPP const size_t len = statusMessage.size(); std::vector<char> buf(2 + len); buf[0] = ((((int)statusCode) >> 8) & 0xff); @@ -121,6 +122,7 @@ public: | static_cast<char>(WSOpCode::Close); sendFrame(socket, buf.data(), buf.size(), flags); +#endif } bool handleOneIncomingMessage(const std::shared_ptr<StreamSocket>& socket) @@ -306,13 +308,24 @@ public: /// Implementation of the SocketHandlerInterface. virtual void handleIncomingMessage(SocketDisposition&) override { + // LOG_TRC("***** WebSocketHandler::handleIncomingMessage()"); + std::shared_ptr<StreamSocket> socket = _socket.lock(); + +#ifdef MOBILEAPP + // No separate "upgrade" is going on + if (socket != nullptr && !socket->isWebSocket()) + socket->setWebSocket(); +#endif + if (socket == nullptr) { LOG_ERR("No socket associated with WebSocketHandler " << this); } +#ifndef MOBILEAPP else if (_isClient && !socket->isWebSocket()) handleClientUpgrade(); +#endif else { while (handleOneIncomingMessage(socket)) @@ -332,6 +345,7 @@ public: return POLLIN; } +#ifndef MOBILEAPP /// Send a ping message void sendPingOrPong(std::chrono::steady_clock::time_point now, const char* data, const size_t len, @@ -369,10 +383,12 @@ public: assert(_isClient); sendPingOrPong(now, data, len, WSOpCode::Pong, socket); } +#endif /// Do we need to handle a timeout ? void checkTimeout(std::chrono::steady_clock::time_point now) override { +#ifndef MOBILEAPP if (_isClient) return; @@ -384,6 +400,7 @@ public: if (socket) sendPing(now, socket); } +#endif } /// By default rely on the socket buffer. @@ -400,11 +417,9 @@ public: /// 0 for closed/invalid socket, and -1 for other errors. int sendMessage(const char* data, const size_t len, const WSOpCode code, const bool flush = true) const { -#ifndef MOBILEAPP int unitReturn = -1; if (UnitBase::get().filterSendMessage(data, len, code, flush, unitReturn)) return unitReturn; -#endif //TODO: Support fragmented messages. @@ -431,6 +446,8 @@ private: std::vector<char>& out = socket->_outBuffer; const size_t oldSize = out.size(); +#ifndef MOBILEAPP + out.push_back(flags); int maskFlag = _isMasking ? 0x80 : 0; @@ -479,7 +496,14 @@ private: out.insert(out.end(), data, data + len); } const size_t size = out.size() - oldSize; +#else + LOG_TRC("WebSocketHandle::sendFrame: Writing to #" << socket->getFD() << " " << len << " bytes"); + assert(flush); + assert(out.size() == 0); + out.insert(out.end(), data, data + len); + const size_t size = out.size(); +#endif if (flush) socket->writeOutgoingData(); @@ -517,6 +541,7 @@ protected: LOG_TRC("#" << socket->getFD() << ": Upgrading to WebSocket."); assert(!socket->isWebSocket()); +#ifndef MOBILEAPP // create our websocket goodness ... const int wsVersion = std::stoi(req.get("Sec-WebSocket-Version", "13")); const std::string wsKey = req.get("Sec-WebSocket-Key", ""); @@ -540,9 +565,11 @@ protected: const std::string res = oss.str(); LOG_TRC("#" << socket->getFD() << ": Sending WS Upgrade response: " << res); socket->send(res); +#endif setWebSocket(); } +#ifndef MOBILEAPP // Handle incoming upgrade to full socket as client WS. void handleClientUpgrade() { @@ -607,6 +634,7 @@ protected: setWebSocket(); socket->eraseFirstInputBytes(responseSize); } +#endif void setWebSocket() { diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp index c5b7bc1ac..ad623b25d 100644 --- a/wsd/ClientSession.cpp +++ b/wsd/ClientSession.cpp @@ -66,11 +66,11 @@ ClientSession::~ClientSession() void ClientSession::handleIncomingMessage(SocketDisposition &disposition) { -#ifndef MOBILEAPP + // LOG_TRC("***** ClientSession::handleIncomingMessage()"); if (UnitWSD::get().filterHandleRequest( UnitWSD::TestRequest::Client, disposition, *this)) return; -#endif + Session::handleIncomingMessage(disposition); } @@ -95,7 +95,6 @@ bool ClientSession::_handleInput(const char *buffer, int length) updateLastActivityTime(); docBroker->updateLastActivityTime(); } -#ifndef MOBILEAPP if (tokens[0] == "loolclient") { if (tokens.size() < 1) @@ -125,8 +124,7 @@ bool ClientSession::_handleInput(const char *buffer, int length) return true; } -#endif - if (tokens[0] == "load") + else if (tokens[0] == "load") { if (_docURL != "") { @@ -761,6 +759,7 @@ bool ClientSession::handleKitToClientMessage(const char* buffer, const int lengt return false; } } +#ifndef MOBILEAPP else if (tokens.size() == 3 && tokens[0] == "saveas:") { bool isConvertTo = static_cast<bool>(_saveAsSocket); @@ -857,6 +856,7 @@ bool ClientSession::handleKitToClientMessage(const char* buffer, const int lengt return true; } +#endif else if (tokens.size() == 2 && tokens[0] == "statechanged:") { StringTokenizer stateTokens(tokens[1], "=", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp index b29616ec6..79d410407 100644 --- a/wsd/DocumentBroker.cpp +++ b/wsd/DocumentBroker.cpp @@ -194,9 +194,8 @@ void DocumentBroker::pollThread() _threadStart = std::chrono::steady_clock::now(); -#ifndef MOBILEAPP - // Request a kit process for this doc. +#ifndef MOBILEAPP do { static const int timeoutMs = COMMAND_TIMEOUT_MS * 5; @@ -205,11 +204,13 @@ void DocumentBroker::pollThread() std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - _threadStart).count() > timeoutMs) break; - // Nominal time between retries, lest we busy-loop. getNewChild could also wait, so don't double that here. std::this_thread::sleep_for(std::chrono::milliseconds(CHILD_REBALANCE_INTERVAL_MS / 10)); } while (!_stop && _poll->continuePolling() && !TerminationFlag && !ShutdownRequestFlag); +#else + _childProcess = getNewChild_Blocks(getPublicUri().getPath()); +#endif if (!_childProcess) { @@ -236,7 +237,6 @@ void DocumentBroker::pollThread() LOG_INF("Finished docBroker polling thread for docKey [" << _docKey << "]."); return; } -#endif _childProcess->setDocumentBroker(shared_from_this()); LOG_INF("Doc [" << _docKey << "] attached to child [" << _childProcess->getPid() << "]."); @@ -258,9 +258,9 @@ void DocumentBroker::pollThread() { const auto now = std::chrono::steady_clock::now(); -#ifndef MOBILEAPP _poll->poll(SocketPoll::DefaultPollTimeoutMs); +#ifndef MOBILEAPP if (std::chrono::duration_cast<std::chrono::milliseconds> (now - lastBWUpdateTime).count() >= 5 * 1000) { @@ -391,10 +391,8 @@ DocumentBroker::~DocumentBroker() LOG_INF("~DocumentBroker [" << _docKey << "] destroyed with " << _sessions.size() << " sessions left."); -#ifndef MOBILEAPP // Do this early - to avoid operating on _childProcess from two threads. _poll->joinThread(); -#endif if (!_sessions.empty()) { @@ -408,9 +406,7 @@ DocumentBroker::~DocumentBroker() void DocumentBroker::joinThread() { -#ifndef MOBILEAPP _poll->joinThread(); -#endif } void DocumentBroker::stop(const std::string& reason) @@ -429,13 +425,11 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s LOG_INF("Loading [" << _docKey << "] for session [" << sessionId << "] and jail [" << jailId << "]."); -#ifndef MOBILEAPP { bool result; if (UnitWSD::get().filterLoad(sessionId, jailId, result)) return result; } -#endif if (_markToDestroy) { @@ -996,11 +990,7 @@ bool DocumentBroker::sendUnoSave(const std::string& sessionId, bool dontTerminat oss << "}"; assert(_storage); - _storage->setIsAutosave(isAutosave -#ifndef MOBILEAPP - || UnitWSD::get().isAutosave() -#endif - ); + _storage->setIsAutosave(isAutosave || UnitWSD::get().isAutosave()); const std::string saveArgs = oss.str(); LOG_TRC(".uno:Save arguments: " << saveArgs); @@ -1190,10 +1180,8 @@ void DocumentBroker::alertAllUsers(const std::string& msg) { assertCorrectThread(); -#ifndef MOBILEAPP if (UnitWSD::get().filterAlertAllusers(msg)) return; -#endif auto payload = std::make_shared<Message>(msg, Message::Dir::Out); diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index c52cbf243..4993acd66 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -57,7 +57,6 @@ public: class ChildProcess { public: -#ifndef MOBILEAPP /// @param pid is the process ID of the child. /// @param socket is the underlying Sockeet to the child. ChildProcess(const Poco::Process::PID pid, @@ -72,15 +71,6 @@ public: { LOG_INF("ChildProcess ctor [" << _pid << "]."); } -#else - ChildProcess(const std::shared_ptr<WebSocketHandler>& ws) : - _pid(0), - _jailId(""), - _ws(ws) - { - LOG_INF("ChildProcess ctor."); - } -#endif ChildProcess(ChildProcess&& other) = delete; @@ -89,7 +79,6 @@ public: ~ChildProcess() { -#ifndef MOBILEAPP if (_pid <= 0) return; @@ -99,7 +88,6 @@ public: // No need for the socket anymore. _ws.reset(); _socket.reset(); -#endif } void setDocumentBroker(const std::shared_ptr<DocumentBroker>& docBroker); @@ -108,7 +96,6 @@ public: /// Let the child close a nice way. void close() { -#ifndef MOBILEAPP if (_pid < 0) return; @@ -133,16 +120,15 @@ public: } _pid = -1; -#endif } /// Kill or abandon the child. void terminate() { -#ifndef MOBILEAPP if (_pid < 0) return; +#ifndef MOBILEAPP if (::kill(_pid, 0) == 0) { LOG_INF("Killing child [" << _pid << "]."); @@ -151,9 +137,11 @@ public: LOG_ERR("Cannot terminate lokit [" << _pid << "]. Abandoning."); } } - - _pid = -1; +#else + // What to do? Throw some unique exception that the outermost call in the thread catches and + // exits from the thread? #endif + _pid = -1; } Poco::Process::PID getPid() const { return _pid; } @@ -206,9 +194,7 @@ private: Poco::Process::PID _pid; const std::string _jailId; std::shared_ptr<WebSocketHandler> _ws; -#ifndef MOBILEAP std::shared_ptr<Socket> _socket; -#endif std::weak_ptr<DocumentBroker> _docBroker; }; diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index 8f83234da..8ef290914 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -62,8 +62,6 @@ #include <Poco/Net/PartHandler.h> #include <Poco/Net/SocketAddress.h> -#include <ServerSocket.hpp> - using Poco::Net::HTMLForm; using Poco::Net::PartHandler; @@ -111,7 +109,7 @@ using Poco::Net::PartHandler; #include "FileServer.hpp" #include <FileUtil.hpp> #include <IoUtil.hpp> -#ifdef KIT_IN_PROCESS +#if defined KIT_IN_PROCESS || defined MOBILEAPP # include <Kit.hpp> #endif #include <Log.hpp> @@ -133,6 +131,12 @@ using Poco::Net::PartHandler; #include <common/SigUtil.hpp> +#include <ServerSocket.hpp> + +#ifdef IOS +#include "ios.h" +#endif + using namespace LOOLProtocol; using Poco::DirectoryIterator; @@ -199,10 +203,6 @@ extern "C" { void dump_state(void); /* easy for gdb */ } static int careerSpanMs = 0; #endif -#ifdef IOS -#include "ios.h" -#endif - bool LOOLWSD::NoCapsForKit = false; bool LOOLWSD::TileCachePersistent = true; std::atomic<unsigned> LOOLWSD::NumConnections; @@ -210,16 +210,9 @@ std::string LOOLWSD::Cache = LOOLWSD_CACHEDIR; std::set<std::string> LOOLWSD::EditFileExtensions; #ifdef MOBILEAPP - -// Some dummy versions of static ember functions - -void LOOLWSD::dumpIncomingTrace(const std::string& id, const std::string& sessionId, const std::string& data) -{ -} - -#else - -// Most of this file is probably not used in a mobile app. Let's see. +// Or can this be retreieved in some other way? +int LOOLWSD::prisonerServerSocketFD; +#endif namespace { @@ -247,6 +240,8 @@ inline void shutdownLimitReached(WebSocketHandler& ws) inline void checkSessionLimitsAndWarnClients() { +#ifndef MOBILEAPP + if (DocBrokers.size() > LOOLWSD::MaxDocuments || LOOLWSD::NumConnections >= LOOLWSD::MaxConnections) { const std::string info = Poco::format(PAYLOAD_INFO_LIMIT_REACHED, LOOLWSD::MaxDocuments, LOOLWSD::MaxConnections); @@ -261,12 +256,15 @@ inline void checkSessionLimitsAndWarnClients() LOG_ERR("Error while shuting down socket on reaching limit: " << ex.what()); } } +#endif } /// Internal implementation to alert all clients /// connected to any document. void alertAllUsersInternal(const std::string& msg) { +#ifndef MOBILEAPP + std::lock_guard<std::mutex> docBrokersLock(DocBrokersMutex); LOG_INF("Alerting all users: [" << msg << "]"); @@ -279,10 +277,12 @@ void alertAllUsersInternal(const std::string& msg) std::shared_ptr<DocumentBroker> docBroker = brokerIt.second; docBroker->addCallback([msg, docBroker](){ docBroker->alertAllUsers(msg); }); } +#endif } static void checkDiskSpaceAndWarnClients(const bool cacheLastCheck) { +#ifndef MOBILEAPP try { const std::string fs = FileUtil::checkDiskSpaceOnRegisteredFileSystems(cacheLastCheck); @@ -296,6 +296,7 @@ static void checkDiskSpaceAndWarnClients(const bool cacheLastCheck) { LOG_WRN("Exception while checking disk-space and warning clients: " << exc.what()); } +#endif } } @@ -339,6 +340,8 @@ void cleanupDocBrokers() } } +#ifndef MOBILEAPP + /// Forks as many children as requested. /// Returns the number of children requested to spawn, /// -1 for error. @@ -437,6 +440,8 @@ static bool prespawnChildren() return lock.try_lock() && (rebalanceChildren(LOOLWSD::NumPreSpawnedChildren) > 0); } +#endif + static size_t addNewChild(const std::shared_ptr<ChildProcess>& child) { std::unique_lock<std::mutex> lock(NewChildrenMutex); @@ -446,22 +451,29 @@ static size_t addNewChild(const std::shared_ptr<ChildProcess>& child) if (OutstandingForks < 0) ++OutstandingForks; + LOG_TRC("Adding one child to NewChildren"); NewChildren.emplace_back(child); const size_t count = NewChildren.size(); LOG_INF("Have " << count << " spare " << (count == 1 ? "child" : "children") << " after adding [" << child->getPid() << "]."); lock.unlock(); + LOG_TRC("Notifying NewChildrenCV"); NewChildrenCV.notify_one(); return count; } -std::shared_ptr<ChildProcess> getNewChild_Blocks() +std::shared_ptr<ChildProcess> getNewChild_Blocks( +#ifdef MOBILEAPP + const std::string& uri +#endif + ) { std::unique_lock<std::mutex> lock(NewChildrenMutex); const auto startTime = std::chrono::steady_clock::now(); +#ifndef MOBILEAPP LOG_DBG("getNewChild: Rebalancing children."); int numPreSpawn = LOOLWSD::NumPreSpawnedChildren; ++numPreSpawn; // Replace the one we'll dispatch just now. @@ -474,17 +486,33 @@ std::shared_ptr<ChildProcess> getNewChild_Blocks() // Let the caller retry after a while. return nullptr; } - // With valgrind we need extended time to spawn kits. const size_t timeoutMs = CHILD_TIMEOUT_MS / 2; LOG_TRC("Waiting for a new child for a max of " << timeoutMs << " ms."); const auto timeout = std::chrono::milliseconds(timeoutMs); +#else + const auto timeout = std::chrono::hours(100); + + std::thread([&] + { + // Ugly to have that static global, otoh we know there is just one LOOLWSD + // object. (Even in real Online.) I have no idea what I am doing here. + lokit_main(uri, LOOLWSD::prisonerServerSocketFD); + }).detach(); +#endif + // FIXME: blocks ... // Unfortunately we need to wait after spawning children to avoid bombing the system. // If we fail fast and return, the next document will spawn more children without knowing // there are some on the way already. And if the system is slow already, that wouldn't help. - if (NewChildrenCV.wait_for(lock, timeout, []() { return !NewChildren.empty(); })) - { + LOG_TRC("Waiting for NewChildrenCV"); + if (NewChildrenCV.wait_for(lock, timeout, []() + { + LOG_TRC("Predicate for NewChildrenCV wait: NewChildren.size()=" << NewChildren.size()); + return !NewChildren.empty(); + })) + { + LOG_TRC("NewChildrenCV wait successful"); std::shared_ptr<ChildProcess> child = NewChildren.back(); NewChildren.pop_back(); const size_t available = NewChildren.size(); @@ -504,6 +532,7 @@ std::shared_ptr<ChildProcess> getNewChild_Blocks() } else { + LOG_TRC("NewChildrenCV wait failed"); LOG_WRN("getNewChild: No child available. Sending spawn request to forkit and failing."); } @@ -511,6 +540,8 @@ std::shared_ptr<ChildProcess> getNewChild_Blocks() return nullptr; } +#ifndef MOBILEAPP + /// Handles the filename part of the convert-to POST request payload. class ConvertToPartHandler : public PartHandler { @@ -605,6 +636,8 @@ inline std::string getAdminURI(const Poco::Util::LayeredConfiguration &config) } // anonymous namespace +#endif // MOBILEAPP + std::atomic<unsigned> LOOLWSD::NextSessionId; #ifndef KIT_IN_PROCESS std::atomic<int> LOOLWSD::ForKitWritePipe(-1); @@ -678,10 +711,12 @@ LOOLWSD::~LOOLWSD() void LOOLWSD::initialize(Application& self) { +#ifndef MOBILEAPP if (geteuid() == 0) { throw std::runtime_error("Do not run as root. Please run as lool user."); } +#endif if (!UnitWSD::init(UnitWSD::UnitType::Wsd, UnitTestLibrary)) { @@ -761,6 +796,8 @@ void LOOLWSD::initialize(Application& self) AutoPtr<AppConfigMap> defConfig(new AppConfigMap(DefAppConfig)); conf.addWriteable(defConfig, PRIO_SYSTEM); // Lowest priority +#ifndef MOBILEAPP + // Load default configuration files, if present. if (loadConfiguration(PRIO_DEFAULT) == 0) { @@ -831,6 +868,7 @@ void LOOLWSD::initialize(Application& self) } } + // Log at trace level until we complete the initialization. Log::initialize("wsd", "trace", withColor, logToFile, logProperties); if (LogLevel != "trace") @@ -879,6 +917,8 @@ void LOOLWSD::initialize(Application& self) std::string allowedLanguages(config().getString("allowed_languages")); setenv("LOK_WHITELIST_LANGUAGES", allowedLanguages.c_str(), 1); +#endif + Cache = getPathFromConfig("tile_cache_path"); SysTemplate = getPathFromConfig("sys_template_path"); LoTemplate = getPathFromConfig("lo_template_path"); @@ -894,12 +934,14 @@ void LOOLWSD::initialize(Application& self) } LOG_INF("NumPreSpawnedChildren set to " << NumPreSpawnedChildren << "."); +#ifndef MOBILEAPP const auto maxConcurrency = getConfigValue<int>(conf, "per_document.max_concurrency", 4); if (maxConcurrency > 0) { setenv("MAX_CONCURRENCY", std::to_string(maxConcurrency).c_str(), 1); } LOG_INF("MAX_CONCURRENCY set to " << maxConcurrency << "."); +#endif // Otherwise we profile the soft-device at jail creation time. setenv("SAL_DISABLE_OPENCL", "true", 1); @@ -995,10 +1037,13 @@ void LOOLWSD::initialize(Application& self) TraceDumper.reset(new TraceFileWriter(path, recordOutgoing, compress, takeSnapshot, filters)); } +#ifndef MOBILEAPP FileServerRequestHandler::initialize(); +#endif StorageBase::initialize(); +#ifndef MOBILEAPP ServerApplication::initialize(self); DocProcSettings docProcSettings; @@ -1007,6 +1052,7 @@ void LOOLWSD::initialize(Application& self) docProcSettings.LimitFileSizeMb = getConfigValue<int>("per_document.limit_file_size_mb", 0); docProcSettings.LimitNumberOpenFiles = getConfigValue<int>("per_document.limit_num_open_files", 0); Admin::instance().setDefDocProcSettings(docProcSettings, false); +#endif #if ENABLE_DEBUG std::cerr << "\nLaunch one of these in your browser:\n\n" @@ -1024,6 +1070,7 @@ void LOOLWSD::initialize(Application& self) void LOOLWSD::initializeSSL() { +#if ENABLE_SSL if (!LOOLWSD::isSSLEnabled()) return; @@ -1039,7 +1086,6 @@ void LOOLWSD::initializeSSL() const std::string ssl_cipher_list = config().getString("ssl.cipher_list", ""); LOG_INF("SSL Cipher list: " << ssl_cipher_list); -#if ENABLE_SSL // Initialize the non-blocking socket SSL. SslContext::initialize(ssl_cert_file_path, ssl_key_file_path, @@ -1104,6 +1150,7 @@ void LOOLWSD::dumpOutgoingTrace(const std::string& id, const std::string& sessio void LOOLWSD::defineOptions(OptionSet& optionSet) { +#ifndef MOBILEAPP ServerApplication::defineOptions(optionSet); optionSet.addOption(Option("help", "", "Display help information on command line arguments.") @@ -1161,11 +1208,13 @@ void LOOLWSD::defineOptions(OptionSet& optionSet) .repeatable(false) .argument("trace_file_name")); #endif +#endif } void LOOLWSD::handleOption(const std::string& optionName, const std::string& value) { +#ifndef MOBILEAPP ServerApplication::handleOption(optionName, value); if (optionName == "help") @@ -1215,8 +1264,11 @@ void LOOLWSD::handleOption(const std::string& optionName, else if (optionName == "fuzz") FuzzFileName = value; #endif +#endif } +#ifndef MOBILEAPP + void LOOLWSD::displayHelp() { HelpFormatter helpFormatter(options()); @@ -1312,6 +1364,8 @@ bool LOOLWSD::checkAndRestoreForKit() #endif } +#endif + void LOOLWSD::doHousekeeping() { PrisonerPoll.wakeup(); @@ -1346,6 +1400,7 @@ void LOOLWSD::autoSave(const std::string& docKey) /// Really do the house-keeping void PrisonerPoll::wakeupHook() { +#ifndef MOBILEAPP if (!LOOLWSD::checkAndRestoreForKit()) { // No children have died. @@ -1375,15 +1430,17 @@ void PrisonerPoll::wakeupHook() #endif } } - +#endif std::unique_lock<std::mutex> docBrokersLock(DocBrokersMutex, std::defer_lock); if (docBrokersLock.try_lock()) cleanupDocBrokers(); } +#ifndef MOBILEAPP + bool LOOLWSD::createForKit() { -#ifdef KIT_IN_PROCESS +#if defined KIT_IN_PROCESS return true; #else LOG_INF("Creating new forkit process."); @@ -1474,6 +1531,8 @@ bool LOOLWSD::createForKit() #endif } +#endif + #ifdef FUZZER std::mutex Connection::Mutex; #endif @@ -1639,6 +1698,8 @@ private: /// Called after successful socket reads. void handleIncomingMessage(SocketDisposition &disposition) override { + // LOG_TRC("***** PrisonerRequestDispatcher::handleIncomingMessage()"); + if (UnitWSD::get().filterHandleRequest( UnitWSD::TestRequest::Prisoner, disposition, *this)) return; @@ -1659,6 +1720,7 @@ private: try { +#ifndef MOBILEAPP if (!socket->parseHeader("Prisoner", message, request, &requestSize)) return; @@ -1707,6 +1769,12 @@ private: LOG_INF("New child [" << pid << "], jailId: " << jailId << "."); UnitWSD::get().newChild(*this); +#else + Poco::Process::PID pid = 1; + std::string jailId = "jail"; + socket->_inBuffer.clear(); +#endif + LOG_TRC("Calling make_shared<ChildProcess>, for NewChildren?"); auto child = std::make_shared<ChildProcess>(pid, jailId, socket, request); @@ -1716,6 +1784,7 @@ private: // until we attach the childProcess (with this socket) // to a docBroker, which will do the polling. disposition.setMove([child](const std::shared_ptr<Socket> &){ + LOG_TRC("Calling addNewChild in disposition's move thing to add to NewChildren"); addNewChild(child); }); } @@ -1812,8 +1881,10 @@ private: /// Called after successful socket reads. void handleIncomingMessage(SocketDisposition &disposition) override { + // LOG_TRC("***** ClientRequestDispatcher::handleIncomingMessage()"); std::shared_ptr<StreamSocket> socket = _socket.lock(); +#ifndef MOBILEAPP Poco::MemoryInputStream message(&socket->_inBuffer[0], socket->_inBuffer.size());; Poco::Net::HTTPRequest request; @@ -1927,6 +1998,11 @@ private: // if we succeeded - remove the request from our input buffer // we expect one request per socket socket->eraseFirstInputBytes(requestSize); +#else + Poco::Net::HTTPRequest request; + handleClientWsUpgrade(request, std::string(socket->_inBuffer.data(), socket->_inBuffer.size()), disposition); + socket->_inBuffer.clear(); +#endif } int getPollEvents(std::chrono::steady_clock::time_point /* now */, @@ -1939,6 +2015,7 @@ private: { } +#ifndef MOBILEAPP void handleFileServerRequest(const Poco::Net::HTTPRequest& request, Poco::MemoryInputStream& message) { std::shared_ptr<StreamSocket> socket = _socket.lock(); @@ -2305,6 +2382,7 @@ private: throw BadRequestException("Invalid or unknown request."); } +#endif void handleClientWsUpgrade(const Poco::Net::HTTPRequest& request, const std::string& url, SocketDisposition &disposition) @@ -2373,7 +2451,7 @@ private: docBroker->startThread(); // We no longer own this socket. - moveSocket->setThreadOwner(std::thread::id(0)); + moveSocket->setThreadOwner(std::thread::id()); docBroker->addCallback([docBroker, moveSocket, clientSession]() { @@ -2504,10 +2582,10 @@ class PlainSocketFactory final : public SocketFactory std::shared_ptr<Socket> create(const int physicalFd) override { int fd = physicalFd; - +#ifndef MOBILEAPP if (SimulatedLatencyMs > 0) fd = Delay::create(SimulatedLatencyMs, physicalFd); - +#endif std::shared_ptr<Socket> socket = StreamSocket::create<StreamSocket>( fd, false, std::make_shared<ClientRequestDispatcher>()); @@ -2574,9 +2652,18 @@ public: void start(const int port) { _acceptPoll.startThread(); - _acceptPoll.insertNewSocket(findServerPort(port)); + std::shared_ptr<ServerSocket> serverSocket(findServerPort(port)); + _acceptPoll.insertNewSocket(serverSocket); + +#ifdef MOBILEAPP + loolwsd_server_socket_fd = serverSocket->getFD(); +#endif + WebServerPoll.startThread(); + +#ifndef MOBILEAPP Admin::instance().start(); +#endif } void stop() @@ -2613,11 +2700,13 @@ public: os << "Prisoner poll:\n"; PrisonerPoll.dumpState(os); +#ifndef MOBILEAPP os << "Admin poll:\n"; Admin::instance().dumpState(os); // If we have any delaying work going on. Delay::dumpState(os); +#endif os << "Document Broker polls " << "[ " << DocBrokers.size() << " ]:\n"; @@ -2694,6 +2783,9 @@ private: MasterPortNumber = port; LOG_INF("Listening to prisoner connections on port " << port); +#ifdef MOBILEAPP + LOOLWSD::prisonerServerSocketFD = socket->getFD(); +#endif return socket; } @@ -2709,9 +2801,9 @@ private: #endif factory = std::make_shared<PlainSocketFactory>(); - std::shared_ptr<ServerSocket> socket = getServerSocket( ServerSocket::Type::Public, port, WebServerPoll, factory); +#ifdef MOBILEAPP #ifdef BUILDLING_TESTS while (!socket) { @@ -2721,7 +2813,7 @@ private: ServerSocket::Type::Public, port, WebServerPoll, factory); } #endif - +#endif if (!socket) { LOG_FTL("Failed to listen on Server port(s) (" << @@ -2735,7 +2827,7 @@ private: } }; -LOOLWSDServer srv; +static LOOLWSDServer srv; bool LOOLWSD::handleShutdownRequest() { @@ -2758,6 +2850,7 @@ int LOOLWSD::innerMain() SigUtil::setTerminationSignals(); #endif +#ifdef __linux // down-pay all the forkit linking cost once & early. Environment::set("LD_BIND_NOW", "1"); @@ -2767,6 +2860,7 @@ int LOOLWSD::innerMain() Util::getVersionInfo(version, hash); LOG_INF("Loolwsd version details: " << version << " - " << hash); } +#endif initializeSSL(); @@ -2783,6 +2877,7 @@ int LOOLWSD::innerMain() return Application::EXIT_SOFTWARE; } +#ifndef MOBILEAPP // We use the same option set for both parent and child loolwsd, // so must check options required in the parent (but not in the // child) separately now. Also check for options that are @@ -2819,11 +2914,15 @@ int LOOLWSD::innerMain() throw IncompatibleOptionsException("port"); ClientRequestDispatcher::InitStaticFileContentCache(); +#endif // Start the internal prisoner server and spawn forkit, // which in turn forks first child. srv.startPrisoners(MasterPortNumber); +// No need to "have at least one child" beforehand on mobile +#ifndef MOBILEAPP + #ifndef KIT_IN_PROCESS { // Make sure we have at least one child before moving forward. @@ -2862,6 +2961,8 @@ int LOOLWSD::innerMain() Log::logger().setLevel(LogLevel); } +#endif + // Start the server. srv.start(ClientPortNumber); @@ -2886,6 +2987,7 @@ int LOOLWSD::innerMain() // Wake the prisoner poll to spawn some children, if necessary. PrisonerPoll.wakeup(); +#ifndef MOBILEAPP const std::chrono::milliseconds::rep timeSinceStartMs = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - startStamp).count(); @@ -2900,8 +3002,12 @@ int LOOLWSD::innerMain() break; } #endif +#endif } +// No point in doing any orderly shutdown on mobile, we will never exit intentionally, the OS will +// kill us. +#ifndef MOBILEAPP // Stop the listening to new connections // and wait until sockets close. LOG_INF("Stopping server socket listening. ShutdownFlag: " << @@ -2975,22 +3081,27 @@ int LOOLWSD::innerMain() LOG_INF("Removing jail [" << path << "]."); FileUtil::removeFile(path, true); } +#endif // !MOBILEAPP + return Application::EXIT_OK; } + void LOOLWSD::cleanup() { +#ifndef MOBILEAPP FileServerRequestHandler::uninitialize(); +#if ENABLE_SSL // Finally, we no longer need SSL. if (LOOLWSD::isSSLEnabled()) { Poco::Net::uninitializeSSL(); Poco::Crypto::uninitializeCrypto(); -#if ENABLE_SSL SslContext::uninitialize(); -#endif } +#endif +#endif } int LOOLWSD::main(const std::vector<std::string>& /*args*/) @@ -3015,6 +3126,8 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/) return returnValue; } +#ifndef MOBILEAPP + void UnitWSD::testHandleRequest(TestRequest type, UnitHTTPServerRequest& /* request */, UnitHTTPServerResponse& /* response */) { switch (type) @@ -3060,6 +3173,8 @@ void alertAllUsers(const std::string& msg) } #endif +#endif + void dump_state() { std::ostringstream oss; @@ -3070,8 +3185,10 @@ void dump_state() LOG_TRC(msg); } +#ifndef MOBILEAPP + POCO_SERVER_MAIN(LOOLWSD) -#endif // !MOBILEAPP +#endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/LOOLWSD.hpp b/wsd/LOOLWSD.hpp index 90cc932e5..369b8ab33 100644 --- a/wsd/LOOLWSD.hpp +++ b/wsd/LOOLWSD.hpp @@ -28,7 +28,11 @@ class ChildProcess; class TraceFileWriter; class DocumentBroker; -std::shared_ptr<ChildProcess> getNewChild_Blocks(); +std::shared_ptr<ChildProcess> getNewChild_Blocks( +#ifdef MOBILEAPP + const std::string& uri +#endif + ); /// The Server class which is responsible for all /// external interactions. @@ -143,11 +147,12 @@ public: /// Autosave a given document static void autoSave(const std::string& docKey); + int innerMain(); + protected: void initialize(Poco::Util::Application& self) override; void defineOptions(Poco::Util::OptionSet& options) override; void handleOption(const std::string& name, const std::string& value) override; - int innerMain(); int main(const std::vector<std::string>& args) override; /// Handle various global static destructors. @@ -237,6 +242,11 @@ private: private: /// Settings passed from the command-line to override those in the config file. std::map<std::string, std::string> _overrideSettings; + +#ifdef MOBILEAPP +public: + static int prisonerServerSocketFD; +#endif }; #endif diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp index 52ab41d16..951fe31d0 100644 --- a/wsd/Storage.cpp +++ b/wsd/Storage.cpp @@ -56,6 +56,8 @@ bool StorageBase::FilesystemEnabled; bool StorageBase::WopiEnabled; Util::RegexListMatcher StorageBase::WopiHosts; +#ifndef MOBILEAPP + std::string StorageBase::getLocalRootPath() const { std::string localPath = _jailPath; @@ -77,9 +79,12 @@ size_t StorageBase::getFileSize(const std::string& filename) return std::ifstream(filename, std::ifstream::ate | std::ifstream::binary).tellg(); } +#endif + void StorageBase::initialize() { const auto& app = Poco::Util::Application::instance(); +#ifndef MOBILEAPP FilesystemEnabled = app.config().getBool("storage.filesystem[@allow]", false); // Parse the WOPI settings. @@ -128,6 +133,9 @@ void StorageBase::initialize() Poco::Net::Context::Ptr sslClientContext = new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, sslClientParams); Poco::Net::SSLManager::instance().initializeClient(consoleClientHandler, invalidClientCertHandler, sslClientContext); #endif +#else + FilesystemEnabled = true; +#endif } #ifndef MOBILEAPP @@ -179,7 +187,6 @@ std::unique_ptr<StorageBase> StorageBase::create(const Poco::URI& uri, const std // here much earlier. Also, using exceptions is lame and makes understanding the code harder, // but that is just my personal preference. -#ifndef MOBILEAPP std::unique_ptr<StorageBase> storage; if (UnitWSD::get().createStorage(uri, jailRoot, jailPath, storage)) @@ -190,10 +197,6 @@ std::unique_ptr<StorageBase> StorageBase::create(const Poco::URI& uri, const std return storage; } } -#else - if (false) - ; -#endif else if (uri.isRelative() || uri.getScheme() == "file") { LOG_INF("Public URI [" << uri.toString() << "] is a file."); @@ -263,6 +266,7 @@ std::unique_ptr<LocalStorage::LocalFileInfo> LocalStorage::getLocalFileInfo() std::string LocalStorage::loadStorageFileToLocal(const Authorization& /*auth*/) { +#ifndef MOBILEAPP // /chroot/jailId/user/doc/childId/file.ext const std::string filename = Poco::Path(_uri.getPath()).getFileName(); _jailedFilePath = Poco::Path(getLocalRootPath(), filename).toString(); @@ -311,6 +315,16 @@ std::string LocalStorage::loadStorageFileToLocal(const Authorization& /*auth*/) #else return _jailedFilePath; #endif + +#else // MOBILEAPP + + // In the mobile app we use no jail + _jailedFilePath = _uri.getPath(); + _isLoaded = true; + + return _jailedFilePath; +#endif + } StorageBase::SaveResult LocalStorage::saveLocalFileToStorage(const Authorization& /*auth*/, const std::string& /*saveAsPath*/, const std::string& /*saveAsFilename*/) diff --git a/wsd/TileCache.cpp b/wsd/TileCache.cpp index e76d1c7db..52d923ffd 100644 --- a/wsd/TileCache.cpp +++ b/wsd/TileCache.cpp @@ -151,11 +151,9 @@ std::unique_ptr<std::fstream> TileCache::lookupTile(const TileDesc& tile) std::unique_ptr<std::fstream> result(new std::fstream(fileName, std::ios::in)); -#ifndef MOBILEAPP UnitWSD::get().lookupTile(tile.getPart(), tile.getWidth(), tile.getHeight(), tile.getTilePosX(), tile.getTilePosY(), tile.getTileWidth(), tile.getTileHeight(), result); -#endif if (result && result->is_open()) { LOG_TRC("Found cache tile: " << fileName); commit 384a57092c72b5fa7ce87efebb3e476334840d13 Author: Tor Lillqvist <[email protected]> AuthorDate: Wed Sep 12 22:18:08 2018 +0300 Commit: Tor Lillqvist <[email protected]> CommitDate: Wed Sep 19 11:19:25 2018 +0300 Make the "Online" source files actually show up in Xcode diff --git a/Mobile/Mobile.xcodeproj/project.pbxproj b/Mobile/Mobile.xcodeproj/project.pbxproj index 1bb123e3b..0d2062dff 100644 --- a/Mobile/Mobile.xcodeproj/project.pbxproj +++ b/Mobile/Mobile.xcodeproj/project.pbxproj @@ -24,7 +24,6 @@ BE5EB5C6213FE29900E0826C /* SigUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5BE213FE29900E0826C /* SigUtil.cpp */; }; BE5EB5C7213FE29900E0826C /* Protocol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5BF213FE29900E0826C /* Protocol.cpp */; }; BE5EB5C8213FE29900E0826C /* FileUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5C0213FE29900E0826C /* FileUtil.cpp */; }; - BE5EB5CA213FE2B100E0826C /* Socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5C9213FE2B100E0826C /* Socket.cpp */; }; BE5EB5CF213FE2D000E0826C /* ClientSession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5CC213FE2D000E0826C /* ClientSession.cpp */; }; BE5EB5D0213FE2D000E0826C /* TileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5CD213FE2D000E0826C /* TileCache.cpp */; }; BE5EB5D22140039100E0826C /* LOOLWSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5D12140039100E0826C /* LOOLWSD.cpp */; }; @@ -57,6 +56,7 @@ BEA2835621467FDD00848631 /* Kit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA2835521467FDD00848631 /* Kit.cpp */; }; BEA283582146945500848631 /* ChildSession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA283572146945500848631 /* ChildSession.cpp */; }; BEA2835A21470A1C00848631 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEA2835921470A1C00848631 /* WebKit.framework */; }; + BEA2835D21498AD400848631 /* Socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA2835C21498AD400848631 /* Socket.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -69,20 +69,19 @@ BE00F89E21396585001CE2D4 /* images */ = {isa = PBXFileReference; lastKnownFileType = folder; name = images; path = ../../../loleaflet/dist/images; sourceTree = "<group>"; }; BE00F8B4213ED543001CE2D4 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; BE00F8B6213ED573001CE2D4 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; - BE5EB5B9213FE29900E0826C /* Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Log.cpp; path = ../../../common/Log.cpp; sourceTree = "<group>"; }; - BE5EB5BA213FE29900E0826C /* SpookyV2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SpookyV2.cpp; path = ../../../common/SpookyV2.cpp; sourceTree = "<group>"; }; - BE5EB5BB213FE29900E0826C /* Session.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Session.cpp; path = ../../../common/Session.cpp; sourceTree = "<group>"; }; - BE5EB5BC213FE29900E0826C /* Util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Util.cpp; path = ../../../common/Util.cpp; sourceTree = "<group>"; }; - BE5EB5BD213FE29900E0826C /* MessageQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MessageQueue.cpp; path = ../../../common/MessageQueue.cpp; sourceTree = "<group>"; }; - BE5EB5BE213FE29900E0826C /* SigUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SigUtil.cpp; path = ../../../common/SigUtil.cpp; sourceTree = "<group>"; }; - BE5EB5BF213FE29900E0826C /* Protocol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Protocol.cpp; path = ../../../common/Protocol.cpp; sourceTree = "<group>"; }; - BE5EB5C0213FE29900E0826C /* FileUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileUtil.cpp; path = ../../../common/FileUtil.cpp; sourceTree = "<group>"; }; - BE5EB5C9213FE2B100E0826C /* Socket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Socket.cpp; path = ../../../net/Socket.cpp; sourceTree = "<group>"; }; - BE5EB5CC213FE2D000E0826C /* ClientSession.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClientSession.cpp; path = ../../../wsd/ClientSession.cpp; sourceTree = "<group>"; }; - BE5EB5CD213FE2D000E0826C /* TileCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TileCache.cpp; path = ../../../wsd/TileCache.cpp; sourceTree = "<group>"; }; - BE5EB5D12140039100E0826C /* LOOLWSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LOOLWSD.cpp; path = ../../../wsd/LOOLWSD.cpp; sourceTree = "<group>"; }; - BE5EB5D321400DC100E0826C /* DocumentBroker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DocumentBroker.cpp; path = ../../../wsd/DocumentBroker.cpp; sourceTree = "<group>"; }; - BE5EB5D521401E0F00E0826C /* Storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Storage.cpp; path = ../../../wsd/Storage.cpp; sourceTree = "<group>"; }; + BE5EB5B9213FE29900E0826C /* Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Log.cpp; sourceTree = "<group>"; }; + BE5EB5BA213FE29900E0826C /* SpookyV2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpookyV2.cpp; sourceTree = "<group>"; }; + BE5EB5BB213FE29900E0826C /* Session.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Session.cpp; sourceTree = "<group>"; }; + BE5EB5BC213FE29900E0826C /* Util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Util.cpp; sourceTree = "<group>"; }; + BE5EB5BD213FE29900E0826C /* MessageQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MessageQueue.cpp; sourceTree = "<group>"; }; + BE5EB5BE213FE29900E0826C /* SigUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SigUtil.cpp; sourceTree = "<group>"; }; + BE5EB5BF213FE29900E0826C /* Protocol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Protocol.cpp; sourceTree = "<group>"; }; + BE5EB5C0213FE29900E0826C /* FileUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileUtil.cpp; sourceTree = "<group>"; }; + BE5EB5CC213FE2D000E0826C /* ClientSession.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClientSession.cpp; sourceTree = "<group>"; }; + BE5EB5CD213FE2D000E0826C /* TileCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TileCache.cpp; sourceTree = "<group>"; }; + BE5EB5D12140039100E0826C /* LOOLWSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LOOLWSD.cpp; sourceTree = "<group>"; }; + BE5EB5D321400DC100E0826C /* DocumentBroker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DocumentBroker.cpp; sourceTree = "<group>"; }; + BE5EB5D521401E0F00E0826C /* Storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Storage.cpp; sourceTree = "<group>"; }; BE5EB5D92140363100E0826C /* ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios.mm; path = ../../ios/ios.mm; sourceTree = "<group>"; }; BE5EB5DB2140480B00E0826C /* icudt62l.dat */ = {isa = PBXFileReference; lastKnownFileType = file; name = icudt62l.dat; path = "$(LOBUILDDIR)/workdir/CustomTarget/ios/resources/icudt62l.dat"; sourceTree = "<group>"; }; BE8D77272136762500AC58EA /* Mobile.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Mobile.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -113,9 +112,10 @@ ... etc. - the rest is truncated _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
