#import <Cocoa/Cocoa.h>
#import <FinderSync/FinderSync.h>
+#import <NCDesktopClientSocketKit/LocalSocketClient.h>
+
#import "SyncClient.h"
-#import "LineProcessor.h"
-#import "LocalSocketClient.h"
+#import "FinderSyncSocketLineProcessor.h"
@interface FinderSync : FIFinderSync <SyncClientDelegate>
{
NSCondition *_menuIsComplete;
}
-@property LineProcessor *lineProcessor;
+@property FinderSyncSocketLineProcessor *lineProcessor;
@property LocalSocketClient *localSocketClient;
@end
NSLog(@"Socket path: %@", socketPath.path);
if (socketPath.path) {
- self.lineProcessor = [[LineProcessor alloc] initWithDelegate:self];
+ self.lineProcessor = [[FinderSyncSocketLineProcessor alloc] initWithDelegate:self];
self.localSocketClient = [[LocalSocketClient alloc] initWithSocketPath:socketPath.path
lineProcessor:self.lineProcessor];
[self.localSocketClient start];
--- /dev/null
+/*
+ * 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 <NCDesktopClientSocketKit/LineProcessor.h>
+
+#import "SyncClient.h"
+
+#ifndef FinderSyncSocketLineProcessor_h
+#define FinderSyncSocketLineProcessor_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 (UTF8 strings) acquired from the socket; look at the
+/// LocalSocketClient for more detail on how data is read from and written to the socket.
+
+@interface FinderSyncSocketLineProcessor : NSObject<LineProcessor>
+
+@property(nonatomic, weak) id<SyncClientDelegate> delegate;
+
+- (instancetype)initWithDelegate:(id<SyncClientDelegate>)delegate;
+
+@end
+#endif /* LineProcessor_h */
--- /dev/null
+/*
+ * 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 "FinderSyncSocketLineProcessor.h"
+
+@implementation FinderSyncSocketLineProcessor
+
+-(instancetype)initWithDelegate:(id<SyncClientDelegate>)delegate
+{
+ NSLog(@"Init line processor with delegate.");
+ self = [super init];
+ if (self) {
+ 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
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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 = [super init];
- if (self) {
- 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
+++ /dev/null
-/*
- * 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
-
-- (instancetype)initWithSocketPath:(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 */
+++ /dev/null
-/*
- * 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>
-
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <stdio.h>
-#include <string.h>
-
-#import "LocalSocketClient.h"
-
-@interface LocalSocketClient()
-{
- NSString* _socketPath;
- LineProcessor* _lineProcessor;
-
- int _sock;
- dispatch_queue_t _localSocketQueue;
- dispatch_source_t _readSource;
- dispatch_source_t _writeSource;
- NSMutableData* _inBuffer;
- NSMutableData* _outBuffer;
-}
-@end
-
-@implementation LocalSocketClient
-
-- (instancetype)initWithSocketPath:(NSString*)socketPath lineProcessor:(LineProcessor*)lineProcessor
-{
- NSLog(@"Initiating local socket client.");
- self = [super init];
-
- if(self) {
- _socketPath = socketPath;
- _lineProcessor = lineProcessor;
-
- _sock = -1;
- _localSocketQueue = dispatch_queue_create("localSocketQueue", DISPATCH_QUEUE_SERIAL);
-
- _inBuffer = [NSMutableData data];
- _outBuffer = [NSMutableData data];
- }
-
- return self;
-}
-
-- (BOOL)isConnected
-{
- NSLog(@"Checking is connected: %@", _sock != -1 ? @"YES" : @"NO");
- return _sock != -1;
-}
-
-- (void)start
-{
- if([self isConnected]) {
- NSLog(@"Socket client already connected. Not starting.");
- return;
- }
-
- struct sockaddr_un localSocketAddr;
- unsigned long socketPathByteCount = [_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", _socketPath, maxByteCount, socketPathByteCount);
- return;
- }
-
- NSLog(@"Opening local socket...");
-
- // LOG THAT THE SOCKET IS BEING OPENED HERE
- _sock = socket(AF_LOCAL, SOCK_STREAM, 0);
-
- if(_sock == -1) {
- NSLog(@"Cannot open socket: '%@'", [self strErr]);
- [self restart];
- return;
- }
-
- NSLog(@"Local socket opened. Connecting to '%@' ...", _socketPath);
-
- localSocketAddr.sun_family = AF_LOCAL & 0xff;
-
- const char* pathBytes = [_socketPath UTF8String];
- strcpy(localSocketAddr.sun_path, pathBytes);
-
- int connectionStatus = connect(_sock, (struct sockaddr*)&localSocketAddr, sizeof(localSocketAddr));
-
- if(connectionStatus == -1) {
- NSLog(@"Could not connect to '%@': '%@'", _socketPath, [self strErr]);
- [self restart];
- return;
- }
-
- int flags = fcntl(_sock, F_GETFL, 0);
-
- if(fcntl(_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...");
-
- _readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, _sock, 0, _localSocketQueue);
- dispatch_source_set_event_handler(_readSource, ^(void){ [self readFromSocket]; });
- dispatch_source_set_cancel_handler(_readSource, ^(void){
- self->_readSource = nil;
- [self closeConnection];
- });
-
- _writeSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, _sock, 0, _localSocketQueue);
- dispatch_source_set_event_handler(_writeSource, ^(void){ [self writeToSocket]; });
- dispatch_source_set_cancel_handler(_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(_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.");
-
- if(_readSource) {
- // Since dispatch_source_cancel works asynchronously, if we deallocate the dispatch source here then we can
- // cause a crash. So instead we strongly hold a reference to the read source and deallocate it asynchronously
- // with the handler.
- __block dispatch_source_t previousReadSource = _readSource;
- dispatch_source_set_cancel_handler(_readSource, ^{
- previousReadSource = nil;
- });
- dispatch_source_cancel(_readSource);
- // The readSource is still alive due to the other reference and will be deallocated by the cancel handler
- _readSource = nil;
- }
-
- if(_writeSource) {
- // Same deal with the write source
- __block dispatch_source_t previousWriteSource = _writeSource;
- dispatch_source_set_cancel_handler(_writeSource, ^{
- previousWriteSource = nil;
- });
- dispatch_source_cancel(_writeSource);
- _writeSource = nil;
- }
-
- [_inBuffer setLength:0];
- [_outBuffer setLength: 0];
-
- if(_sock != -1) {
- close(_sock);
- _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\10", err];
- }
-}
-
-- (void)askOnSocket:(NSString *)path query:(NSString *)verb
-{
- NSString *line = [NSString stringWithFormat:@"%@:%@\n", verb, path];
- dispatch_async(_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([_outBuffer length] == 0) {
- NSLog(@"Empty out buffer, suspending write dispatch source.");
- dispatch_suspend(_writeSource);
- return;
- }
-
- NSLog(@"About to write %li bytes from outbuffer to socket.", [_outBuffer length]);
-
- long bytesWritten = write(_sock, [_outBuffer bytes], [_outBuffer length]);
- char lineWritten[[_outBuffer length]];
- memcpy(lineWritten, [_outBuffer bytes], [_outBuffer length]);
- NSLog(@"Wrote %li bytes to socket. Line written was: '%@'", bytesWritten, [NSString stringWithUTF8String:lineWritten]);
-
- if(bytesWritten == 0) {
- // 0 means we reached "end of file" and thus the socket was closed\10. 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) {
- [_outBuffer replaceBytesInRange:NSMakeRange(0, bytesWritten) withBytes:NULL length:0];
-
- NSLog(@"Out buffer cleared. Now count is %li bytes.", [_outBuffer length]);
-
- if([_outBuffer length] == 0) {
- NSLog(@"Out buffer has been emptied, suspending write dispatch source.");
- dispatch_suspend(_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(_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\10. 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 {
- [_inBuffer appendBytes:buffer length:bytesRead];
- [self processInBuffer];
- }
- }
-}
-
-- (void)processInBuffer
-{
- NSLog(@"Processing in buffer. In buffer length %li", [_inBuffer length]);
- UInt8 separator[] = {0xa}; // Byte value for "\n"
- while(true) {
- NSRange firstSeparatorIndex = [_inBuffer rangeOfData:[NSData dataWithBytes:separator length:1] options:0 range:NSMakeRange(0, [_inBuffer length])];
-
- if(firstSeparatorIndex.location == NSNotFound) {
- NSLog(@"No separator found. Stopping.");
- return; // No separator, nope out
- } else {
- unsigned char *buffer = [_inBuffer mutableBytes];
- buffer[firstSeparatorIndex.location] = 0; // Add NULL terminator, so we can use C string methods
-
- NSString *newLine = [NSString stringWithUTF8String:[_inBuffer bytes]];
-
- [_inBuffer replaceBytesInRange:NSMakeRange(0, firstSeparatorIndex.location + 1) withBytes:NULL length:0];
- [_lineProcessor process:newLine];
- }
- }
-}
-
-@end
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef LineProcessor_h
+#define LineProcessor_h
+
+@protocol LineProcessor<NSObject>
+
+- (void)process:(NSString*)line;
+
+@end
+
+#endif /* LineProcessor_h */
--- /dev/null
+/*
+ * 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 <NCDesktopClientSocketKit/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
+
+- (instancetype)initWithSocketPath:(NSString*)socketPath
+ lineProcessor:(id<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 */
--- /dev/null
+/*
+ * 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>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <string.h>
+
+#import "LocalSocketClient.h"
+
+@interface LocalSocketClient()
+{
+ NSString* _socketPath;
+ id<LineProcessor> _lineProcessor;
+
+ int _sock;
+ dispatch_queue_t _localSocketQueue;
+ dispatch_source_t _readSource;
+ dispatch_source_t _writeSource;
+ NSMutableData* _inBuffer;
+ NSMutableData* _outBuffer;
+}
+@end
+
+@implementation LocalSocketClient
+
+- (instancetype)initWithSocketPath:(NSString*)socketPath
+ lineProcessor:(id<LineProcessor>)lineProcessor
+{
+ NSLog(@"Initiating local socket client.");
+ self = [super init];
+
+ if(self) {
+ _socketPath = socketPath;
+ _lineProcessor = lineProcessor;
+
+ _sock = -1;
+ _localSocketQueue = dispatch_queue_create("localSocketQueue", DISPATCH_QUEUE_SERIAL);
+
+ _inBuffer = [NSMutableData data];
+ _outBuffer = [NSMutableData data];
+ }
+
+ return self;
+}
+
+- (BOOL)isConnected
+{
+ NSLog(@"Checking is connected: %@", _sock != -1 ? @"YES" : @"NO");
+ return _sock != -1;
+}
+
+- (void)start
+{
+ if([self isConnected]) {
+ NSLog(@"Socket client already connected. Not starting.");
+ return;
+ }
+
+ struct sockaddr_un localSocketAddr;
+ unsigned long socketPathByteCount = [_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", _socketPath, maxByteCount, socketPathByteCount);
+ return;
+ }
+
+ NSLog(@"Opening local socket...");
+
+ // LOG THAT THE SOCKET IS BEING OPENED HERE
+ _sock = socket(AF_LOCAL, SOCK_STREAM, 0);
+
+ if(_sock == -1) {
+ NSLog(@"Cannot open socket: '%@'", [self strErr]);
+ [self restart];
+ return;
+ }
+
+ NSLog(@"Local socket opened. Connecting to '%@' ...", _socketPath);
+
+ localSocketAddr.sun_family = AF_LOCAL & 0xff;
+
+ const char* pathBytes = [_socketPath UTF8String];
+ strcpy(localSocketAddr.sun_path, pathBytes);
+
+ int connectionStatus = connect(_sock, (struct sockaddr*)&localSocketAddr, sizeof(localSocketAddr));
+
+ if(connectionStatus == -1) {
+ NSLog(@"Could not connect to '%@': '%@'", _socketPath, [self strErr]);
+ [self restart];
+ return;
+ }
+
+ int flags = fcntl(_sock, F_GETFL, 0);
+
+ if(fcntl(_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...");
+
+ _readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, _sock, 0, _localSocketQueue);
+ dispatch_source_set_event_handler(_readSource, ^(void){ [self readFromSocket]; });
+ dispatch_source_set_cancel_handler(_readSource, ^(void){
+ self->_readSource = nil;
+ [self closeConnection];
+ });
+
+ _writeSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, _sock, 0, _localSocketQueue);
+ dispatch_source_set_event_handler(_writeSource, ^(void){ [self writeToSocket]; });
+ dispatch_source_set_cancel_handler(_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(_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.");
+
+ if(_readSource) {
+ // Since dispatch_source_cancel works asynchronously, if we deallocate the dispatch source here then we can
+ // cause a crash. So instead we strongly hold a reference to the read source and deallocate it asynchronously
+ // with the handler.
+ __block dispatch_source_t previousReadSource = _readSource;
+ dispatch_source_set_cancel_handler(_readSource, ^{
+ previousReadSource = nil;
+ });
+ dispatch_source_cancel(_readSource);
+ // The readSource is still alive due to the other reference and will be deallocated by the cancel handler
+ _readSource = nil;
+ }
+
+ if(_writeSource) {
+ // Same deal with the write source
+ __block dispatch_source_t previousWriteSource = _writeSource;
+ dispatch_source_set_cancel_handler(_writeSource, ^{
+ previousWriteSource = nil;
+ });
+ dispatch_source_cancel(_writeSource);
+ _writeSource = nil;
+ }
+
+ [_inBuffer setLength:0];
+ [_outBuffer setLength: 0];
+
+ if(_sock != -1) {
+ close(_sock);
+ _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\10", err];
+ }
+}
+
+- (void)askOnSocket:(NSString *)path query:(NSString *)verb
+{
+ NSString *line = [NSString stringWithFormat:@"%@:%@\n", verb, path];
+ dispatch_async(_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([_outBuffer length] == 0) {
+ NSLog(@"Empty out buffer, suspending write dispatch source.");
+ dispatch_suspend(_writeSource);
+ return;
+ }
+
+ NSLog(@"About to write %li bytes from outbuffer to socket.", [_outBuffer length]);
+
+ long bytesWritten = write(_sock, [_outBuffer bytes], [_outBuffer length]);
+ char lineWritten[[_outBuffer length]];
+ memcpy(lineWritten, [_outBuffer bytes], [_outBuffer length]);
+ NSLog(@"Wrote %li bytes to socket. Line written was: '%@'", bytesWritten, [NSString stringWithUTF8String:lineWritten]);
+
+ if(bytesWritten == 0) {
+ // 0 means we reached "end of file" and thus the socket was closed\10. 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) {
+ [_outBuffer replaceBytesInRange:NSMakeRange(0, bytesWritten) withBytes:NULL length:0];
+
+ NSLog(@"Out buffer cleared. Now count is %li bytes.", [_outBuffer length]);
+
+ if([_outBuffer length] == 0) {
+ NSLog(@"Out buffer has been emptied, suspending write dispatch source.");
+ dispatch_suspend(_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(_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\10. 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 {
+ [_inBuffer appendBytes:buffer length:bytesRead];
+ [self processInBuffer];
+ }
+ }
+}
+
+- (void)processInBuffer
+{
+ NSLog(@"Processing in buffer. In buffer length %li", [_inBuffer length]);
+ UInt8 separator[] = {0xa}; // Byte value for "\n"
+ while(true) {
+ NSRange firstSeparatorIndex = [_inBuffer rangeOfData:[NSData dataWithBytes:separator length:1] options:0 range:NSMakeRange(0, [_inBuffer length])];
+
+ if(firstSeparatorIndex.location == NSNotFound) {
+ NSLog(@"No separator found. Stopping.");
+ return; // No separator, nope out
+ } else {
+ unsigned char *buffer = [_inBuffer mutableBytes];
+ buffer[firstSeparatorIndex.location] = 0; // Add NULL terminator, so we can use C string methods
+
+ NSString *newLine = [NSString stringWithUTF8String:[_inBuffer bytes]];
+
+ [_inBuffer replaceBytesInRange:NSMakeRange(0, firstSeparatorIndex.location + 1) withBytes:NULL length:0];
+ [_lineProcessor process:newLine];
+ }
+ }
+}
+
+@end
--- /dev/null
+//
+// NCDesktopClientSocketKit.h
+// NCDesktopClientSocketKit
+//
+// Created by Claudio Cambra on 23/12/22.
+//
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for NCDesktopClientSocketKit.
+FOUNDATION_EXPORT double NCDesktopClientSocketKitVersionNumber;
+
+//! Project version string for NCDesktopClientSocketKit.
+FOUNDATION_EXPORT const unsigned char NCDesktopClientSocketKitVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <NCDesktopClientSocketKit/PublicHeader.h>
+
+#import <NCDesktopClientSocketKit/LocalSocketClient.h>
+#import <NCDesktopClientSocketKit/LineProcessor.h>
538E396F27F4765000FA63D5 /* FileProviderItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538E396E27F4765000FA63D5 /* FileProviderItem.swift */; };
538E397127F4765000FA63D5 /* FileProviderEnumerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538E397027F4765000FA63D5 /* FileProviderEnumerator.swift */; };
538E397627F4765000FA63D5 /* FileProviderExt.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 538E396727F4765000FA63D5 /* FileProviderExt.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
- 539158AC27BE71A900816F56 /* LineProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 539158AB27BE71A900816F56 /* LineProcessor.m */; };
- 539158B327BEC98A00816F56 /* LocalSocketClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 539158B227BEC98A00816F56 /* LocalSocketClient.m */; };
+ 53903D1E2956164F00D0B308 /* NCDesktopClientSocketKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 53903D0E2956164F00D0B308 /* NCDesktopClientSocketKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 53903D212956164F00D0B308 /* NCDesktopClientSocketKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53903D0C2956164F00D0B308 /* NCDesktopClientSocketKit.framework */; };
+ 53903D222956164F00D0B308 /* NCDesktopClientSocketKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 53903D0C2956164F00D0B308 /* NCDesktopClientSocketKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 53903D2A295616F000D0B308 /* LocalSocketClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 539158B227BEC98A00816F56 /* LocalSocketClient.m */; };
+ 53903D2B2956173000D0B308 /* NCDesktopClientSocketKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53903D0C2956164F00D0B308 /* NCDesktopClientSocketKit.framework */; };
+ 53903D2C2956173000D0B308 /* NCDesktopClientSocketKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 53903D0C2956164F00D0B308 /* NCDesktopClientSocketKit.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 53903D302956173F00D0B308 /* NCDesktopClientSocketKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53903D0C2956164F00D0B308 /* NCDesktopClientSocketKit.framework */; };
+ 53903D312956173F00D0B308 /* NCDesktopClientSocketKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 53903D0C2956164F00D0B308 /* NCDesktopClientSocketKit.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 53903D352956184400D0B308 /* LocalSocketClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 539158B127BE891500816F56 /* LocalSocketClient.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 53903D37295618A400D0B308 /* LineProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 53903D36295618A400D0B308 /* LineProcessor.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 539158AC27BE71A900816F56 /* FinderSyncSocketLineProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 539158AB27BE71A900816F56 /* FinderSyncSocketLineProcessor.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 */; };
remoteGlobalIDString = 538E396627F4765000FA63D5;
remoteInfo = FileProviderExt;
};
+ 53903D1F2956164F00D0B308 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = C2B573951B1CD88000303B36 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 53903D0B2956164F00D0B308;
+ remoteInfo = NCDesktopClientSocketKit;
+ };
+ 53903D2D2956173000D0B308 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = C2B573951B1CD88000303B36 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 53903D0B2956164F00D0B308;
+ remoteInfo = NCDesktopClientSocketKit;
+ };
+ 53903D322956173F00D0B308 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = C2B573951B1CD88000303B36 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 53903D0B2956164F00D0B308;
+ remoteInfo = NCDesktopClientSocketKit;
+ };
C2B573DF1B1CD9CE00303B36 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = C2B573951B1CD88000303B36 /* Project object */;
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
+ 53903D232956165000D0B308 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 53903D222956164F00D0B308 /* NCDesktopClientSocketKit.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 53903D2F2956173000D0B308 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 53903D2C2956173000D0B308 /* NCDesktopClientSocketKit.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 53903D342956173F00D0B308 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 53903D312956173F00D0B308 /* NCDesktopClientSocketKit.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
C2B573E11B1CD9CE00303B36 /* Embed App Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
538E397027F4765000FA63D5 /* FileProviderEnumerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderEnumerator.swift; sourceTree = "<group>"; };
538E397227F4765000FA63D5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
538E397327F4765000FA63D5 /* FileProviderExt.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FileProviderExt.entitlements; sourceTree = "<group>"; };
- 539158A927BE606500816F56 /* LineProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LineProcessor.h; sourceTree = "<group>"; };
+ 53903D0C2956164F00D0B308 /* NCDesktopClientSocketKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NCDesktopClientSocketKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 53903D0E2956164F00D0B308 /* NCDesktopClientSocketKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NCDesktopClientSocketKit.h; sourceTree = "<group>"; };
+ 53903D36295618A400D0B308 /* LineProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LineProcessor.h; sourceTree = "<group>"; };
+ 539158A927BE606500816F56 /* FinderSyncSocketLineProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FinderSyncSocketLineProcessor.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>"; };
+ 539158AB27BE71A900816F56 /* FinderSyncSocketLineProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FinderSyncSocketLineProcessor.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; };
buildActionMask = 2147483647;
files = (
538E396A27F4765000FA63D5 /* UniformTypeIdentifiers.framework in Frameworks */,
+ 53903D302956173F00D0B308 /* NCDesktopClientSocketKit.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 53903D092956164F00D0B308 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 53903D212956164F00D0B308 /* NCDesktopClientSocketKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 53903D2B2956173000D0B308 /* NCDesktopClientSocketKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
path = FileProviderExt;
sourceTree = "<group>";
};
+ 53903D0D2956164F00D0B308 /* NCDesktopClientSocketKit */ = {
+ isa = PBXGroup;
+ children = (
+ 53903D0E2956164F00D0B308 /* NCDesktopClientSocketKit.h */,
+ 539158B127BE891500816F56 /* LocalSocketClient.h */,
+ 539158B227BEC98A00816F56 /* LocalSocketClient.m */,
+ 53903D36295618A400D0B308 /* LineProcessor.h */,
+ );
+ path = NCDesktopClientSocketKit;
+ sourceTree = "<group>";
+ };
C2B573941B1CD88000303B36 = {
isa = PBXGroup;
children = (
C2B573B31B1CD91E00303B36 /* desktopclient */,
C2B573D81B1CD9CE00303B36 /* FinderSyncExt */,
538E396B27F4765000FA63D5 /* FileProviderExt */,
+ 53903D0D2956164F00D0B308 /* NCDesktopClientSocketKit */,
538E396827F4765000FA63D5 /* Frameworks */,
C2B573B21B1CD91E00303B36 /* Products */,
);
C2B573B11B1CD91E00303B36 /* desktopclient.app */,
C2B573D71B1CD9CE00303B36 /* FinderSyncExt.appex */,
538E396727F4765000FA63D5 /* FileProviderExt.appex */,
+ 53903D0C2956164F00D0B308 /* NCDesktopClientSocketKit.framework */,
);
name = Products;
sourceTree = "<group>";
539158AA27BE67CC00816F56 /* SyncClient.h */,
C2B573DC1B1CD9CE00303B36 /* FinderSync.h */,
C2B573DD1B1CD9CE00303B36 /* FinderSync.m */,
- 539158A927BE606500816F56 /* LineProcessor.h */,
- 539158AB27BE71A900816F56 /* LineProcessor.m */,
- 539158B127BE891500816F56 /* LocalSocketClient.h */,
- 539158B227BEC98A00816F56 /* LocalSocketClient.m */,
+ 539158A927BE606500816F56 /* FinderSyncSocketLineProcessor.h */,
+ 539158AB27BE71A900816F56 /* FinderSyncSocketLineProcessor.m */,
C2B573D91B1CD9CE00303B36 /* Supporting Files */,
);
path = FinderSyncExt;
};
/* End PBXGroup section */
+/* Begin PBXHeadersBuildPhase section */
+ 53903D072956164F00D0B308 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 53903D352956184400D0B308 /* LocalSocketClient.h in Headers */,
+ 53903D37295618A400D0B308 /* LineProcessor.h in Headers */,
+ 53903D1E2956164F00D0B308 /* NCDesktopClientSocketKit.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
/* Begin PBXNativeTarget section */
538E396627F4765000FA63D5 /* FileProviderExt */ = {
isa = PBXNativeTarget;
538E396327F4765000FA63D5 /* Sources */,
538E396427F4765000FA63D5 /* Frameworks */,
538E396527F4765000FA63D5 /* Resources */,
+ 53903D342956173F00D0B308 /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
+ 53903D332956173F00D0B308 /* PBXTargetDependency */,
);
name = FileProviderExt;
packageProductDependencies = (
productReference = 538E396727F4765000FA63D5 /* FileProviderExt.appex */;
productType = "com.apple.product-type.app-extension";
};
+ 53903D0B2956164F00D0B308 /* NCDesktopClientSocketKit */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 53903D282956165000D0B308 /* Build configuration list for PBXNativeTarget "NCDesktopClientSocketKit" */;
+ buildPhases = (
+ 53903D072956164F00D0B308 /* Headers */,
+ 53903D082956164F00D0B308 /* Sources */,
+ 53903D092956164F00D0B308 /* Frameworks */,
+ 53903D0A2956164F00D0B308 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = NCDesktopClientSocketKit;
+ productName = NCDesktopClientSocketKit;
+ productReference = 53903D0C2956164F00D0B308 /* NCDesktopClientSocketKit.framework */;
+ productType = "com.apple.product-type.framework";
+ };
C2B573B01B1CD91E00303B36 /* desktopclient */ = {
isa = PBXNativeTarget;
buildConfigurationList = C2B573CC1B1CD91E00303B36 /* Build configuration list for PBXNativeTarget "desktopclient" */;
C2B573AE1B1CD91E00303B36 /* Frameworks */,
C2B573AF1B1CD91E00303B36 /* Resources */,
C2B573E11B1CD9CE00303B36 /* Embed App Extensions */,
+ 53903D232956165000D0B308 /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
C2B573E01B1CD9CE00303B36 /* PBXTargetDependency */,
538E397527F4765000FA63D5 /* PBXTargetDependency */,
+ 53903D202956164F00D0B308 /* PBXTargetDependency */,
);
name = desktopclient;
productName = desktopclient;
C2B573D41B1CD9CE00303B36 /* Frameworks */,
C2B573D51B1CD9CE00303B36 /* Resources */,
5B3335471CA058E200E11A45 /* ShellScript */,
+ 53903D2F2956173000D0B308 /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
+ 53903D2E2956173000D0B308 /* PBXTargetDependency */,
);
name = FinderSyncExt;
productName = FinderSyncExt;
C2B573951B1CD88000303B36 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastSwiftUpdateCheck = 1330;
+ LastSwiftUpdateCheck = 1420;
LastUpgradeCheck = 1240;
TargetAttributes = {
538E396627F4765000FA63D5 = {
CreatedOnToolsVersion = 13.3;
ProvisioningStyle = Manual;
};
+ 53903D0B2956164F00D0B308 = {
+ CreatedOnToolsVersion = 14.2;
+ ProvisioningStyle = Manual;
+ };
C2B573B01B1CD91E00303B36 = {
CreatedOnToolsVersion = 6.3.1;
DevelopmentTeam = 9B5WD74GWJ;
C2B573B01B1CD91E00303B36 /* desktopclient */,
C2B573D61B1CD9CE00303B36 /* FinderSyncExt */,
538E396627F4765000FA63D5 /* FileProviderExt */,
+ 53903D0B2956164F00D0B308 /* NCDesktopClientSocketKit */,
);
};
/* End PBXProject section */
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 53903D0A2956164F00D0B308 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
C2B573AF1B1CD91E00303B36 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 53903D082956164F00D0B308 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 53903D2A295616F000D0B308 /* LocalSocketClient.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
C2B573AD1B1CD91E00303B36 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 539158B327BEC98A00816F56 /* LocalSocketClient.m in Sources */,
- 539158AC27BE71A900816F56 /* LineProcessor.m in Sources */,
+ 539158AC27BE71A900816F56 /* FinderSyncSocketLineProcessor.m in Sources */,
C2B573DE1B1CD9CE00303B36 /* FinderSync.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
target = 538E396627F4765000FA63D5 /* FileProviderExt */;
targetProxy = 538E397427F4765000FA63D5 /* PBXContainerItemProxy */;
};
+ 53903D202956164F00D0B308 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 53903D0B2956164F00D0B308 /* NCDesktopClientSocketKit */;
+ targetProxy = 53903D1F2956164F00D0B308 /* PBXContainerItemProxy */;
+ };
+ 53903D2E2956173000D0B308 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 53903D0B2956164F00D0B308 /* NCDesktopClientSocketKit */;
+ targetProxy = 53903D2D2956173000D0B308 /* PBXContainerItemProxy */;
+ };
+ 53903D332956173F00D0B308 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 53903D0B2956164F00D0B308 /* NCDesktopClientSocketKit */;
+ targetProxy = 53903D322956173F00D0B308 /* PBXContainerItemProxy */;
+ };
C2B573E01B1CD9CE00303B36 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = C2B573D61B1CD9CE00303B36 /* FinderSyncExt */;
INFOPLIST_KEY_CFBundleDisplayName = FileProviderExt;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks";
- MACOSX_DEPLOYMENT_TARGET = 12.3;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
INFOPLIST_KEY_CFBundleDisplayName = FileProviderExt;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks";
- MACOSX_DEPLOYMENT_TARGET = 12.3;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
};
name = Release;
};
+ 53903D242956165000D0B308 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ APPLICATION_EXTENSION_API_ONLY = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "-";
+ CODE_SIGN_STYLE = Manual;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = "";
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 16.2;
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks @loader_path/Frameworks";
+ "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks @loader_path/Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
+ MARKETING_VERSION = 1.0;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = com.owncloud.NCDesktopClientSocketKit;
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SDKROOT = auto;
+ SKIP_INSTALL = YES;
+ SUPPORTED_PLATFORMS = macosx;
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Debug;
+ };
+ 53903D252956165000D0B308 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ APPLICATION_EXTENSION_API_ONLY = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "-";
+ CODE_SIGN_STYLE = Manual;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = "";
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_NS_ASSERTIONS = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 16.2;
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks @loader_path/Frameworks";
+ "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks @loader_path/Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
+ MARKETING_VERSION = 1.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = com.owncloud.NCDesktopClientSocketKit;
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SDKROOT = auto;
+ SKIP_INSTALL = YES;
+ SUPPORTED_PLATFORMS = macosx;
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Release;
+ };
C2B573991B1CD88000303B36 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 53903D282956165000D0B308 /* Build configuration list for PBXNativeTarget "NCDesktopClientSocketKit" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 53903D242956165000D0B308 /* Debug */,
+ 53903D252956165000D0B308 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
C2B573981B1CD88000303B36 /* Build configuration list for PBXProject "NextcloudIntegration" */ = {
isa = XCConfigurationList;
buildConfigurations = (
</BuildableReference>
</MacroExpansion>
<Testables>
+ <TestableReference
+ skipped = "NO"
+ parallelizable = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "53903D142956164F00D0B308"
+ BuildableName = "NCDesktopClientSocketKitTests.xctest"
+ BlueprintName = "NCDesktopClientSocketKitTests"
+ ReferencedContainer = "container:NextcloudIntegration.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
</Testables>
</TestAction>
<LaunchAction