mirror of
https://github.com/nextcloud/desktop.git
synced 2024-12-12 04:21:47 +03:00
318 lines
9.2 KiB
Objective-C
318 lines
9.2 KiB
Objective-C
/**
|
|
* Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU Lesser General Public License as published by the Free
|
|
* Software Foundation; either version 2.1 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This library 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 Lesser General Public License for more
|
|
* details.
|
|
*/
|
|
|
|
#import "ContentManager.h"
|
|
#import "IconCache.h"
|
|
#import "RequestManager.h"
|
|
|
|
#define READ_TAG 2422
|
|
|
|
static RequestManager* sharedInstance = nil;
|
|
|
|
@implementation RequestManager
|
|
|
|
- (id)init
|
|
{
|
|
if ((self = [super init]))
|
|
{
|
|
_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
|
|
|
|
_isConnected = NO;
|
|
|
|
_registeredPathes = [[NSMutableDictionary alloc] init];
|
|
_requestedPaths = [[NSMutableSet alloc] init];
|
|
|
|
_shareMenuTitle = nil;
|
|
|
|
[NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(start) userInfo:nil repeats:YES];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_socket setDelegate:nil delegateQueue:NULL];
|
|
[_socket disconnect];
|
|
[_socket release];
|
|
|
|
sharedInstance = nil;
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
+ (RequestManager*)sharedInstance
|
|
{
|
|
@synchronized(self)
|
|
{
|
|
if (sharedInstance == nil)
|
|
{
|
|
sharedInstance = [[self alloc] init];
|
|
}
|
|
}
|
|
|
|
return sharedInstance;
|
|
}
|
|
|
|
|
|
- (void)askOnSocket:(NSString*)path query:(NSString*)verb
|
|
{
|
|
NSString *query = [NSString stringWithFormat:@"%@:%@\n", verb,path];
|
|
// NSLog(@"Query: %@", query);
|
|
|
|
NSData* data = [query dataUsingEncoding:NSUTF8StringEncoding];
|
|
[_socket writeData:data withTimeout:5 tag:4711];
|
|
|
|
NSData* stop = [@"\n" dataUsingEncoding:NSUTF8StringEncoding];
|
|
[_socket readDataToData:stop withTimeout:-1 tag:READ_TAG];
|
|
}
|
|
|
|
|
|
- (BOOL)isRegisteredPath:(NSString*)path isDirectory:(BOOL)isDir
|
|
{
|
|
// check if the file in question is underneath a registered directory
|
|
NSArray *regPathes = [_registeredPathes allKeys];
|
|
BOOL registered = NO;
|
|
|
|
NSString* checkPath = [NSString stringWithString:path];
|
|
if (isDir && ![checkPath hasSuffix:@"/"]) {
|
|
// append a slash
|
|
checkPath = [path stringByAppendingString:@"/"];
|
|
}
|
|
|
|
for( NSString *regPath in regPathes ) {
|
|
if( [checkPath hasPrefix:regPath]) {
|
|
// the path was registered
|
|
registered = YES;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return registered;
|
|
}
|
|
|
|
- (void)askForIcon:(NSString*)path isDirectory:(BOOL)isDir
|
|
{
|
|
NSString *verb = @"RETRIEVE_FILE_STATUS";
|
|
|
|
if( [self isRegisteredPath:path isDirectory:isDir] ) {
|
|
[_requestedPaths addObject:path];
|
|
if( _isConnected ) {
|
|
if(isDir) {
|
|
verb = @"RETRIEVE_FOLDER_STATUS";
|
|
}
|
|
|
|
[self askOnSocket:path query:verb];
|
|
} else {
|
|
[_requestQueue addObject:path];
|
|
[self start]; // try again to connect
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- (void)socket:(GCDAsyncSocket*)socket didReadData:(NSData*)data withTag:(long)tag
|
|
{
|
|
NSString *answer = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
NSArray *chunks = nil;
|
|
if (answer != nil && [answer length] > 0) {
|
|
// cut a trailing newline
|
|
answer = [answer substringToIndex:[answer length] - 1];
|
|
chunks = [answer componentsSeparatedByString: @":"];
|
|
}
|
|
ContentManager *contentman = [ContentManager sharedInstance];
|
|
|
|
if( chunks && [chunks count] > 0 && tag == READ_TAG ) {
|
|
// NSLog(@"READ from socket (%ld): <%@>", tag, answer);
|
|
if( [[chunks objectAtIndex:0] isEqualToString:@"STATUS"] ) {
|
|
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] ];
|
|
}
|
|
}
|
|
// The client will broadcast all changes, do not fill the cache for paths that Finder didn't ask for.
|
|
if ([_requestedPaths containsObject:path]) {
|
|
[contentman setResultForPath:path result:[chunks objectAtIndex:1]];
|
|
}
|
|
} else if( [[chunks objectAtIndex:0] isEqualToString:@"UPDATE_VIEW"] ) {
|
|
NSString *path = [chunks objectAtIndex:1];
|
|
[_requestedPaths removeAllObjects];
|
|
[contentman reFetchFileNameCacheForPath:path];
|
|
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"REGISTER_PATH"] ) {
|
|
NSNumber *one = [NSNumber numberWithInt:1];
|
|
NSString *path = [chunks objectAtIndex:1];
|
|
// NSLog(@"Registering path: %@", path);
|
|
[_registeredPathes setObject:one forKey:path];
|
|
|
|
[contentman repaintAllWindows];
|
|
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"UNREGISTER_PATH"] ) {
|
|
NSString *path = [chunks objectAtIndex:1];
|
|
[_registeredPathes removeObjectForKey:path];
|
|
|
|
[contentman repaintAllWindows];
|
|
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"ICON_PATH"] ) {
|
|
NSString *path = [chunks objectAtIndex:1];
|
|
[[ContentManager sharedInstance] loadIconResourcePath:path];
|
|
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"SHARE_MENU_TITLE"] ) {
|
|
_shareMenuTitle = [[chunks objectAtIndex:1] copy];
|
|
// NSLog(@"Received shar menu title: %@", _shareMenuTitle);
|
|
} else {
|
|
NSLog(@"SyncState: Unknown command %@", [chunks objectAtIndex:0]);
|
|
}
|
|
} else if (tag != READ_TAG) {
|
|
NSLog(@"SyncState: Received unknown tag %ld <%@>", tag, answer);
|
|
}
|
|
// Read on and on
|
|
NSData* stop = [@"\n" dataUsingEncoding:NSUTF8StringEncoding];
|
|
[_socket readDataToData:stop withTimeout:-1 tag:READ_TAG];
|
|
|
|
}
|
|
|
|
- (NSTimeInterval)socket:(GCDAsyncSocket*)socket shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length
|
|
{
|
|
// Called if a read operation has reached its timeout without completing.
|
|
return 0.0;
|
|
}
|
|
|
|
-(void)socket:(GCDAsyncSocket*)socket didConnectToUrl:(NSURL *)url {
|
|
// NSLog( @"Connected to sync client successfully on %@", url);
|
|
_isConnected = YES;
|
|
|
|
[self askOnSocket:@"" query:@"SHARE_MENU_TITLE"];
|
|
|
|
if( [_requestQueue count] > 0 ) {
|
|
// NSLog( @"We have to empty the queue");
|
|
for( NSString *path in _requestQueue ) {
|
|
[self askOnSocket:path query:@"RETRIEVE_FILE_STATUS"];
|
|
}
|
|
[_requestQueue removeAllObjects];
|
|
}
|
|
|
|
ContentManager *contentman = [ContentManager sharedInstance];
|
|
[contentman clearFileNameCache];
|
|
[contentman repaintAllWindows];
|
|
|
|
// Read for the UPDATE_VIEW requests
|
|
NSData* stop = [@"\n" dataUsingEncoding:NSUTF8StringEncoding];
|
|
[_socket readDataToData:stop withTimeout:-1 tag:READ_TAG];
|
|
}
|
|
|
|
- (void)socketDidDisconnect:(GCDAsyncSocket*)socket withError:(NSError*)err
|
|
{
|
|
// NSLog(@"Socket DISconnected! %@", [err localizedDescription]);
|
|
|
|
_isConnected = NO;
|
|
|
|
// clear the registered pathes.
|
|
[_registeredPathes release];
|
|
_registeredPathes = [[NSMutableDictionary alloc] init];
|
|
[_requestedPaths removeAllObjects];
|
|
|
|
// clear the caches in conent manager
|
|
ContentManager *contentman = [ContentManager sharedInstance];
|
|
[contentman clearFileNameCache];
|
|
[contentman repaintAllWindows];
|
|
}
|
|
|
|
- (NSDate*)fileDate:(NSString*)fn
|
|
{
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
NSError *error = 0;
|
|
NSDictionary *oneAttributes = [fileManager attributesOfItemAtPath:fn error:&error];
|
|
if (!error) {
|
|
return [oneAttributes valueForKey:NSFileModificationDate];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (NSString*) getNewerFileOne:(NSString*)one two:(NSString*)two
|
|
{
|
|
if (!one) {
|
|
return two;
|
|
}
|
|
NSDate *oneDate = [self fileDate:one];
|
|
NSDate *twoDate = [self fileDate:two];
|
|
if (oneDate && twoDate) {
|
|
if ([oneDate compare:twoDate] == NSOrderedDescending) {
|
|
return one;
|
|
} else if ([oneDate compare:twoDate] == NSOrderedAscending) {
|
|
return two;
|
|
} else {
|
|
return two;
|
|
}
|
|
}
|
|
return one;
|
|
}
|
|
|
|
|
|
- (void)start
|
|
{
|
|
if (!_isConnected && ![_socket isConnected])
|
|
{
|
|
NSError *err = nil;
|
|
NSURL *url = nil;
|
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
|
if ([paths count])
|
|
{
|
|
// e.g file:///Users/guruz/Library/Caches/SyncStateHelper/ownCloud.socket
|
|
NSString *syncStateHelperDir = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"SyncStateHelper"];
|
|
NSError *pnsError = NULL;
|
|
NSArray *paths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:syncStateHelperDir error:&pnsError];
|
|
if (!pnsError && paths && [paths count] > 0) {
|
|
NSString *currentLatestPath = nil;
|
|
if (paths.count > 1) {
|
|
// NSLog(@"Possible paths: %@", paths);
|
|
}
|
|
for (int i = 0; i < paths.count; i++) {
|
|
NSString *currentPath = [syncStateHelperDir stringByAppendingPathComponent:[paths objectAtIndex:i]];
|
|
if (![currentPath hasSuffix:@".socket"]) {
|
|
continue;
|
|
}
|
|
currentLatestPath = [self getNewerFileOne:currentLatestPath two:currentPath];
|
|
}
|
|
// FIXME Instead of connecting to the newest socket we could go multi-socket to support multiple instances
|
|
if (currentLatestPath) {
|
|
url = [NSURL fileURLWithPath:currentLatestPath];
|
|
}
|
|
}
|
|
}
|
|
if (url) {
|
|
// NSLog(@"Connect Socket to %@", url);
|
|
[_socket connectToUrl:url withTimeout:1 error:&err];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)menuItemClicked:(NSDictionary*)actionDictionary
|
|
{
|
|
// NSLog(@"RequestManager menuItemClicked %@", actionDictionary);
|
|
NSArray *filePaths = [actionDictionary valueForKey:@"files"];
|
|
for (int i = 0; i < filePaths.count; i++) {
|
|
[self askOnSocket:[filePaths objectAtIndex:i] query:@"SHARE"];
|
|
}
|
|
}
|
|
|
|
- (NSString*) shareItemTitle
|
|
{
|
|
if (_socket && _socket.isConnected && _shareMenuTitle) {
|
|
return _shareMenuTitle;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
|
|
|
|
@end
|