Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/owncloud/client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Verbruggen <erik@verbruggen.consulting>2021-11-10 20:05:55 +0300
committerErik Verbruggen <erik@verbruggen.consulting>2021-11-19 19:40:50 +0300
commit16acce08b9039aaecdb8f3bf372eea3b04fbc0d9 (patch)
tree7ec4877dd08e8e15ae152882118a0f8e0c4d0d08
parent3535be9cf8686f1a6ea9ae73a4399e2106a43bfa (diff)
macOS: Use local socket to communicate with finder extensionwork/macos-use-local-socket
https://github.com/owncloud/client/issues/6343
-rw-r--r--shell_integration/MacOSX/OwnCloud.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist8
-rw-r--r--shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h20
-rw-r--r--shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m311
-rw-r--r--shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSyncExt-Bridging-Header.h5
-rw-r--r--shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/LineProcessorV1.swift67
-rw-r--r--shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/LocalSocketClient.swift289
-rw-r--r--shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClient.h27
-rw-r--r--shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.h45
-rw-r--r--shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.m161
-rw-r--r--shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj36
-rw-r--r--src/gui/guiutility_mac.mm10
-rw-r--r--src/gui/socketapi/CMakeLists.txt4
-rw-r--r--src/gui/socketapi/socketapi.cpp37
-rw-r--r--src/gui/socketapi/socketapi.h2
-rw-r--r--src/gui/socketapi/socketapisocket_mac.h69
-rw-r--r--src/gui/socketapi/socketapisocket_mac.mm273
16 files changed, 627 insertions, 737 deletions
diff --git a/shell_integration/MacOSX/OwnCloud.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/shell_integration/MacOSX/OwnCloud.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/shell_integration/MacOSX/OwnCloud.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h
index 803c5270f..27d7a5a02 100644
--- a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h
+++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h
@@ -15,15 +15,19 @@
#import <Cocoa/Cocoa.h>
#import <FinderSync/FinderSync.h>
-#import "SyncClientProxy.h"
-@interface FinderSync : FIFinderSync <SyncClientProxyDelegate>
-{
- SyncClientProxy *_syncClientProxy;
- NSMutableSet *_registeredDirectories;
- NSString *_shareMenuTitle;
- NSMutableDictionary *_strings;
- NSMutableArray *_menuItems;
+#import "FinderSyncExt-Swift.h"
+#import "SyncClient.h"
+
+@interface FinderSync : FIFinderSync <SyncClientDelegate> {
+ NSMutableSet *_registeredDirectories;
+ NSString *_shareMenuTitle;
+ NSMutableDictionary *_strings;
+ NSMutableArray *_menuItems;
+ NSCondition *_menuIsComplete;
}
+@property LineProcessorV1 *v1LineProcessor;
+@property LocalSocketClient *localSocketClient;
+
@end
diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m
index a0f791882..0f95465f2 100644
--- a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m
+++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m
@@ -20,190 +20,219 @@
- (instancetype)init
{
- self = [super init];
-
- FIFinderSyncController *syncController = [FIFinderSyncController defaultController];
- NSBundle *extBundle = [NSBundle bundleForClass:[self class]];
- // This was added to the bundle's Info.plist to get it from the build system
- NSString *socketApiPrefix = [extBundle objectForInfoDictionaryKey:@"SocketApiPrefix"];
-
- NSImage *ok = [extBundle imageForResource:@"ok.icns"];
- NSImage *ok_swm = [extBundle imageForResource:@"ok_swm.icns"];
- NSImage *sync = [extBundle imageForResource:@"sync.icns"];
- NSImage *warning = [extBundle imageForResource:@"warning.icns"];
- NSImage *error = [extBundle imageForResource:@"error.icns"];
-
- [syncController setBadgeImage:ok label:@"Up to date" forBadgeIdentifier:@"OK"];
- [syncController setBadgeImage:sync label:@"Synchronizing" forBadgeIdentifier:@"SYNC"];
- [syncController setBadgeImage:sync label:@"Synchronizing" forBadgeIdentifier:@"NEW"];
- [syncController setBadgeImage:warning label:@"Ignored" forBadgeIdentifier:@"IGNORE"];
- [syncController setBadgeImage:error label:@"Error" forBadgeIdentifier:@"ERROR"];
- [syncController setBadgeImage:ok_swm label:@"Shared" forBadgeIdentifier:@"OK+SWM"];
- [syncController setBadgeImage:sync label:@"Synchronizing" forBadgeIdentifier:@"SYNC+SWM"];
- [syncController setBadgeImage:sync label:@"Synchronizing" forBadgeIdentifier:@"NEW+SWM"];
- [syncController setBadgeImage:warning label:@"Ignored" forBadgeIdentifier:@"IGNORE+SWM"];
- [syncController setBadgeImage:error label:@"Error" forBadgeIdentifier:@"ERROR+SWM"];
-
- // The Mach port name needs to:
- // - Be prefixed with the code signing Team ID
- // - Then infixed with the sandbox App Group
- // - The App Group itself must be a prefix of (or equal to) the application bundle identifier
- // We end up in the official signed client with: 9B5WD74GWJ.com.owncloud.desktopclient.socketApi
- // With ad-hoc signing (the '-' signing identity) we must drop the Team ID.
- // When the code isn't sandboxed (e.g. the OC client or the legacy overlay icon extension)
- // the OS doesn't seem to put any restriction on the port name, so we just follow what
- // the sandboxed App Extension needs.
- // https://developer.apple.com/library/mac/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24
- NSString *serverName = [socketApiPrefix stringByAppendingString:@".socketApi"];
- //NSLog(@"FinderSync serverName %@", serverName);
-
- _syncClientProxy = [[SyncClientProxy alloc] initWithDelegate:self serverName:serverName];
- _registeredDirectories = [[NSMutableSet alloc] init];
- _strings = [[NSMutableDictionary alloc] init];
-
- [_syncClientProxy start];
- return self;
+ self = [super init];
+
+ FIFinderSyncController *syncController = [FIFinderSyncController defaultController];
+ NSBundle *extBundle = [NSBundle bundleForClass:[self class]];
+ // This was added to the bundle's Info.plist to get it from the build system
+ NSString *socketApiPrefix = [extBundle objectForInfoDictionaryKey:@"SocketApiPrefix"];
+
+ NSImage *ok = [extBundle imageForResource:@"ok.icns"];
+ NSImage *ok_swm = [extBundle imageForResource:@"ok_swm.icns"];
+ NSImage *sync = [extBundle imageForResource:@"sync.icns"];
+ NSImage *warning = [extBundle imageForResource:@"warning.icns"];
+ NSImage *error = [extBundle imageForResource:@"error.icns"];
+
+ [syncController setBadgeImage:ok label:@"Up to date" forBadgeIdentifier:@"OK"];
+ [syncController setBadgeImage:sync label:@"Synchronizing" forBadgeIdentifier:@"SYNC"];
+ [syncController setBadgeImage:sync label:@"Synchronizing" forBadgeIdentifier:@"NEW"];
+ [syncController setBadgeImage:warning label:@"Ignored" forBadgeIdentifier:@"IGNORE"];
+ [syncController setBadgeImage:error label:@"Error" forBadgeIdentifier:@"ERROR"];
+ [syncController setBadgeImage:ok_swm label:@"Shared" forBadgeIdentifier:@"OK+SWM"];
+ [syncController setBadgeImage:sync label:@"Synchronizing" forBadgeIdentifier:@"SYNC+SWM"];
+ [syncController setBadgeImage:sync label:@"Synchronizing" forBadgeIdentifier:@"NEW+SWM"];
+ [syncController setBadgeImage:warning label:@"Ignored" forBadgeIdentifier:@"IGNORE+SWM"];
+ [syncController setBadgeImage:error label:@"Error" forBadgeIdentifier:@"ERROR+SWM"];
+
+ // The Mach port name needs to:
+ // - Be prefixed with the code signing Team ID
+ // - Then infixed with the sandbox App Group
+ // - The App Group itself must be a prefix of (or equal to) the application bundle identifier
+ // We end up in the official signed client with: 9B5WD74GWJ.com.owncloud.desktopclient.socketApi
+ // With ad-hoc signing (the '-' signing identity) we must drop the Team ID.
+ // When the code isn't sandboxed (e.g. the OC client or the legacy overlay icon extension)
+ // the OS doesn't seem to put any restriction on the port name, so we just follow what
+ // the sandboxed App Extension needs.
+ // https://developer.apple.com/library/mac/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24
+
+ NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:socketApiPrefix];
+ NSURL *socketPath = [container URLByAppendingPathComponent:@"GUI.socket" isDirectory:false];
+
+ if (socketPath.path) {
+ self.v1LineProcessor = [[LineProcessorV1 alloc] initWithDelegate:self];
+ self.localSocketClient = [[LocalSocketClient alloc] initWithSocketPath:socketPath.path
+ lineProcessor:self.v1LineProcessor];
+ [self.localSocketClient start];
+ } else {
+ self.localSocketClient = nil;
+ }
+ _registeredDirectories = [[NSMutableSet alloc] init];
+ _strings = [[NSMutableDictionary alloc] init];
+ _menuIsComplete = [[NSCondition alloc] init];
+
+ return self;
}
#pragma mark - Primary Finder Sync protocol methods
- (void)requestBadgeIdentifierForURL:(NSURL *)url
{
- BOOL isDir;
- if ([[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory: &isDir] == NO) {
- NSLog(@"ERROR: Could not determine file type of %@", [url path]);
- isDir = NO;
- }
-
- NSString* normalizedPath = [[url path] decomposedStringWithCanonicalMapping];
- [_syncClientProxy askForIcon:normalizedPath isDirectory:isDir];
+ BOOL isDir;
+ if ([[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&isDir] == NO) {
+ NSLog(@"ERROR: Could not determine file type of %@", [url path]);
+ isDir = NO;
+ }
+
+ NSString *normalizedPath = [[url path] decomposedStringWithCanonicalMapping];
+ [self.localSocketClient askForIcon:normalizedPath isDirectory:isDir];
}
#pragma mark - Menu and toolbar item support
-- (NSString*) selectedPathsSeparatedByRecordSeparator
+- (NSString *)selectedPathsSeparatedByRecordSeparator
{
- FIFinderSyncController *syncController = [FIFinderSyncController defaultController];
- NSMutableString *string = [[NSMutableString alloc] init];
- [syncController.selectedItemURLs enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
- if (string.length > 0) {
- [string appendString:@"\x1e"]; // record separator
- }
- NSString* normalizedPath = [[obj path] decomposedStringWithCanonicalMapping];
- [string appendString:normalizedPath];
- }];
- return string;
+ FIFinderSyncController *syncController = [FIFinderSyncController defaultController];
+ NSMutableString *string = [[NSMutableString alloc] init];
+ [syncController.selectedItemURLs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ if (string.length > 0) {
+ [string appendString:@"\x1e"]; // record separator
+ }
+ NSString *normalizedPath = [[obj path] decomposedStringWithCanonicalMapping];
+ [string appendString:normalizedPath];
+ }];
+ return string;
}
- (NSMenu *)menuForMenuKind:(FIMenuKind)whichMenu
{
- FIFinderSyncController *syncController = [FIFinderSyncController defaultController];
- NSMutableSet *rootPaths = [[NSMutableSet alloc] init];
- [syncController.directoryURLs enumerateObjectsUsingBlock: ^(id obj, BOOL *stop) {
- [rootPaths addObject:[obj path]];
- }];
-
- // The server doesn't support sharing a root directory so do not show the option in this case.
- // It is still possible to get a problematic sharing by selecting both the root and a child,
- // but this is so complicated to do and meaningless that it's not worth putting this check
- // also in shareMenuAction.
- __block BOOL onlyRootsSelected = YES;
- [syncController.selectedItemURLs enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
- if (![rootPaths member:[obj path]]) {
- onlyRootsSelected = NO;
- *stop = YES;
- }
- }];
-
- NSString *paths = [self selectedPathsSeparatedByRecordSeparator];
- // calling this IPC calls us back from client with several MENU_ITEM entries and then our askOnSocket returns again
- [_syncClientProxy askOnSocket:paths query:@"GET_MENU_ITEMS"];
-
- id contextMenuTitle = [_strings objectForKey:@"CONTEXT_MENU_TITLE"];
- if (contextMenuTitle && !onlyRootsSelected) {
- NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
- NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@""];
- NSMenuItem *subMenuItem = [menu addItemWithTitle:contextMenuTitle action:nil keyEquivalent:@""];
- subMenuItem.submenu = subMenu;
- subMenuItem.image = [[NSBundle mainBundle] imageForResource:@"app.icns"];
-
- // There is an annoying bug in macOS (at least 10.13.3), it does not use/copy over the representedObject of a menu item
- // So we have to use tag instead.
- int idx = 0;
- for (NSArray* item in _menuItems) {
- NSMenuItem *actionItem = [subMenu addItemWithTitle:[item valueForKey:@"text"]
- action:@selector(subMenuActionClicked:)
- keyEquivalent:@""];
- [actionItem setTag:idx];
- [actionItem setTarget:self];
- NSString *flags = [item valueForKey:@"flags"]; // e.g. "d"
- if ([flags rangeOfString:@"d"].location != NSNotFound) {
- [actionItem setEnabled:false];
- }
- idx++;
- }
- return menu;
- }
- return nil;
+ if (!self.localSocketClient.isConnected) {
+ return nil;
+ }
+
+ FIFinderSyncController *syncController = [FIFinderSyncController defaultController];
+ NSMutableSet *rootPaths = [[NSMutableSet alloc] init];
+ [syncController.directoryURLs enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
+ [rootPaths addObject:[obj path]];
+ }];
+
+ // The server doesn't support sharing a root directory so do not show the option in this case.
+ // It is still possible to get a problematic sharing by selecting both the root and a child,
+ // but this is so complicated to do and meaningless that it's not worth putting this check
+ // also in shareMenuAction.
+ __block BOOL onlyRootsSelected = YES;
+ [syncController.selectedItemURLs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ if (![rootPaths member:[obj path]]) {
+ onlyRootsSelected = NO;
+ *stop = YES;
+ }
+ }];
+
+ NSString *paths = [self selectedPathsSeparatedByRecordSeparator];
+ [self.localSocketClient askOnSocket:paths query:@"GET_MENU_ITEMS"];
+
+ // The LocalSocketClient uses asynchronous communication, so we have to wait here until the menu items have
+ // delivered by another thread.
+ [self waitForMenuToArrive];
+
+ id contextMenuTitle = [_strings objectForKey:@"CONTEXT_MENU_TITLE"];
+ if (contextMenuTitle && !onlyRootsSelected) {
+ NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
+ NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@""];
+ NSMenuItem *subMenuItem = [menu addItemWithTitle:contextMenuTitle action:nil keyEquivalent:@""];
+ subMenuItem.submenu = subMenu;
+ subMenuItem.image = [[NSBundle mainBundle] imageForResource:@"app.icns"];
+
+ // There is an annoying bug in macOS (at least 10.13.3), it does not use/copy over the representedObject of a menu item
+ // So we have to use tag instead.
+ int idx = 0;
+ for (NSArray *item in _menuItems) {
+ NSMenuItem *actionItem = [subMenu addItemWithTitle:[item valueForKey:@"text"]
+ action:@selector(subMenuActionClicked:)
+ keyEquivalent:@""];
+ [actionItem setTag:idx];
+ [actionItem setTarget:self];
+ NSString *flags = [item valueForKey:@"flags"]; // e.g. "d"
+ if ([flags rangeOfString:@"d"].location != NSNotFound) {
+ [actionItem setEnabled:false];
+ }
+ idx++;
+ }
+ return menu;
+ }
+ return nil;
}
-- (void)subMenuActionClicked:(id)sender {
- long idx = [(NSMenuItem*)sender tag];
- NSString *command = [[_menuItems objectAtIndex:idx] valueForKey:@"command"];
- NSString *paths = [self selectedPathsSeparatedByRecordSeparator];
- [_syncClientProxy askOnSocket:paths query:command];
+- (void)waitForMenuToArrive
+{
+ [self->_menuIsComplete lock];
+ [self->_menuIsComplete wait];
+ [self->_menuIsComplete unlock];
+}
+
+- (void)subMenuActionClicked:(id)sender
+{
+ long idx = [(NSMenuItem *)sender tag];
+ NSString *command = [[_menuItems objectAtIndex:idx] valueForKey:@"command"];
+ NSString *paths = [self selectedPathsSeparatedByRecordSeparator];
+ [self.localSocketClient askOnSocket:paths query:command];
}
#pragma mark - SyncClientProxyDelegate implementation
-- (void)setResultForPath:(NSString*)path result:(NSString*)result
+- (void)setResultForPath:(NSString *)path result:(NSString *)result
{
- NSString *normalizedPath = [path decomposedStringWithCanonicalMapping];
- [[FIFinderSyncController defaultController] setBadgeIdentifier:result forURL:[NSURL fileURLWithPath:normalizedPath]];
+ NSString *normalizedPath = [path decomposedStringWithCanonicalMapping];
+ [[FIFinderSyncController defaultController] setBadgeIdentifier:result forURL:[NSURL fileURLWithPath:normalizedPath]];
}
-- (void)reFetchFileNameCacheForPath:(NSString*)path
+- (void)reFetchFileNameCacheForPath:(NSString *)path
{
}
-- (void)registerPath:(NSString*)path
+- (void)registerPath:(NSString *)path
{
- assert(_registeredDirectories);
- [_registeredDirectories addObject:[NSURL fileURLWithPath:path]];
- [FIFinderSyncController defaultController].directoryURLs = _registeredDirectories;
+ assert(_registeredDirectories);
+ [_registeredDirectories addObject:[NSURL fileURLWithPath:path]];
+ [FIFinderSyncController defaultController].directoryURLs = _registeredDirectories;
}
-- (void)unregisterPath:(NSString*)path
+- (void)unregisterPath:(NSString *)path
{
- [_registeredDirectories removeObject:[NSURL fileURLWithPath:path]];
- [FIFinderSyncController defaultController].directoryURLs = _registeredDirectories;
+ [_registeredDirectories removeObject:[NSURL fileURLWithPath:path]];
+ [FIFinderSyncController defaultController].directoryURLs = _registeredDirectories;
}
-- (void)setString:(NSString*)key value:(NSString*)value
+- (void)setString:(NSString *)key value:(NSString *)value
{
- [_strings setObject:value forKey:key];
+ [_strings setObject:value forKey:key];
}
- (void)resetMenuItems
{
- _menuItems = [[NSMutableArray alloc] init];
+ _menuItems = [[NSMutableArray alloc] init];
+}
+
+- (void)addMenuItem:(NSDictionary *)item
+{
+ [_menuItems addObject:item];
}
-- (void)addMenuItem:(NSDictionary *)item {
- [_menuItems addObject:item];
+
+- (void)menuHasCompleted
+{
+ [self->_menuIsComplete signal];
}
- (void)connectionDidDie
{
- [_strings removeAllObjects];
- [_registeredDirectories removeAllObjects];
- // For some reason the FIFinderSync cache doesn't seem to be cleared for the root item when
- // we reset the directoryURLs (seen on macOS 10.12 at least).
- // First setting it to the FS root and then setting it to nil seems to work around the issue.
- [FIFinderSyncController defaultController].directoryURLs = [NSSet setWithObject:[NSURL fileURLWithPath:@"/"]];
- // This will tell Finder that this extension isn't attached to any directory
- // until we can reconnect to the sync client.
- [FIFinderSyncController defaultController].directoryURLs = nil;
+ [_strings removeAllObjects];
+ [_registeredDirectories removeAllObjects];
+ // For some reason the FIFinderSync cache doesn't seem to be cleared for the root item when
+ // we reset the directoryURLs (seen on macOS 10.12 at least).
+ // First setting it to the FS root and then setting it to nil seems to work around the issue.
+ [FIFinderSyncController defaultController].directoryURLs = [NSSet setWithObject:[NSURL fileURLWithPath:@"/"]];
+ // This will tell Finder that this extension isn't attached to any directory
+ // until we can reconnect to the sync client.
+ [FIFinderSyncController defaultController].directoryURLs = nil;
}
@end
-
diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSyncExt-Bridging-Header.h b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSyncExt-Bridging-Header.h
new file mode 100644
index 000000000..a363d7439
--- /dev/null
+++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSyncExt-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 "SyncClient.h"
diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/LineProcessorV1.swift b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/LineProcessorV1.swift
new file mode 100644
index 000000000..a554a0f0a
--- /dev/null
+++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/LineProcessorV1.swift
@@ -0,0 +1,67 @@
+//
+// LineProcessorV1.swift
+// FinderSyncExt
+//
+// Created by Erik Verbruggen <erik@verbruggen.consulting> on 06-11-21.
+//
+
+import Foundation
+import OSLog
+
+class LineProcessorV1: NSObject, LineProcessor {
+ let delegate: SyncClientDelegate
+
+ @objc init(withDelegate delegate: SyncClientDelegate) {
+ self.delegate = delegate
+ }
+
+ private func log(_ str: String, type logType: OSLogType) {
+ // We cannot use OSLog's Logger class, because a lot of methods are only available in macOS 11.0 or higher.
+ os_log("%@", type: logType, str)
+ }
+
+ /// Processes a line, where the trailing \n has already been stripped
+ func process(line: String) {
+
+ self.log("Processing line '\(line)'", type: .debug)
+ let chunks = line.components(separatedBy: ":")
+ let command = chunks[0]
+
+ switch command {
+ case "STATUS":
+ let result = chunks[1]
+ let path = chunks.suffix(from: 2).joined(separator: ":")
+ DispatchQueue.main.async { self.delegate.setResultForPath(path, result: result) }
+ case "UPDATE_VIEW":
+ let path = chunks[1]
+ DispatchQueue.main.async { self.delegate.reFetchFileNameCache(forPath: path) }
+ case "REGISTER_PATH":
+ let path = chunks[1]
+ DispatchQueue.main.async { self.delegate.registerPath(path) }
+ case "UNREGISTER_PATH":
+ let path = chunks[1]
+ DispatchQueue.main.async { self.delegate.unregisterPath(path) }
+ case "GET_STRINGS":
+ // BEGIN and END messages, do nothing.
+ break
+ case "STRING":
+ DispatchQueue.main.async { self.delegate.setString(chunks[1], value: chunks[2]) }
+ case "GET_MENU_ITEMS":
+ if chunks[1] == "BEGIN" {
+ DispatchQueue.main.async { self.delegate.resetMenuItems() }
+ } else {
+ // Do NOT run this on the main queue! It might be blocked waiting for the menu to be completed.
+ delegate.menuHasCompleted()
+ }
+ case "MENU_ITEM":
+ let item = [
+ "command" : chunks[1],
+ "flags" : chunks[2],
+ "text" : chunks[3]
+ ]
+ DispatchQueue.main.async { self.delegate.addMenuItem(item) }
+ default:
+ self.log("Unknown command '\(command)'", type: .error)
+ }
+ }
+}
diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/LocalSocketClient.swift b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/LocalSocketClient.swift
new file mode 100644
index 000000000..c0f2a3acd
--- /dev/null
+++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/LocalSocketClient.swift
@@ -0,0 +1,289 @@
+//
+// LocalSocketClient.swift
+// FinderSyncExt
+//
+// Created by Erik Verbruggen <erik@verbruggen.consulting> on 04-11-21.
+//
+
+import Foundation
+import OSLog
+
+/// Process lines from the `LocalSocketClient`.
+@objc protocol LineProcessor {
+ func process(line: String);
+}
+
+/// Class handling the (asynchronous) communication with a server over a local (UNIX) socket.
+///
+/// The implementation uses a `DispatchQueue` and `DispatchSource`s to handle asynchronous communication and thread
+/// safety. All public/@objc function can be called from any thread/queue. The delegate that handles the
+/// line-decoding is **not invoked on the UI thread**, but the (random) thread associated with the `DispatchQueue`!
+/// If any UI work needs to be done, the class implementing the `LineProcessor` protocol should dispatch this work
+/// on the main queue (so the UI thread) itself!
+///
+/// Other than the `init(withSocketPath:, lineProcessor)` and the `start()` method, all work is done "on the dispatch
+/// queue". The `localSocketQueue` is a serial dispatch queue (so a maximum of 1, and only 1, task is run at any
+/// moment), which guarantees safe access to instance variables. Both `askOnSocket(_:, query:)` and
+/// `askForIcon(_:, isDirectory:)` will internally dispatch the work on the `DispatchQueue`.
+///
+/// Sending and receiving data to and from the socket, is handled by two `DispatchSource`s. These will run an event
+/// handler when data can be read from resp. written to the socket. These handlers will also be run on the
+/// `DispatchQueue`.
+class LocalSocketClient: NSObject {
+ let socketPath: String
+ let lineProcessor: LineProcessor
+
+ private var sock: Int32?
+ private var localSocketQueue = DispatchQueue.init(label: "localSocketQueue")
+ private var readSource: DispatchSourceRead?
+ private var writeSource: DispatchSourceWrite?
+ private var inBuffer = [UInt8]()
+ private var outBuffer = [UInt8]()
+
+ @objc var isConnected: Bool {
+ get {
+ sock != nil
+ }
+ }
+
+ @objc init(withSocketPath socketPath: String, lineProcessor: LineProcessor) {
+ self.socketPath = socketPath
+ self.lineProcessor = lineProcessor
+
+ super.init()
+
+ self.inBuffer.reserveCapacity(1000)
+ }
+
+ private func log(_ str: String, type logType: OSLogType) {
+ if #available(macOSApplicationExtension 11.0, *) {
+ // NOTE: when support for 10.* is dropped, make an instance variable instead of instantiating the `Logger`
+ // object every time.
+ Logger().log(level: logType, "\(str, privacy: .public)")
+ } else {
+ os_log("%@", type: logType, str)
+ }
+ }
+
+ // MARK: Socket management
+ @objc func start() {
+ guard !self.isConnected else { return }
+
+ var sa_un = sockaddr_un()
+
+ let socketPathByteCount = socketPath.utf8.count + 1; // add 1 for the NUL terminator char
+ let maxByteCount = MemoryLayout.size(ofValue: sa_un.sun_path)
+ guard socketPathByteCount < maxByteCount else {
+ log("Socket path '\(socketPath)' is too long: \(socketPathByteCount) is longer than \(maxByteCount)",
+ type: .error)
+ return
+ }
+
+ log("Opening local socket...", type: .debug)
+
+ self.sock = socket(AF_LOCAL, SOCK_STREAM, 0)
+ guard self.sock != -1 else {
+ self.log("Cannot open socket: \(self.strErr())", type: .error)
+ self.restart()
+ return
+ }
+
+ log("Local socket openned, now connecting to '\(self.socketPath)' ...", type: .debug)
+
+ sa_un.sun_family = UInt8(AF_LOCAL & 0xff)
+
+ let pathBytes = socketPath.utf8 + [0]
+ pathBytes.withUnsafeBytes { srcBuffer in
+ withUnsafeMutableBytes(of: &sa_un.sun_path) { dstPtr in
+ dstPtr.copyMemory(from: srcBuffer)
+ }
+ }
+
+ let connStatus = withUnsafePointer(to: &sa_un) { sa_unPtr in
+ // We are now allowed to mess with the raw pointer to `sa_un`, and cast it to a `sockaddr` pointer.
+ // This is basically a barrier before and after this closure, so that all writes have been done before by
+ // the compiler, and that subsequent reads do not read old values (because Swift can't see if `connect`
+ // messes with the memory for which it receives a raw pointer).
+ sa_unPtr.withMemoryRebound(to: sockaddr.self, capacity: 1) { saPtr in
+ connect(self.sock!, saPtr, socklen_t(MemoryLayout<sockaddr_un>.size))
+ }
+ }
+
+ guard connStatus != -1 else {
+ self.log("Cannot connect to '\(self.socketPath): \(self.strErr())", type: .error)
+ self.restart()
+ return
+ }
+
+ let flags = fcntl(self.sock!, F_GETFL, 0)
+ guard -1 != fcntl(self.sock!, F_SETFL, flags | O_NONBLOCK) else {
+ self.log("Cannot set socket to non-blocking mode: \(self.strErr())", type: .error)
+ self.restart()
+ return
+ }
+
+ log("We have a connection! Starting dispatch sources...", type: .debug)
+
+ self.readSource = DispatchSource.makeReadSource(fileDescriptor: self.sock!, queue: self.localSocketQueue)
+ self.readSource!.setEventHandler { self.readFromSocket() }
+ self.readSource!.setCancelHandler {
+ self.readSource = nil
+ self.closeConnection()
+ }
+
+ self.writeSource = DispatchSource.makeWriteSource(fileDescriptor: self.sock!, queue: self.localSocketQueue)
+ self.writeSource!.setEventHandler { self.writeToSocket() }
+ self.writeSource!.setCancelHandler {
+ self.writeSource = nil
+ self.closeConnection()
+ }
+ // The writeSource dispatch queue starts suspended; we will resume it when we have data to send (and suspend it
+ // again when our send buffer is empty).
+
+ log("Going live NOW.", type: .debug)
+
+ self.readSource!.resume()
+ self.askOnSocket("", query: "GET_STRINGS")
+ }
+
+ private func restart() {
+ self.closeConnection()
+
+ DispatchQueue.main.async {
+ Timer.scheduledTimer(withTimeInterval: 5, repeats: false, block: { _ in
+ self.start()
+ });
+ }
+ }
+
+ private func closeConnection() {
+ self.readSource?.cancel()
+ self.writeSource?.cancel()
+ self.readSource = nil
+ self.writeSource = nil
+ self.inBuffer.removeAll()
+ self.outBuffer.removeAll()
+ if let sock = self.sock {
+ close(sock)
+ self.sock = nil
+ }
+ }
+
+ private func strErr() -> String {
+ let err = errno // copy error code now, in case something else happens
+ return String(utf8String: strerror(err)) ?? "Unknown error code (\(err))"
+ }
+
+ // MARK: Write data to socket
+ @objc func askOnSocket(_ path: String, query verb: String) {
+ let line = "\(verb):\(path)\n"
+ self.localSocketQueue.async {
+ guard self.isConnected else {
+ // socket was closed while work was still scheduled on the queue
+ return
+ }
+
+ self.log("Sending line '\(line)", type: .debug)
+
+ let writeSourceIsSuspended = self.outBuffer.isEmpty
+ let uint8Data: [UInt8] = line.utf8 + []
+ self.outBuffer.append(contentsOf: uint8Data)
+
+ // Weird stuff happens when you call resume when the DispatchSource is already resumed, so: if we did NOT
+ // have any data in our output buffer before queueing more data, it must be suspended.
+ if writeSourceIsSuspended {
+ self.writeSource?.resume() // now we will get notified when we can write to the socket.
+ }
+ }
+ }
+
+ private func writeToSocket() {
+ guard self.isConnected else {
+ // socket was closed while work was still scheduled on the queue
+ return
+ }
+
+ guard !self.outBuffer.isEmpty else {
+ // the buffer is empty, suspend you-can-write-data notifications
+ self.writeSource!.suspend()
+ return
+ }
+
+ let totalAmountOfBytes = self.outBuffer.count
+ let bytesWritten = self.outBuffer.withUnsafeBytes { ptr in
+ write(self.sock!, ptr.baseAddress, totalAmountOfBytes)
+ }
+ if bytesWritten == 0 {
+ // we reached "end of file", which means that the socket was closed. So in that case:
+ self.restart()
+ } else if bytesWritten == -1 {
+ let err = errno // make a copy, because errno is often volatile
+ if err == EAGAIN || err == EWOULDBLOCK {
+ // no space free in the buffer on the OS side, we're done
+ } else {
+ self.log("Error writing to local socket: \(self.strErr())", type: .error)
+ self.restart()
+ }
+ } else if bytesWritten > 0 {
+ self.outBuffer.removeFirst(bytesWritten)
+ if self.outBuffer.isEmpty {
+ // the buffer is empty, suspend you-can-write-data notifications
+ self.writeSource!.suspend()
+ }
+ }
+ }
+
+ @objc func askForIcon(_ path: String, isDirectory: Bool) {
+ let verb = isDirectory ? "RETRIEVE_FOLDER_STATUS" : "RETRIEVE_FILE_STATUS"
+ self.askOnSocket(path, query: verb)
+ }
+
+ // MARK: Process data from socket
+ private func readFromSocket() {
+ guard self.isConnected else {
+ // socket was closed while work was still scheduled on the queue
+ return
+ }
+
+ let bufferLength = self.inBuffer.capacity / 2
+ var buffer = [UInt8].init(repeating: 0, count: bufferLength)
+
+ while true {
+ let bytesRead = buffer.withUnsafeMutableBytes { ptr in
+ read(self.sock!, ptr.baseAddress, bufferLength)
+ }
+ if bytesRead == 0 {
+ // we reached "end of file", which means that the socket was closed. So in that case:
+ self.restart()
+ return
+ } else if bytesRead == -1 {
+ if errno == EAGAIN {
+ return // no bytes available, and no error, so we're done
+ } else {
+ self.log("Error reading from local socket: \(self.strErr())", type: .error)
+ self.closeConnection()
+ return // we've closed the connection, we're done
+ }
+ } else {
+ self.inBuffer.append(contentsOf: buffer[0..<bytesRead])
+ self.processInBuffer()
+ // see if there is more to read: restart the loop
+ }
+ }
+ }
+
+ private func processInBuffer() {
+ let separator: UInt8 = 0xa // byte value for '\n'
+ while true {
+ if let pos = self.inBuffer.firstIndex(of: separator) {
+ self.inBuffer[pos] = 0 // add NUL terminator
+ let newLine = String(cString: &self.inBuffer)
+ self.inBuffer.removeFirst(pos + 1) // remove the line from the buffer, including the NUL terminator
+ self.lineProcessor.process(line: newLine)
+ } else {
+ // no separator, we're done
+ return;
+ }
+ }
+ }
+}
diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClient.h b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClient.h
new file mode 100644
index 000000000..f8c495a6c
--- /dev/null
+++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClient.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) by Jocelyn Turcotte <jturcotte@woboq.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#import <Foundation/Foundation.h>
+
+@protocol SyncClientDelegate <NSObject>
+- (void)setResultForPath:(NSString *)path result:(NSString *)result;
+- (void)reFetchFileNameCacheForPath:(NSString *)path;
+- (void)registerPath:(NSString *)path;
+- (void)unregisterPath:(NSString *)path;
+- (void)setString:(NSString *)key value:(NSString *)value;
+- (void)resetMenuItems;
+- (void)addMenuItem:(NSDictionary *)item;
+- (void)menuHasCompleted;
+- (void)connectionDidDie;
+@end
diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.h b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.h
deleted file mode 100644
index 1d0fd74b8..000000000
--- a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) by Jocelyn Turcotte <jturcotte@woboq.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#import <Foundation/Foundation.h>
-
-
-@protocol SyncClientProxyDelegate <NSObject>
-- (void)setResultForPath:(NSString*)path result:(NSString*)result;
-- (void)reFetchFileNameCacheForPath:(NSString*)path;
-- (void)registerPath:(NSString*)path;
-- (void)unregisterPath:(NSString*)path;
-- (void)setString:(NSString*)key value:(NSString*)value;
-- (void)resetMenuItems;
-- (void)addMenuItem:(NSDictionary *)item;
-- (void)connectionDidDie;
-@end
-
-@protocol ChannelProtocol <NSObject>
-- (void)sendMessage:(NSData*)msg;
-@end
-
-@interface SyncClientProxy : NSObject <ChannelProtocol>
-{
- NSString *_serverName;
- NSDistantObject <ChannelProtocol> *_remoteEnd;
-}
-
-@property (weak) id <SyncClientProxyDelegate> delegate;
-
-- (instancetype)initWithDelegate:(id)arg1 serverName:(NSString*)serverName;
-- (void)start;
-- (void)askOnSocket:(NSString*)path query:(NSString*)verb;
-- (void)askForIcon:(NSString*)path isDirectory:(BOOL)isDir;
-@end
diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.m b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.m
deleted file mode 100644
index 656f77003..000000000
--- a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.m
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) by Jocelyn Turcotte <jturcotte@woboq.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#import "SyncClientProxy.h"
-
-@protocol ServerProtocol <NSObject>
-- (void)registerClient:(id)client;
-@end
-
-@interface SyncClientProxy ()
-- (void)registerTransmitter:(id)tx;
-@end
-
-@implementation SyncClientProxy
-
-- (instancetype)initWithDelegate:(id)arg1 serverName:(NSString*)serverName
-{
- self = [super init];
-
- self.delegate = arg1;
- _serverName = serverName;
- _remoteEnd = nil;
-
- return self;
-}
-
-#pragma mark - Connection setup
-
-- (void)start
-{
- if (_remoteEnd)
- return;
-
- // Lookup the server connection
- NSConnection *conn = [NSConnection connectionWithRegisteredName:_serverName host:nil];
-
- if (!conn) {
- // Could not connect to the sync client
- [self scheduleRetry];
- return;
- }
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(connectionDidDie:)
- name:NSConnectionDidDieNotification
- object:conn];
-
- NSDistantObject <ServerProtocol> *server = (NSDistantObject <ServerProtocol> *)[conn rootProxy];
- assert(server);
-
- // This saves a few Mach messages, enable "Distributed Objects" in the scheme's Run diagnostics to watch
- [server setProtocolForProxy:@protocol(ServerProtocol)];
-
- // Send an object to the server to act as the channel rx, we'll receive the tx through registerTransmitter
- [server registerClient:self];
-}
-
-- (void)registerTransmitter:(id)tx;
-{
- // The server replied with the distant object that we will use for tx
- _remoteEnd = (NSDistantObject <ChannelProtocol> *)tx;
- [_remoteEnd setProtocolForProxy:@protocol(ChannelProtocol)];
-
- // Everything is set up, start querying
- [self askOnSocket:@"" query:@"GET_STRINGS"];
-}
-
-- (void)scheduleRetry
-{
- [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(start) userInfo:nil repeats:NO];
-}
-
-- (void)connectionDidDie:(NSNotification*)notification
-{
-#pragma unused(notification)
- _remoteEnd = nil;
- [_delegate connectionDidDie];
-
- [self scheduleRetry];
-}
-
-#pragma mark - Communication logic
-
-- (void)sendMessage:(NSData*)msg
-{
- NSString *answer = [[NSString alloc] initWithData:msg encoding:NSUTF8StringEncoding];
-
- // Cut the trailing newline. We always only receive one line from the client.
- answer = [answer substringToIndex:[answer length] - 1];
- NSArray *chunks = [answer componentsSeparatedByString: @":"];
-
- if( [[chunks objectAtIndex:0] isEqualToString:@"STATUS"] ) {
- NSString *result = [chunks objectAtIndex:1];
- NSString *path = [chunks objectAtIndex:2];
- if( [chunks count] > 3 ) {
- for( int i = 2; i < [chunks count]-1; i++ ) {
- path = [NSString stringWithFormat:@"%@:%@",
- path, [chunks objectAtIndex:i+1] ];
- }
- }
- [_delegate setResultForPath:path result:result];
- } else if( [[chunks objectAtIndex:0] isEqualToString:@"UPDATE_VIEW"] ) {
- NSString *path = [chunks objectAtIndex:1];
- [_delegate reFetchFileNameCacheForPath:path];
- } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"REGISTER_PATH"] ) {
- NSString *path = [chunks objectAtIndex:1];
- [_delegate registerPath:path];
- } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"UNREGISTER_PATH"] ) {
- NSString *path = [chunks objectAtIndex:1];
- [_delegate unregisterPath:path];
- } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"GET_STRINGS"] ) {
- // BEGIN and END messages, do nothing.
- } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"STRING"] ) {
- [_delegate setString:[chunks objectAtIndex:1] value:[chunks objectAtIndex:2]];
- } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"GET_MENU_ITEMS"] ) {
- if ([[chunks objectAtIndex:1] isEqualToString:@"BEGIN"]) {
- [_delegate resetMenuItems];
- } else if ([[chunks objectAtIndex:1] isEqualToString:@"END"]) {
- // Don't do anything special, the askOnSocket call in FinderSync menuForMenuKind will return after this line
- }
- } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"MENU_ITEM"] ) {
- NSMutableDictionary *item = [[NSMutableDictionary alloc] init];
- [item setValue:[chunks objectAtIndex:1] forKey:@"command"]; // e.g. "COPY_PRIVATE_LINK"
- [item setValue:[chunks objectAtIndex:2] forKey:@"flags"]; // e.g. "d"
- [item setValue:[chunks objectAtIndex:3] forKey:@"text"]; // e.g. "Copy private link to clipboard"
- [_delegate addMenuItem:item];
- } else {
- NSLog(@"SyncState: Unknown command %@", [chunks objectAtIndex:0]);
- }
-}
-
-- (void)askOnSocket:(NSString*)path query:(NSString*)verb
-{
- NSString *query = [NSString stringWithFormat:@"%@:%@\n", verb,path];
-
- @try {
- [_remoteEnd sendMessage:[query dataUsingEncoding:NSUTF8StringEncoding]];
- } @catch(NSException* e) {
- // Do nothing and wait for connectionDidDie
- }
-}
-
-- (void)askForIcon:(NSString*)path isDirectory:(BOOL)isDir
-{
- NSString *verb = isDir ? @"RETRIEVE_FOLDER_STATUS" : @"RETRIEVE_FILE_STATUS";
- [self askOnSocket:path query:verb];
-}
-
-@end
-
diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj b/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj
index 5d987c86f..efb88bb56 100644
--- a/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj
+++ b/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj
@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
+ 03559BDE2736D71B0008FD8E /* LineProcessorV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03559BDD2736D71B0008FD8E /* LineProcessorV1.swift */; };
+ 03DB1B202733F6A70026E647 /* LocalSocketClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03DB1B1F2733F6A70026E647 /* LocalSocketClient.swift */; };
C2B573BA1B1CD91E00303B36 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C2B573B91B1CD91E00303B36 /* main.m */; };
C2B573D21B1CD94B00303B36 /* main.m in Resources */ = {isa = PBXBuildFile; fileRef = C2B573B91B1CD91E00303B36 /* main.m */; };
C2B573DE1B1CD9CE00303B36 /* FinderSync.m in Sources */ = {isa = PBXBuildFile; fileRef = C2B573DD1B1CD9CE00303B36 /* FinderSync.m */; };
@@ -16,7 +18,6 @@
C2B573F51B1DAD6400303B36 /* ok.iconset in Resources */ = {isa = PBXBuildFile; fileRef = C2B573ED1B1DAD6400303B36 /* ok.iconset */; };
C2B573F71B1DAD6400303B36 /* sync.iconset in Resources */ = {isa = PBXBuildFile; fileRef = C2B573EF1B1DAD6400303B36 /* sync.iconset */; };
C2B573F91B1DAD6400303B36 /* warning.iconset in Resources */ = {isa = PBXBuildFile; fileRef = C2B573F11B1DAD6400303B36 /* warning.iconset */; };
- C2C932F01F0BFC6700C8BCB3 /* SyncClientProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = C2C932EF1F0BFC6700C8BCB3 /* SyncClientProxy.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -44,6 +45,9 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 03559BDD2736D71B0008FD8E /* LineProcessorV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineProcessorV1.swift; sourceTree = "<group>"; };
+ 03DB1B1E2733F6A70026E647 /* FinderSyncExt-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FinderSyncExt-Bridging-Header.h"; sourceTree = "<group>"; };
+ 03DB1B1F2733F6A70026E647 /* LocalSocketClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalSocketClient.swift; sourceTree = "<group>"; };
C2B573B11B1CD91E00303B36 /* desktopclient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = desktopclient.app; sourceTree = BUILT_PRODUCTS_DIR; };
C2B573B51B1CD91E00303B36 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C2B573B91B1CD91E00303B36 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
@@ -57,8 +61,7 @@
C2B573ED1B1DAD6400303B36 /* ok.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = ok.iconset; path = ../../icons/nopadding/ok.iconset; sourceTree = SOURCE_ROOT; };
C2B573EF1B1DAD6400303B36 /* sync.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = sync.iconset; path = ../../icons/nopadding/sync.iconset; sourceTree = SOURCE_ROOT; };
C2B573F11B1DAD6400303B36 /* warning.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = warning.iconset; path = ../../icons/nopadding/warning.iconset; sourceTree = SOURCE_ROOT; };
- C2C932EE1F0BFC6700C8BCB3 /* SyncClientProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SyncClientProxy.h; sourceTree = "<group>"; };
- C2C932EF1F0BFC6700C8BCB3 /* SyncClientProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SyncClientProxy.m; sourceTree = "<group>"; };
+ C2C932EE1F0BFC6700C8BCB3 /* SyncClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SyncClient.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -117,11 +120,13 @@
C2B573D81B1CD9CE00303B36 /* FinderSyncExt */ = {
isa = PBXGroup;
children = (
- C2C932EE1F0BFC6700C8BCB3 /* SyncClientProxy.h */,
- C2C932EF1F0BFC6700C8BCB3 /* SyncClientProxy.m */,
+ C2C932EE1F0BFC6700C8BCB3 /* SyncClient.h */,
C2B573DC1B1CD9CE00303B36 /* FinderSync.h */,
C2B573DD1B1CD9CE00303B36 /* FinderSync.m */,
C2B573D91B1CD9CE00303B36 /* Supporting Files */,
+ 03DB1B1F2733F6A70026E647 /* LocalSocketClient.swift */,
+ 03559BDD2736D71B0008FD8E /* LineProcessorV1.swift */,
+ 03DB1B1E2733F6A70026E647 /* FinderSyncExt-Bridging-Header.h */,
);
path = FinderSyncExt;
sourceTree = "<group>";
@@ -195,6 +200,7 @@
C2B573D61B1CD9CE00303B36 = {
CreatedOnToolsVersion = 6.3.1;
DevelopmentTeam = 9B5WD74GWJ;
+ LastSwiftMigration = 1310;
SystemCapabilities = {
com.apple.ApplicationGroups.Mac = {
enabled = 1;
@@ -275,8 +281,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- C2C932F01F0BFC6700C8BCB3 /* SyncClientProxy.m in Sources */,
+ 03DB1B202733F6A70026E647 /* LocalSocketClient.swift in Sources */,
C2B573DE1B1CD9CE00303B36 /* FinderSync.m in Sources */,
+ 03559BDE2736D71B0008FD8E /* LineProcessorV1.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -294,18 +301,21 @@
C2B573991B1CD88000303B36 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
};
name = Debug;
};
C2B5739A1B1CD88000303B36 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
};
name = Release;
};
C2B573CD1B1CD91E00303B36 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
@@ -343,7 +353,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = desktopclient/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
- MACOSX_DEPLOYMENT_TARGET = 10.10;
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -355,6 +365,7 @@
C2B573CE1B1CD91E00303B36 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
@@ -386,7 +397,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = desktopclient/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
- MACOSX_DEPLOYMENT_TARGET = 10.10;
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
@@ -434,7 +445,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = FinderSyncExt/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks";
- MACOSX_DEPLOYMENT_TARGET = 10.10;
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = YES;
OC_APPLICATION_NAME = ownCloud;
OC_APPLICATION_REV_DOMAIN = com.owncloud.desktopclient;
@@ -445,6 +456,9 @@
PROVISIONING_PROFILE = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
+ SWIFT_OBJC_BRIDGING_HEADER = "FinderSyncExt/FinderSyncExt-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
};
name = Debug;
};
@@ -483,7 +497,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = FinderSyncExt/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks";
- MACOSX_DEPLOYMENT_TARGET = 10.10;
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = NO;
OC_APPLICATION_NAME = ownCloud;
OC_APPLICATION_REV_DOMAIN = com.owncloud.desktopclient;
@@ -493,6 +507,8 @@
PROVISIONING_PROFILE = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
+ SWIFT_OBJC_BRIDGING_HEADER = "FinderSyncExt/FinderSyncExt-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
};
name = Release;
};
diff --git a/src/gui/guiutility_mac.mm b/src/gui/guiutility_mac.mm
index 65e05ab6a..0aa7e114e 100644
--- a/src/gui/guiutility_mac.mm
+++ b/src/gui/guiutility_mac.mm
@@ -19,6 +19,7 @@
#include <QProcess>
#import <Foundation/NSBundle.h>
+#import <Foundation/NSFileManager.h>
namespace OCC {
@@ -54,9 +55,12 @@ void Utility::startShellIntegration()
QString Utility::socketApiSocketPath()
{
// This must match the code signing Team setting of the extension
- // Example for developer builds (with ad-hoc signing identity): "" "com.owncloud.desktopclient" ".socketApi"
- // Example for official signed packages: "9B5WD74GWJ." "com.owncloud.desktopclient" ".socketApi"
- return QLatin1String(SOCKETAPI_TEAM_IDENTIFIER_PREFIX "." APPLICATION_REV_DOMAIN ".socketApi");
+ // Example for all builds: "9B5WD74GWJ" "." "com.owncloud.desktopclient"
+ NSString *appGroupId = @SOCKETAPI_TEAM_IDENTIFIER_PREFIX "." APPLICATION_REV_DOMAIN;
+
+ NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupId];
+ NSURL *socketPath = [container URLByAppendingPathComponent:@"GUI.socket" isDirectory:false];
+ return QString::fromNSString(socketPath.path);
}
} // namespace OCC
diff --git a/src/gui/socketapi/CMakeLists.txt b/src/gui/socketapi/CMakeLists.txt
index 78ce96cfd..0f28db92e 100644
--- a/src/gui/socketapi/CMakeLists.txt
+++ b/src/gui/socketapi/CMakeLists.txt
@@ -2,7 +2,3 @@ target_sources(owncloudCore PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/socketapi.cpp
${CMAKE_CURRENT_SOURCE_DIR}/socketuploadjob.cpp
)
-
-if( APPLE )
- target_sources(owncloudCore PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/socketapisocket_mac.mm)
-endif()
diff --git a/src/gui/socketapi/socketapi.cpp b/src/gui/socketapi/socketapi.cpp
index c1dad9184..e83166932 100644
--- a/src/gui/socketapi/socketapi.cpp
+++ b/src/gui/socketapi/socketapi.cpp
@@ -37,37 +37,30 @@
#include "syncfileitem.h"
#include "theme.h"
-#include <array>
+#include <QAction>
+#include <QApplication>
#include <QBitArray>
-#include <QUrl>
-#include <QMetaMethod>
-#include <QMetaObject>
-#include <QStringList>
-#include <QScopedPointer>
-#include <QFile>
+#include <QBuffer>
+#include <QClipboard>
#include <QDir>
-#include <QApplication>
-#include <QLocalSocket>
-#include <QStringBuilder>
-#include <QMessageBox>
+#include <QFile>
#include <QFileDialog>
-
-
-#include <QAction>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
-#include <QWidget>
-#include <QBuffer>
-
-#include <QClipboard>
-
+#include <QLocalSocket>
+#include <QMessageBox>
+#include <QMetaMethod>
+#include <QMetaObject>
#include <QProcess>
+#include <QScopedPointer>
#include <QStandardPaths>
+#include <QStringBuilder>
+#include <QStringList>
+#include <QUrl>
+#include <QWidget>
-#ifdef Q_OS_MAC
-#include <CoreFoundation/CoreFoundation.h>
-#endif
+#include <array>
// This is the version that is returned when the client asks for the VERSION.
diff --git a/src/gui/socketapi/socketapi.h b/src/gui/socketapi/socketapi.h
index 84229b1dd..1fc8e4546 100644
--- a/src/gui/socketapi/socketapi.h
+++ b/src/gui/socketapi/socketapi.h
@@ -22,7 +22,7 @@
#include "config.h"
-#if defined(Q_OS_MAC)
+#if defined(Q_OS_MAC) && 0
#include "socketapisocket_mac.h"
#else
#include <QLocalServer>
diff --git a/src/gui/socketapi/socketapisocket_mac.h b/src/gui/socketapi/socketapisocket_mac.h
deleted file mode 100644
index cbacdf028..000000000
--- a/src/gui/socketapi/socketapisocket_mac.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) by Jocelyn Turcotte <jturcotte@woboq.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#ifndef SOCKETAPISOCKET_OSX_H
-#define SOCKETAPISOCKET_OSX_H
-
-#include <QAbstractSocket>
-#include <QIODevice>
-
-class SocketApiServerPrivate;
-class SocketApiSocketPrivate;
-
-class SocketApiSocket : public QIODevice
-{
- Q_OBJECT
-public:
- SocketApiSocket(QObject *parent, SocketApiSocketPrivate *p);
- ~SocketApiSocket() override;
-
- qint64 readData(char *data, qint64 maxlen) override;
- qint64 writeData(const char *data, qint64 len) override;
-
- bool isSequential() const override { return true; }
- qint64 bytesAvailable() const override;
- bool canReadLine() const override;
-
-signals:
- void disconnected();
-
-private:
- // Use Qt's p-impl system to hide objective-c types from C++ code including this file
- Q_DECLARE_PRIVATE(SocketApiSocket)
- QScopedPointer<SocketApiSocketPrivate> d_ptr;
- friend class SocketApiServerPrivate;
-};
-
-class SocketApiServer : public QObject
-{
- Q_OBJECT
-public:
- SocketApiServer();
- ~SocketApiServer() override;
-
- void close();
- bool listen(const QString &name);
- SocketApiSocket *nextPendingConnection();
-
- static bool removeServer(const QString &) { return false; }
-
-signals:
- void newConnection();
-
-private:
- Q_DECLARE_PRIVATE(SocketApiServer)
- QScopedPointer<SocketApiServerPrivate> d_ptr;
-};
-
-#endif // SOCKETAPISOCKET_OSX_H
diff --git a/src/gui/socketapi/socketapisocket_mac.mm b/src/gui/socketapi/socketapisocket_mac.mm
deleted file mode 100644
index 938494e21..000000000
--- a/src/gui/socketapi/socketapisocket_mac.mm
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) by Jocelyn Turcotte <jturcotte@woboq.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#include "socketapisocket_mac.h"
-#import <Cocoa/Cocoa.h>
-
-@protocol ChannelProtocol <NSObject>
-
-- (void)sendMessage:(NSData *)msg;
-
-@end
-
-@protocol RemoteEndProtocol <NSObject, ChannelProtocol>
-
-- (void)registerTransmitter:(id)tx;
-
-@end
-
-@interface LocalEnd : NSObject <ChannelProtocol>
-
-@property (atomic) SocketApiSocketPrivate *wrapper;
-
-- (instancetype)initWithWrapper:(SocketApiSocketPrivate *)wrapper;
-
-@end
-
-@interface Server : NSObject
-
-@property (atomic) SocketApiServerPrivate *wrapper;
-
-- (instancetype)initWithWrapper:(SocketApiServerPrivate *)wrapper;
-- (void)registerClient:(NSDistantObject<RemoteEndProtocol> *)remoteEnd;
-
-@end
-
-class SocketApiSocketPrivate
-{
-public:
- SocketApiSocket *q_ptr;
-
- SocketApiSocketPrivate(NSDistantObject<ChannelProtocol> *remoteEnd);
- ~SocketApiSocketPrivate();
-
- // release remoteEnd
- void disconnectRemote();
-
- NSDistantObject<ChannelProtocol> *remoteEnd;
- LocalEnd *localEnd;
- QByteArray inBuffer;
- bool isRemoteDisconnected = false;
-};
-
-class SocketApiServerPrivate
-{
-public:
- SocketApiServer *q_ptr;
-
- SocketApiServerPrivate();
- ~SocketApiServerPrivate();
-
- QList<SocketApiSocket *> pendingConnections;
- NSConnection *connection;
- Server *server;
-};
-
-
-@implementation LocalEnd
-
-@synthesize wrapper = _wrapper;
-
-- (instancetype)initWithWrapper:(SocketApiSocketPrivate *)wrapper
-{
- self = [super init];
- self.wrapper = wrapper;
- return self;
-}
-
-- (void)sendMessage:(NSData *)msg
-{
- if (self.wrapper) {
- self.wrapper->inBuffer += QByteArray::fromRawNSData(msg);
- emit self.wrapper->q_ptr->readyRead();
- }
-}
-
-- (void)connectionDidDie:(NSNotification *)notification
-{
- // The NSConnectionDidDieNotification docs say to disconnect from NSConnection here
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-
- if (self.wrapper) {
- self.wrapper->disconnectRemote();
- emit self.wrapper->q_ptr->disconnected();
- }
-}
-@end
-
-@implementation Server
-
-@synthesize wrapper = _wrapper;
-
-- (instancetype)initWithWrapper:(SocketApiServerPrivate *)wrapper
-{
- self = [super init];
- self.wrapper = wrapper;
- return self;
-}
-
-- (void)registerClient:(NSDistantObject<RemoteEndProtocol> *)remoteEnd
-{
- // This saves a few mach messages that would otherwise be needed to query the interface
- [remoteEnd setProtocolForProxy:@protocol(RemoteEndProtocol)];
-
- SocketApiServer *server = self.wrapper->q_ptr;
- SocketApiSocketPrivate *socketPrivate = new SocketApiSocketPrivate(remoteEnd);
- SocketApiSocket *socket = new SocketApiSocket(server, socketPrivate);
- self.wrapper->pendingConnections.append(socket);
- emit server->newConnection();
-
- [remoteEnd registerTransmitter:socketPrivate->localEnd];
-}
-@end
-
-
-SocketApiSocket::SocketApiSocket(QObject *parent, SocketApiSocketPrivate *p)
- : QIODevice(parent)
- , d_ptr(p)
-{
- Q_D(SocketApiSocket);
- d->q_ptr = this;
- open(ReadWrite);
-}
-
-SocketApiSocket::~SocketApiSocket()
-{
-}
-
-qint64 SocketApiSocket::readData(char *data, qint64 maxlen)
-{
- Q_D(SocketApiSocket);
- qint64 len = std::min(maxlen, static_cast<qint64>(d->inBuffer.size()));
- if (len < 0 || len > std::numeric_limits<int>::max()) {
- return -1;
- }
-
- memcpy(data, d->inBuffer.constData(), static_cast<size_t>(len));
- d->inBuffer.remove(0, static_cast<int>(len));
- return len;
-}
-
-qint64 SocketApiSocket::writeData(const char *data, qint64 len)
-{
- Q_D(SocketApiSocket);
- if (d->isRemoteDisconnected) {
- return -1;
- }
-
- if (len < std::numeric_limits<NSUInteger>::min() || len > std::numeric_limits<NSUInteger>::max()) {
- return -1;
- }
-
- @try {
- // FIXME: The NSConnection will make this block unless the function is marked as "oneway"
- // in the protocol. This isn't async and reduces our performances but this currectly avoids
- // a Mach queue deadlock during requests bursts of the legacy OwnCloudFinder extension.
- // Since FinderSync already runs in a separate process, blocking isn't too critical.
- NSData *payload = QByteArray::fromRawData(data, static_cast<int>(len)).toRawNSData();
- [d->remoteEnd sendMessage:payload];
- return len;
- } @catch (NSException *) {
- // connectionDidDie can be notified too late, also interpret any sending exception as a disconnection.
- d->disconnectRemote();
- emit disconnected();
- return -1;
- }
-}
-
-qint64 SocketApiSocket::bytesAvailable() const
-{
- Q_D(const SocketApiSocket);
- return d->inBuffer.size() + QIODevice::bytesAvailable();
-}
-
-bool SocketApiSocket::canReadLine() const
-{
- Q_D(const SocketApiSocket);
- return d->inBuffer.indexOf('\n', int(pos())) != -1 || QIODevice::canReadLine();
-}
-
-SocketApiSocketPrivate::SocketApiSocketPrivate(NSDistantObject<ChannelProtocol> *remoteEnd)
- : remoteEnd(remoteEnd)
- , localEnd([[LocalEnd alloc] initWithWrapper:this])
-{
- [remoteEnd retain];
- // (Ab)use our objective-c object just to catch the notification
- [[NSNotificationCenter defaultCenter] addObserver:localEnd
- selector:@selector(connectionDidDie:)
- name:NSConnectionDidDieNotification
- object:[remoteEnd connectionForProxy]];
-}
-
-SocketApiSocketPrivate::~SocketApiSocketPrivate()
-{
- disconnectRemote();
-
- // The DO vended localEnd might still be referenced by the connection
- localEnd.wrapper = nil;
- [localEnd release];
-}
-
-void SocketApiSocketPrivate::disconnectRemote()
-{
- if (isRemoteDisconnected)
- return;
- isRemoteDisconnected = true;
-
- [remoteEnd release];
-}
-
-SocketApiServer::SocketApiServer()
- : d_ptr(new SocketApiServerPrivate)
-{
- Q_D(SocketApiServer);
- d->q_ptr = this;
-}
-
-SocketApiServer::~SocketApiServer()
-{
-}
-
-void SocketApiServer::close()
-{
- // Assume we'll be destroyed right after
-}
-
-bool SocketApiServer::listen(const QString &name)
-{
- Q_D(SocketApiServer);
- // Set the name of the root object
- return [d->connection registerName:name.toNSString()];
-}
-
-SocketApiSocket *SocketApiServer::nextPendingConnection()
-{
- Q_D(SocketApiServer);
- return d->pendingConnections.takeFirst();
-}
-
-SocketApiServerPrivate::SocketApiServerPrivate()
-{
- // Create the connection and server object to vend over Disributed Objects
- connection = [[NSConnection alloc] init];
- server = [[Server alloc] initWithWrapper:this];
- [connection setRootObject:server];
-}
-
-SocketApiServerPrivate::~SocketApiServerPrivate()
-{
- [connection release];
- server.wrapper = nil;
- [server release];
-}