mirror of https://github.com/nextcloud/desktop
Implement local socket to communicate with finder extension
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
This commit is contained in:
parent
6aefe8f2e3
commit
628ee10008
|
@ -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>
|
|
@ -15,15 +15,20 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <FinderSync/FinderSync.h>
|
||||
#import "SyncClientProxy.h"
|
||||
#import "SyncClient.h"
|
||||
#import "LineProcessor.h"
|
||||
#import "LocalSocketClient.h"
|
||||
|
||||
@interface FinderSync : FIFinderSync <SyncClientProxyDelegate>
|
||||
@interface FinderSync : FIFinderSync <SyncClientDelegate>
|
||||
{
|
||||
SyncClientProxy *_syncClientProxy;
|
||||
NSMutableSet *_registeredDirectories;
|
||||
NSString *_shareMenuTitle;
|
||||
NSMutableDictionary *_strings;
|
||||
NSMutableArray *_menuItems;
|
||||
NSMutableSet *_registeredDirectories;
|
||||
NSString *_shareMenuTitle;
|
||||
NSMutableDictionary *_strings;
|
||||
NSMutableArray *_menuItems;
|
||||
NSCondition *_menuIsComplete;
|
||||
}
|
||||
|
||||
@property LineProcessor *lineProcessor;
|
||||
@property LocalSocketClient *localSocketClient;
|
||||
|
||||
@end
|
||||
|
|
|
@ -48,21 +48,32 @@
|
|||
// - 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
|
||||
// We end up in the official signed client with: 9B5WD74GWJ.com.owncloud.desktopclient.socket
|
||||
// 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);
|
||||
|
||||
NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:socketApiPrefix];
|
||||
NSURL *socketPath = [container URLByAppendingPathComponent:@".socket" isDirectory:NO];
|
||||
|
||||
NSLog(@"Socket path: %@", socketPath.path);
|
||||
|
||||
_syncClientProxy = [[SyncClientProxy alloc] initWithDelegate:self serverName:serverName];
|
||||
_registeredDirectories = [[NSMutableSet alloc] init];
|
||||
_strings = [[NSMutableDictionary alloc] init];
|
||||
if (socketPath.path) {
|
||||
self.lineProcessor = [[LineProcessor alloc] initWithDelegate:self];
|
||||
self.localSocketClient = [[LocalSocketClient alloc] init:socketPath.path
|
||||
lineProcessor:self.lineProcessor];
|
||||
[self.localSocketClient start];
|
||||
} else {
|
||||
NSLog(@"No socket path. Not initiating local socket client.");
|
||||
self.localSocketClient = nil;
|
||||
}
|
||||
_registeredDirectories = [[NSMutableSet alloc] init];
|
||||
_strings = [[NSMutableDictionary alloc] init];
|
||||
_menuIsComplete = [[NSCondition alloc] init];
|
||||
|
||||
[_syncClientProxy start];
|
||||
return self;
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Primary Finder Sync protocol methods
|
||||
|
@ -76,7 +87,7 @@
|
|||
}
|
||||
|
||||
NSString* normalizedPath = [[url path] decomposedStringWithCanonicalMapping];
|
||||
[_syncClientProxy askForIcon:normalizedPath isDirectory:isDir];
|
||||
[self.localSocketClient askForIcon:normalizedPath isDirectory:isDir];
|
||||
}
|
||||
|
||||
#pragma mark - Menu and toolbar item support
|
||||
|
@ -95,8 +106,19 @@
|
|||
return string;
|
||||
}
|
||||
|
||||
- (void)waitForMenuToArrive
|
||||
{
|
||||
[self->_menuIsComplete lock];
|
||||
[self->_menuIsComplete wait];
|
||||
[self->_menuIsComplete unlock];
|
||||
}
|
||||
|
||||
- (NSMenu *)menuForMenuKind:(FIMenuKind)whichMenu
|
||||
{
|
||||
if(![self.localSocketClient isConnected]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
FIFinderSyncController *syncController = [FIFinderSyncController defaultController];
|
||||
NSMutableSet *rootPaths = [[NSMutableSet alloc] init];
|
||||
[syncController.directoryURLs enumerateObjectsUsingBlock: ^(id obj, BOOL *stop) {
|
||||
|
@ -116,8 +138,11 @@
|
|||
}];
|
||||
|
||||
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"];
|
||||
[self.localSocketClient askOnSocket:paths query:@"GET_MENU_ITEMS"];
|
||||
|
||||
// Since the LocalSocketClient communicates asynchronously. wait here until the menu
|
||||
// is delivered by another thread
|
||||
[self waitForMenuToArrive];
|
||||
|
||||
id contextMenuTitle = [_strings objectForKey:@"CONTEXT_MENU_TITLE"];
|
||||
if (contextMenuTitle && !onlyRootsSelected) {
|
||||
|
@ -151,7 +176,7 @@
|
|||
long idx = [(NSMenuItem*)sender tag];
|
||||
NSString *command = [[_menuItems objectAtIndex:idx] valueForKey:@"command"];
|
||||
NSString *paths = [self selectedPathsSeparatedByRecordSeparator];
|
||||
[_syncClientProxy askOnSocket:paths query:command];
|
||||
[self.localSocketClient askOnSocket:paths query:command];
|
||||
}
|
||||
|
||||
#pragma mark - SyncClientProxyDelegate implementation
|
||||
|
@ -164,6 +189,7 @@
|
|||
|
||||
- (void)reFetchFileNameCacheForPath:(NSString*)path
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)registerPath:(NSString*)path
|
||||
|
@ -189,9 +215,16 @@
|
|||
_menuItems = [[NSMutableArray alloc] init];
|
||||
}
|
||||
- (void)addMenuItem:(NSDictionary *)item {
|
||||
NSLog(@"Adding menu item.");
|
||||
[_menuItems addObject:item];
|
||||
}
|
||||
|
||||
- (void)menuHasCompleted
|
||||
{
|
||||
NSLog(@"Emitting menu is complete signal now.");
|
||||
[self->_menuIsComplete signal];
|
||||
}
|
||||
|
||||
- (void)connectionDidDie
|
||||
{
|
||||
[_strings removeAllObjects];
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.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 "SyncClient.h"
|
||||
|
||||
#ifndef LineProcessor_h
|
||||
#define LineProcessor_h
|
||||
|
||||
/// This class is in charge of dispatching all work that must be done on the UI side of the extension.
|
||||
/// Tasks are dispatched on the main UI thread for this reason.
|
||||
///
|
||||
/// These tasks are parsed from byte data (UTF9 strings) acquired from the socket; look at the
|
||||
/// LocalSocketClient for more detail on how data is read from and written to the socket.
|
||||
|
||||
@interface LineProcessor : NSObject
|
||||
@property(nonatomic, weak)id<SyncClientDelegate> delegate;
|
||||
|
||||
- (instancetype)initWithDelegate:(id<SyncClientDelegate>)delegate;
|
||||
- (void)process:(NSString*)line;
|
||||
|
||||
@end
|
||||
#endif /* LineProcessor_h */
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.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>
|
||||
#import "LineProcessor.h"
|
||||
|
||||
@implementation LineProcessor
|
||||
|
||||
-(instancetype)initWithDelegate:(id<SyncClientDelegate>)delegate
|
||||
{
|
||||
NSLog(@"Init line processor with delegate.");
|
||||
self.delegate = delegate;
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)process:(NSString*)line
|
||||
{
|
||||
NSLog(@"Processing line: %@", line);
|
||||
NSArray *split = [line componentsSeparatedByString:@":"];
|
||||
NSString *command = [split objectAtIndex:0];
|
||||
|
||||
NSLog(@"Command: %@", command);
|
||||
|
||||
if([command isEqualToString:@"STATUS"]) {
|
||||
NSString *result = [split objectAtIndex:1];
|
||||
NSArray *pathSplit = [split subarrayWithRange:NSMakeRange(2, [split count] - 2)]; // Get everything after location 2
|
||||
NSString *path = [pathSplit componentsJoinedByString:@":"];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"Setting result %@ for path %@", result, path);
|
||||
[self.delegate setResultForPath:path result:result];
|
||||
});
|
||||
} else if([command isEqualToString:@"UPDATE_VIEW"]) {
|
||||
NSString *path = [split objectAtIndex:1];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"Re-fetching filename cache for path %@", path);
|
||||
[self.delegate reFetchFileNameCacheForPath:path];
|
||||
});
|
||||
} else if([command isEqualToString:@"REGISTER_PATH"]) {
|
||||
NSString *path = [split objectAtIndex:1];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"Registering path %@", path);
|
||||
[self.delegate registerPath:path];
|
||||
});
|
||||
} else if([command isEqualToString:@"UNREGISTER_PATH"]) {
|
||||
NSString *path = [split objectAtIndex:1];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"Unregistering path %@", path);
|
||||
[self.delegate unregisterPath:path];
|
||||
});
|
||||
} else if([command isEqualToString:@"GET_STRINGS"]) {
|
||||
// BEGIN and END messages, do nothing.
|
||||
return;
|
||||
} else if([command isEqualToString:@"STRING"]) {
|
||||
NSString *key = [split objectAtIndex:1];
|
||||
NSString *value = [split objectAtIndex:2];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"Setting string %@ to value %@", key, value);
|
||||
[self.delegate setString:key value:value];
|
||||
});
|
||||
} else if([command isEqualToString:@"GET_MENU_ITEMS"]) {
|
||||
if([[split objectAtIndex:1] isEqualToString:@"BEGIN"]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"Resetting menu items.");
|
||||
[self.delegate resetMenuItems];
|
||||
});
|
||||
} else {
|
||||
NSLog(@"Emitting menu has completed signal.");
|
||||
[self.delegate menuHasCompleted];
|
||||
}
|
||||
} else if([command isEqualToString:@"MENU_ITEM"]) {
|
||||
NSDictionary *item = @{@"command": [split objectAtIndex:1], @"flags": [split objectAtIndex:2], @"text": [split objectAtIndex:3]};
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"Adding menu item with command %@, flags %@, and text %@", [split objectAtIndex:1], [split objectAtIndex:2], [split objectAtIndex:3]);
|
||||
[self.delegate addMenuItem:item];
|
||||
});
|
||||
} else {
|
||||
// LOG UNKOWN COMMAND
|
||||
NSLog(@"Unkown command: %@", command);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.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 "LineProcessor.h"
|
||||
|
||||
#ifndef LocalSocketClient_h
|
||||
#define LocalSocketClient_h
|
||||
#define BUF_SIZE 4096
|
||||
|
||||
/// Class handling 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. 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 `LineProcessor` class dispatches 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`.
|
||||
|
||||
@interface LocalSocketClient : NSObject
|
||||
|
||||
@property NSString* socketPath;
|
||||
@property LineProcessor* lineProcessor;
|
||||
@property int sock;
|
||||
@property dispatch_queue_t localSocketQueue;
|
||||
@property dispatch_source_t readSource;
|
||||
@property dispatch_source_t writeSource;
|
||||
@property NSMutableData* inBuffer;
|
||||
@property NSMutableData* outBuffer;
|
||||
|
||||
- (instancetype)init:(NSString*)socketPath lineProcessor:(LineProcessor*)lineProcessor;
|
||||
- (BOOL)isConnected;
|
||||
- (void)start;
|
||||
- (void)restart;
|
||||
- (void)closeConnection;
|
||||
- (NSString*)strErr;
|
||||
- (void)askOnSocket:(NSString*)path query:(NSString*)verb;
|
||||
- (void)askForIcon:(NSString*)path isDirectory:(BOOL)isDirectory;
|
||||
- (void)readFromSocket;
|
||||
- (void)writeToSocket;
|
||||
- (void)processInBuffer;
|
||||
|
||||
@end
|
||||
#endif /* LocalSocketClient_h */
|
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.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>
|
||||
#import "LocalSocketClient.h"
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
@implementation LocalSocketClient
|
||||
|
||||
- (instancetype)init:(NSString*)socketPath lineProcessor:(LineProcessor*)lineProcessor
|
||||
{
|
||||
NSLog(@"Initiating local socket client.");
|
||||
self = [super init];
|
||||
|
||||
if(self) {
|
||||
self.socketPath = socketPath;
|
||||
self.lineProcessor = lineProcessor;
|
||||
|
||||
self.sock = -1;
|
||||
self.localSocketQueue = dispatch_queue_create("localSocketQueue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
self.inBuffer = [NSMutableData data];
|
||||
self.outBuffer = [NSMutableData data];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isConnected
|
||||
{
|
||||
NSLog(@"Checking is connected: %@", self.sock != -1 ? @"YES" : @"NO");
|
||||
return self.sock != -1;
|
||||
}
|
||||
|
||||
- (void)start
|
||||
{
|
||||
if([self isConnected]) {
|
||||
NSLog(@"Socket client already connected. Not starting.");
|
||||
return;
|
||||
}
|
||||
|
||||
struct sockaddr_un localSocketAddr;
|
||||
unsigned long socketPathByteCount = [self.socketPath lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; // add 1 for the NUL terminator char
|
||||
int maxByteCount = sizeof(localSocketAddr.sun_path);
|
||||
|
||||
if(socketPathByteCount > maxByteCount) {
|
||||
// LOG THAT THE SOCKET PATH IS TOO LONG HERE
|
||||
NSLog(@"Socket path '%@' is too long: maximum socket path length is %i, this path is of length %lu", self.socketPath, maxByteCount, socketPathByteCount);
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"Opening local socket...");
|
||||
|
||||
// LOG THAT THE SOCKET IS BEING OPENED HERE
|
||||
self.sock = socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||
|
||||
if(self.sock == -1) {
|
||||
NSLog(@"Cannot open socket: '%@'", [self strErr]);
|
||||
[self restart];
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"Local socket opened. Connecting to '%@' ...", self.socketPath);
|
||||
|
||||
localSocketAddr.sun_family = AF_LOCAL & 0xff;
|
||||
|
||||
const char* pathBytes = [self.socketPath UTF8String];
|
||||
strcpy(localSocketAddr.sun_path, pathBytes);
|
||||
|
||||
int connectionStatus = connect(self.sock, (struct sockaddr*)&localSocketAddr, sizeof(localSocketAddr));
|
||||
|
||||
if(connectionStatus == -1) {
|
||||
NSLog(@"Could not connect to '%@': '%@'", self.socketPath, [self strErr]);
|
||||
[self restart];
|
||||
return;
|
||||
}
|
||||
|
||||
int flags = fcntl(self.sock, F_GETFL, 0);
|
||||
|
||||
if(fcntl(self.sock, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||
NSLog(@"Could not set socket to non-blocking mode: '%@'", [self strErr]);
|
||||
[self restart];
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"Connected to socket. Setting up dispatch sources...");
|
||||
|
||||
self.readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, self.sock, 0, self.localSocketQueue);
|
||||
dispatch_source_set_event_handler(self.readSource, ^(void){ [self readFromSocket]; });
|
||||
dispatch_source_set_cancel_handler(self.readSource, ^(void){
|
||||
self.readSource = nil;
|
||||
[self closeConnection];
|
||||
});
|
||||
|
||||
self.writeSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, self.sock, 0, self.localSocketQueue);
|
||||
dispatch_source_set_event_handler(self.writeSource, ^(void){ [self writeToSocket]; });
|
||||
dispatch_source_set_cancel_handler(self.writeSource, ^(void){
|
||||
self.writeSource = nil;
|
||||
[self closeConnection];
|
||||
});
|
||||
|
||||
// These dispatch sources are suspended upon creation.
|
||||
// We resume the writeSource when we actually have something to write, suspending it again once our outBuffer is empty.
|
||||
// We start the readSource now.
|
||||
|
||||
NSLog(@"Starting to read from socket");
|
||||
|
||||
dispatch_resume(self.readSource);
|
||||
[self askOnSocket:@"" query:@"GET_STRINGS"];
|
||||
}
|
||||
|
||||
- (void)restart
|
||||
{
|
||||
NSLog(@"Restarting connection to socket.");
|
||||
[self closeConnection];
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void){
|
||||
[NSTimer scheduledTimerWithTimeInterval:5 repeats:NO block:^(NSTimer* timer) {
|
||||
[self start];
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)closeConnection
|
||||
{
|
||||
NSLog(@"Closing connection.");
|
||||
dispatch_source_cancel(self.readSource);
|
||||
dispatch_source_cancel(self.writeSource);
|
||||
self.readSource = nil;
|
||||
self.writeSource = nil;
|
||||
[self.inBuffer setLength:0];
|
||||
[self.outBuffer setLength: 0];
|
||||
|
||||
if(self.sock != -1) {
|
||||
close(self.sock);
|
||||
self.sock = -1;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)strErr
|
||||
{
|
||||
int err = errno;
|
||||
const char *errStr = strerror(err);
|
||||
NSString *errorStr = [NSString stringWithUTF8String:errStr];
|
||||
|
||||
if([errorStr length] == 0) {
|
||||
return errorStr;
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"Unknown error code: %i", err];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)askOnSocket:(NSString *)path query:(NSString *)verb
|
||||
{
|
||||
NSString *line = [NSString stringWithFormat:@"%@:%@\n", verb, path];
|
||||
dispatch_async(self.localSocketQueue, ^(void) {
|
||||
if(![self isConnected]) {
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL writeSourceIsSuspended = [self.outBuffer length] == 0;
|
||||
|
||||
[self.outBuffer appendData:[line dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
NSLog(@"Writing to out buffer: '%@'", line);
|
||||
NSLog(@"Out buffer now %li bytes", [self.outBuffer length]);
|
||||
|
||||
if(writeSourceIsSuspended) {
|
||||
NSLog(@"Resuming write dispatch source.");
|
||||
dispatch_resume(self.writeSource);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)writeToSocket
|
||||
{
|
||||
if(![self isConnected]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if([self.outBuffer length] == 0) {
|
||||
NSLog(@"Empty out buffer, suspending write dispatch source.");
|
||||
dispatch_suspend(self.writeSource);
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"About to write %li bytes from outbuffer to socket.", [self.outBuffer length]);
|
||||
|
||||
long bytesWritten = write(self.sock, [self.outBuffer bytes], [self.outBuffer length]);
|
||||
char lineWritten[4096];
|
||||
memcpy(lineWritten, [self.outBuffer bytes], [self.outBuffer length]);
|
||||
NSLog(@"Wrote %li bytes to socket. Line was: '%@'", bytesWritten, [NSString stringWithUTF8String:lineWritten]);
|
||||
|
||||
if(bytesWritten == 0) {
|
||||
// 0 means we reached "end of file" and thus the socket was closed. So let's restart it
|
||||
NSLog(@"Socket was closed. Restarting...");
|
||||
[self restart];
|
||||
} else if(bytesWritten == -1) {
|
||||
int err = errno; // Make copy before it gets nuked by something else
|
||||
|
||||
if(err == EAGAIN || err == EWOULDBLOCK) {
|
||||
// No free space in the OS' buffer, nothing to do here
|
||||
NSLog(@"No free space in OS buffer. Ending write.");
|
||||
return;
|
||||
} else {
|
||||
NSLog(@"Error writing to local socket: '%@'", [self strErr]);
|
||||
[self restart];
|
||||
}
|
||||
} else if(bytesWritten > 0) {
|
||||
[self.outBuffer replaceBytesInRange:NSMakeRange(0, bytesWritten) withBytes:NULL length:0];
|
||||
|
||||
NSLog(@"Out buffer cleared. Now count is %li bytes.", [self.outBuffer length]);
|
||||
|
||||
if([self.outBuffer length] == 0) {
|
||||
NSLog(@"Out buffer has been emptied, suspending write dispatch source.");
|
||||
dispatch_suspend(self.writeSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)askForIcon:(NSString*)path isDirectory:(BOOL)isDirectory;
|
||||
{
|
||||
NSLog(@"Asking for icon.");
|
||||
|
||||
NSString *verb;
|
||||
if(isDirectory) {
|
||||
verb = @"RETRIEVE_FOLDER_STATUS";
|
||||
} else {
|
||||
verb = @"RETRIEVE_FILE_STATUS";
|
||||
}
|
||||
|
||||
[self askOnSocket:path query:verb];
|
||||
}
|
||||
|
||||
- (void)readFromSocket
|
||||
{
|
||||
if(![self isConnected]) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"Reading from socket.");
|
||||
|
||||
int bufferLength = BUF_SIZE / 2;
|
||||
char buffer[bufferLength];
|
||||
|
||||
while(true) {
|
||||
long bytesRead = read(self.sock, buffer, bufferLength);
|
||||
|
||||
NSLog(@"Read %li bytes from socket.", bytesRead);
|
||||
|
||||
if(bytesRead == 0) {
|
||||
// 0 means we reached "end of file" and thus the socket was closed. So let's restart it
|
||||
NSLog(@"Socket was closed. Restarting...");
|
||||
[self restart];
|
||||
return;
|
||||
} else if(bytesRead == -1) {
|
||||
int err = errno;
|
||||
if(err == EAGAIN) {
|
||||
NSLog(@"No error and no data. Stopping.");
|
||||
return; // No error, no data, so let's stop
|
||||
} else {
|
||||
NSLog(@"Error reading from local socket: '%@'", [self strErr]);
|
||||
[self closeConnection];
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
[self.inBuffer appendBytes:buffer length:bytesRead];
|
||||
[self processInBuffer];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)processInBuffer
|
||||
{
|
||||
NSLog(@"Processing in buffer. In buffer length %li", [self.inBuffer length]);
|
||||
UInt8 separator[] = {0xa}; // Byte value for "\n"
|
||||
while(true) {
|
||||
NSRange firstSeparatorIndex = [self.inBuffer rangeOfData:[NSData dataWithBytes:separator length:1] options:0 range:NSMakeRange(0, [self.inBuffer length])];
|
||||
|
||||
if(firstSeparatorIndex.location == NSNotFound) {
|
||||
NSLog(@"No separator found. Stopping.");
|
||||
return; // No separator, nope out
|
||||
} else {
|
||||
unsigned char *buffer = [self.inBuffer mutableBytes];
|
||||
buffer[firstSeparatorIndex.location] = 0; // Add NULL terminator, so we can use C string methods
|
||||
|
||||
NSString *newLine = [NSString stringWithUTF8String:[self.inBuffer bytes]];
|
||||
|
||||
[self.inBuffer replaceBytesInRange:NSMakeRange(0, firstSeparatorIndex.location + 1) withBytes:NULL length:0];
|
||||
[self.lineProcessor process:newLine];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -7,6 +7,8 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
539158AC27BE71A900816F56 /* LineProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 539158AB27BE71A900816F56 /* LineProcessor.m */; };
|
||||
539158B327BEC98A00816F56 /* LocalSocketClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 539158B227BEC98A00816F56 /* LocalSocketClient.m */; };
|
||||
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,11 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
539158A927BE606500816F56 /* LineProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LineProcessor.h; sourceTree = "<group>"; };
|
||||
539158AA27BE67CC00816F56 /* SyncClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SyncClient.h; sourceTree = "<group>"; };
|
||||
539158AB27BE71A900816F56 /* LineProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LineProcessor.m; sourceTree = "<group>"; };
|
||||
539158B127BE891500816F56 /* LocalSocketClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LocalSocketClient.h; sourceTree = "<group>"; };
|
||||
539158B227BEC98A00816F56 /* LocalSocketClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocalSocketClient.m; 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 +63,6 @@
|
|||
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>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -117,10 +121,13 @@
|
|||
C2B573D81B1CD9CE00303B36 /* FinderSyncExt */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C2C932EE1F0BFC6700C8BCB3 /* SyncClientProxy.h */,
|
||||
C2C932EF1F0BFC6700C8BCB3 /* SyncClientProxy.m */,
|
||||
539158AA27BE67CC00816F56 /* SyncClient.h */,
|
||||
C2B573DC1B1CD9CE00303B36 /* FinderSync.h */,
|
||||
C2B573DD1B1CD9CE00303B36 /* FinderSync.m */,
|
||||
539158A927BE606500816F56 /* LineProcessor.h */,
|
||||
539158AB27BE71A900816F56 /* LineProcessor.m */,
|
||||
539158B127BE891500816F56 /* LocalSocketClient.h */,
|
||||
539158B227BEC98A00816F56 /* LocalSocketClient.m */,
|
||||
C2B573D91B1CD9CE00303B36 /* Supporting Files */,
|
||||
);
|
||||
path = FinderSyncExt;
|
||||
|
@ -186,7 +193,7 @@
|
|||
C2B573951B1CD88000303B36 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0630;
|
||||
LastUpgradeCheck = 1240;
|
||||
TargetAttributes = {
|
||||
C2B573B01B1CD91E00303B36 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
|
@ -208,6 +215,7 @@
|
|||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
|
@ -275,7 +283,8 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C2C932F01F0BFC6700C8BCB3 /* SyncClientProxy.m in Sources */,
|
||||
539158B327BEC98A00816F56 /* LocalSocketClient.m in Sources */,
|
||||
539158AC27BE71A900816F56 /* LineProcessor.m in Sources */,
|
||||
C2B573DE1B1CD9CE00303B36 /* FinderSync.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -294,12 +303,65 @@
|
|||
C2B573991B1CD88000303B36 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
C2B5739A1B1CD88000303B36 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0630"
|
||||
LastUpgradeVersion = "1240"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
|
@ -38,12 +38,10 @@
|
|||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
</Testables>
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
|
@ -53,15 +51,19 @@
|
|||
ReferencedContainer = "container:OwnCloudFinderSync.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
|
@ -74,14 +76,12 @@
|
|||
ReferencedContainer = "container:OwnCloudFinderSync.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
|
|
|
@ -6,5 +6,5 @@ target_sources(nextcloudCore PRIVATE
|
|||
)
|
||||
|
||||
if( APPLE )
|
||||
target_sources(nextcloudCore PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/socketapisocket_mac.mm)
|
||||
target_sources(nextcloudCore PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/socketapi_mac.mm)
|
||||
endif()
|
||||
|
|
|
@ -237,11 +237,8 @@ SocketApi::SocketApi(QObject *parent)
|
|||
// See issue #2388
|
||||
// + Theme::instance()->appName();
|
||||
} else if (Utility::isMac()) {
|
||||
// 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"
|
||||
socketPath = SOCKETAPI_TEAM_IDENTIFIER_PREFIX APPLICATION_REV_DOMAIN ".socketApi";
|
||||
#ifdef Q_OS_MAC
|
||||
#ifdef Q_OS_MACOS
|
||||
socketPath = socketApiSocketPath();
|
||||
CFURLRef url = (CFURLRef)CFAutorelease((CFURLRef)CFBundleCopyBundleURL(CFBundleGetMainBundle()));
|
||||
QString bundlePath = QUrl::fromCFURL(url).path();
|
||||
|
||||
|
@ -269,14 +266,19 @@ SocketApi::SocketApi(QObject *parent)
|
|||
qCWarning(lcSocketApi) << "An unexpected system detected, this probably won't work.";
|
||||
}
|
||||
|
||||
SocketApiServer::removeServer(socketPath);
|
||||
QFileInfo info(socketPath);
|
||||
if (!info.dir().exists()) {
|
||||
bool result = info.dir().mkpath(".");
|
||||
qCDebug(lcSocketApi) << "creating" << info.dir().path() << result;
|
||||
if (result) {
|
||||
QFile::setPermissions(socketPath,
|
||||
QFile::Permissions(QFile::ReadOwner + QFile::WriteOwner + QFile::ExeOwner));
|
||||
QLocalServer::removeServer(socketPath);
|
||||
// Create the socket path:
|
||||
if (!Utility::isMac()) {
|
||||
// Not on macOS: there the directory is there, and created for us by the sandboxing
|
||||
// environment, because we belong to an App Group.
|
||||
QFileInfo info(socketPath);
|
||||
if (!info.dir().exists()) {
|
||||
bool result = info.dir().mkpath(".");
|
||||
qCDebug(lcSocketApi) << "creating" << info.dir().path() << result;
|
||||
if (result) {
|
||||
QFile::setPermissions(socketPath,
|
||||
QFile::Permissions(QFile::ReadOwner + QFile::WriteOwner + QFile::ExeOwner));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!_localServer.listen(socketPath)) {
|
||||
|
@ -285,7 +287,7 @@ SocketApi::SocketApi(QObject *parent)
|
|||
qCInfo(lcSocketApi) << "server started, listening at " << socketPath;
|
||||
}
|
||||
|
||||
connect(&_localServer, &SocketApiServer::newConnection, this, &SocketApi::slotNewConnection);
|
||||
connect(&_localServer, &QLocalServer::newConnection, this, &SocketApi::slotNewConnection);
|
||||
|
||||
// folder watcher
|
||||
connect(FolderMan::instance(), &FolderMan::folderSyncStateChange, this, &SocketApi::slotUpdateFolderView);
|
||||
|
@ -302,8 +304,6 @@ SocketApi::~SocketApi()
|
|||
|
||||
void SocketApi::slotNewConnection()
|
||||
{
|
||||
// Note that on macOS this is not actually a line-based QIODevice, it's a SocketApiSocket which is our
|
||||
// custom message based macOS IPC.
|
||||
QIODevice *socket = _localServer.nextPendingConnection();
|
||||
|
||||
if (!socket) {
|
||||
|
|
|
@ -22,12 +22,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
#include "socketapisocket_mac.h"
|
||||
#else
|
||||
#include <QLocalServer>
|
||||
using SocketApiServer = QLocalServer;
|
||||
#endif
|
||||
|
||||
class QUrl;
|
||||
class QLocalSocket;
|
||||
|
@ -44,6 +39,10 @@ class SocketApiJobV2;
|
|||
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcSocketApi)
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
QString socketApiSocketPath();
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The SocketApi class
|
||||
* @ingroup gui
|
||||
|
@ -173,7 +172,7 @@ private:
|
|||
|
||||
QSet<QString> _registeredAliases;
|
||||
QMap<QIODevice *, QSharedPointer<SocketListener>> _listeners;
|
||||
SocketApiServer _localServer;
|
||||
QLocalServer _localServer;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.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 <Cocoa/Cocoa.h>
|
||||
#import <QString>
|
||||
|
||||
#include "application.h"
|
||||
|
||||
namespace OCC
|
||||
{
|
||||
|
||||
QString socketApiSocketPath()
|
||||
{
|
||||
// This must match the code signing Team setting of the extension
|
||||
// Example for developer builds (with ad-hoc signing identity): "" "com.owncloud.desktopclient" ".socket"
|
||||
// Example for official signed packages: "9B5WD74GWJ." "com.owncloud.desktopclient" ".socket"
|
||||
NSString *appGroupId = @SOCKETAPI_TEAM_IDENTIFIER_PREFIX APPLICATION_REV_DOMAIN;
|
||||
|
||||
NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupId];
|
||||
NSURL *socketPath = [container URLByAppendingPathComponent:@".socket" isDirectory:false];
|
||||
return QString::fromNSString(socketPath.path);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
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();
|
||||
|
||||
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
|
|
@ -1,245 +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 SocketApiSocketPrivate *wrapper;
|
||||
- (instancetype)initWithWrapper:(SocketApiSocketPrivate *)wrapper;
|
||||
@end
|
||||
|
||||
@interface Server : NSObject
|
||||
@property 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
|
||||
- (instancetype)initWithWrapper:(SocketApiSocketPrivate *)wrapper
|
||||
{
|
||||
self = [super init];
|
||||
self->_wrapper = wrapper;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)sendMessage:(NSData *)msg
|
||||
{
|
||||
if (_wrapper) {
|
||||
_wrapper->inBuffer += QByteArray::fromRawNSData(msg);
|
||||
emit _wrapper->q_ptr->readyRead();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connectionDidDie:(NSNotification *)notification
|
||||
{
|
||||
#pragma unused(notification)
|
||||
if (_wrapper) {
|
||||
_wrapper->disconnectRemote();
|
||||
emit _wrapper->q_ptr->disconnected();
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation Server
|
||||
- (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 = _wrapper->q_ptr;
|
||||
SocketApiSocketPrivate *socketPrivate = new SocketApiSocketPrivate(remoteEnd);
|
||||
SocketApiSocket *socket = new SocketApiSocket(server, socketPrivate);
|
||||
_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()));
|
||||
memcpy(data, d->inBuffer.constData(), len);
|
||||
d->inBuffer.remove(0, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
qint64 SocketApiSocket::writeData(const char *data, qint64 len)
|
||||
{
|
||||
Q_D(SocketApiSocket);
|
||||
if (d->isRemoteDisconnected)
|
||||
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.
|
||||
[d->remoteEnd sendMessage:[NSData dataWithBytesNoCopy:const_cast<char *>(data) length:len freeWhenDone:NO]];
|
||||
return len;
|
||||
} @catch (NSException *e) {
|
||||
// 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];
|
||||
}
|
Loading…
Reference in New Issue