// // UtilsFramework.m // Owncloud iOs Client // // Copyright (C) 2016, ownCloud GmbH. ( http://www.owncloud.org/ ) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // #import "UtilsFramework.h" #import "OCCommunication.h" #import "OCFrameworkConstants.h" #import "OCErrorMsg.h" #import "OCConstants.h" #define kSAMLFragmentArray [NSArray arrayWithObjects: @"wayf", @"saml", @"sso_orig_uri", nil] @implementation UtilsFramework /* * Method that return a unique Id. * The global ID for the process includes the host name, process ID, and a time stamp, * which ensures that the ID is unique for the network * @return -> Unique Id (token) */ + (NSString *) getUserSessionToken{ return [[NSProcessInfo processInfo] globallyUniqueString]; } /* * Method that check the file name or folder name to find forbidden characters * This is the forbidden characters in server: "\", "/","<",">",":",""","|","?","*" * @fileName -> file name * * @isFCSupported -> From ownCloud 8.1 the forbidden characters are controller by the server except the '/' */ + (BOOL) isForbiddenCharactersInFileName:(NSString*)fileName withForbiddenCharactersSupported:(BOOL)isFCSupported{ BOOL thereAreForbidenCharacters = NO; //Check the filename for(NSInteger i = 0 ;i<[fileName length]; i++) { if ([fileName characterAtIndex:i]=='/'){ thereAreForbidenCharacters = YES; } if (!isFCSupported) { if ([fileName characterAtIndex:i] == '\\'){ thereAreForbidenCharacters = YES; } if ([fileName characterAtIndex:i] == '<'){ thereAreForbidenCharacters = YES; } if ([fileName characterAtIndex:i] == '>'){ thereAreForbidenCharacters = YES; } if ([fileName characterAtIndex:i] == '"'){ thereAreForbidenCharacters = YES; } if ([fileName characterAtIndex:i] == ','){ thereAreForbidenCharacters = YES; } if ([fileName characterAtIndex:i] == ':'){ thereAreForbidenCharacters = YES; } if ([fileName characterAtIndex:i] == '|'){ thereAreForbidenCharacters = YES; } if ([fileName characterAtIndex:i] == '?'){ thereAreForbidenCharacters = YES; } if ([fileName characterAtIndex:i] == '*'){ thereAreForbidenCharacters = YES; } } } return thereAreForbidenCharacters; } + (NSError *) getErrorWithCode:(NSInteger)errorCode andCustomMessageFromTheServer:(NSString *)message { NSError *error = nil; NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:message forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:errorCode userInfo:details]; return error; } /* * Get error for the same errors in the share api * * Statuscodes: * 100 - successful * 400 - wrong or no update parameter given * 403 - public upload disabled by the admin (or is neccesary put a password) * 404 - couldn’t update share * */ + (NSError *) getShareAPIErrorByCode:(NSInteger)errorCode { NSError *error = nil; switch (errorCode) { case kOCErrorSharedAPIWrong: { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"Wrong or no update parameter given" forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:kOCErrorSharedAPIWrong userInfo:details]; break; } case kOCErrorServerForbidden: { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"Public upload disabled by the admin" forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:kOCErrorServerForbidden userInfo:details]; break; } case kOCErrorServerPathNotFound: { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"Couldn't update share" forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:kOCErrorServerPathNotFound userInfo:details]; break; } default: { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"Unknow error" forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:OCErrorUnknow userInfo:details]; break; } } return error; } ///----------------------------------- /// @name getErrorByCodeId ///----------------------------------- /** * Method to return a Error with description from a OC Error code * * @param int -> errorCode number to identify the OC Error * * @return NSError * */ + (NSError *) getErrorByCodeId:(int) errorCode { NSError *error = nil; switch (errorCode) { case OCErrorForbidenCharacters: { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"You have entered forbbiden characters" forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:OCErrorForbidenCharacters userInfo:details]; break; } case OCErrorMovingDestinyNameHaveForbiddenCharacters: { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"The file or folder that you are moving have forbidden characters" forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:OCErrorMovingDestinyNameHaveForbiddenCharacters userInfo:details]; break; } case OCErrorMovingTheDestinyAndOriginAreTheSame: { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"You are trying to move the file to the same folder" forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:OCErrorMovingTheDestinyAndOriginAreTheSame userInfo:details]; break; } case OCErrorMovingFolderInsideHimself: { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"You are trying to move a folder inside himself" forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:OCErrorMovingFolderInsideHimself userInfo:details]; break; } case kOCErrorServerPathNotFound: { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"You are trying to access to a file that does not exist" forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:kOCErrorServerPathNotFound userInfo:details]; break; } case kOCErrorServerForbidden: { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"You are trying to do a forbbiden operation" forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:kOCErrorServerForbidden userInfo:details]; break; } case OCServerErrorForbiddenCharacters: { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"Server said: File name contains at least one invalid character" forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:OCServerErrorForbiddenCharacters userInfo:details]; break; } default: { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"Unknow error" forKey:NSLocalizedDescriptionKey]; error = [NSError errorWithDomain:k_domain_error_code code:OCErrorUnknow userInfo:details]; break; } } return error; } ///----------------------------------- /// @name getFileNameOrFolderByPath ///----------------------------------- /** * Method that return a filename from a path * * @param NSString -> path of the file (including the file) * * @return NSString -> fileName * */ + (NSString *) getFileNameOrFolderByPath:(NSString *) path { NSString *output; if (path && [path length] > 0) { NSArray *listItems = [path componentsSeparatedByString:@"/"]; output = [listItems objectAtIndex:[listItems count]-1]; if ([output length] <= 0) { output = [listItems objectAtIndex:[listItems count]-2]; } //If is a folder we set the last character in order to compare folders with folders and files with files /*if([path hasSuffix:@"/"]) { output = [NSString stringWithFormat:@"%@/", output]; }*/ } return output; } /* * Method that return a boolean that indicate if is the same url */ + (BOOL) isTheSameFileOrFolderByNewURLString:(NSString *) newURLString andOriginURLString:(NSString *) originalURLString{ if ([originalURLString isEqualToString:newURLString]) { return YES; } return NO; } /* * Method that return a boolean that indicate if newUrl is under the original Url */ + (BOOL) isAFolderUnderItByNewURLString:(NSString *) newURLString andOriginURLString:(NSString *) originalURLString{ if([originalURLString length] < [newURLString length]) { NSString *subString = [newURLString substringToIndex: [originalURLString length]]; if([originalURLString isEqualToString: subString]){ newURLString = [newURLString substringFromIndex:[subString length]]; if ([newURLString rangeOfString:@"/"].location == NSNotFound) { //Is a rename of the last part of the file or folder return NO; } else { //Is a move inside himself return YES; } } } return NO; } ///----------------------------------- /// @name getSizeInBytesByPath ///----------------------------------- /** * Method to return the size of a file by a path * * @param NSString -> path of the file * * @return long long -> size of the file in the path */ + (long long) getSizeInBytesByPath:(NSString *)path { long long fileLength = [[[[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil] valueForKey:NSFileSize] unsignedLongLongValue]; return fileLength; } ///----------------------------------- /// @name isURLWithSamlFragment: ///----------------------------------- /** * Method to check a url string to looking for a SAML fragment * * @param urlString -> url from redirect server * * @return BOOL -> the result about if exist the SAML fragment or not */ + (BOOL) isURLWithSamlFragment:(NSString*)urlString { urlString = [urlString lowercaseString]; if (urlString) { for (NSString* samlFragment in kSAMLFragmentArray) { if ([urlString rangeOfString:samlFragment options:NSCaseInsensitiveSearch].location != NSNotFound) { NSLog(@"A SAML fragment is in the request url"); return YES; } } } return NO; } + (NSString *) AFBase64EncodedStringFromString:(NSString *) string { NSData *data = [NSData dataWithBytes:[string UTF8String] length:[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]]; NSUInteger length = [data length]; NSMutableData *mutableData = [NSMutableData dataWithLength:((length + 2) / 3) * 4]; uint8_t *input = (uint8_t *)[data bytes]; uint8_t *output = (uint8_t *)[mutableData mutableBytes]; for (NSUInteger i = 0; i < length; i += 3) { NSUInteger value = 0; for (NSUInteger j = i; j < (i + 3); j++) { value <<= 8; if (j < length) { value |= (0xFF & input[j]); } } static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; NSUInteger idx = (i / 3) * 4; output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F]; output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F]; output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6) & 0x3F] : '='; output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0) & 0x3F] : '='; } return [[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding]; } #pragma mark - Manage Cookies //----------------------------------- /// @name addCookiesToStorageFromResponse ///----------------------------------- /** * Method to storage all the cookies from a response in order to use them in future requests * * @param NSHTTPURLResponse -> response * @param NSURL -> url * */ + (void) addCookiesToStorageFromResponse: (NSURLResponse *) response andPath:(NSURL *) url { //TODO: Using NSURLSession this should not be necessary /*NSArray* cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:url]; for (NSHTTPCookie *current in cookies) { [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:current]; }*/ } //----------------------------------- /// @name getRequestWithCookiesByRequest ///----------------------------------- /** * Method to return a request with all the necessary cookies of the original url without redirection * * @param NSMutableURLRequest -> request * @param NSString -> originalUrlServer * * @return request * */ + (NSMutableURLRequest *) getRequestWithCookiesByRequest: (NSMutableURLRequest *) request andOriginalUrlServer:(NSString *) originalUrlServer { //We add the cookies of that URL NSArray *cookieStorage = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:originalUrlServer]]; NSDictionary *cookieHeaders = [NSHTTPCookie requestHeaderFieldsWithCookies:cookieStorage]; for (NSString *key in cookieHeaders) { [request addValue:cookieHeaders[key] forHTTPHeaderField:key]; } return request; } //----------------------------------- /// @name deleteAllCookies ///----------------------------------- /** * Method to clean the CookiesStorage * */ + (void) deleteAllCookies { NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; for (NSHTTPCookie *each in cookieStorage.cookies) { [cookieStorage deleteCookie:each]; } } //----------------------------------- /// @name isServerVersionHigherThanLimitVersion ///----------------------------------- /** * Method to detect if a server version is higher than a limit version. * This methos is used for example to know if the server have share API or support Cookies * * @param NSString -> serverVersion * @param NSArray -> limitVersion * * @return BOOL * */ + (BOOL) isServerVersion:(NSString *) serverVersionString higherThanLimitVersion:(NSArray *) limitVersion { //Split the strings - Type 5.0.13 NSArray *spliteVersion = [serverVersionString componentsSeparatedByString:@"."]; NSMutableArray *serverVersion = [NSMutableArray new]; for (NSString *string in spliteVersion) { [serverVersion addObject:string]; } __block BOOL isSupported = NO; //Loop of compare [limitVersion enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSString *firstVersionString = obj; NSString *currentVersionString; if ([serverVersion count] > idx) { currentVersionString = [serverVersion objectAtIndex:idx]; int firstVersionInt = [firstVersionString intValue]; int currentVersionInt = [currentVersionString intValue]; //NSLog(@"firstVersion item %d item is: %d", idx, firstVersionInt); //NSLog(@"currentVersion item %d item is: %d", idx, currentVersionInt); //Comparation secure switch (idx) { case 0: //if the first number is higher if (currentVersionInt > firstVersionInt) { isSupported = YES; *stop=YES; } //if the first number is lower if (currentVersionInt < firstVersionInt) { isSupported = NO; *stop=YES; } break; case 1: //if the seccond number is higger if (currentVersionInt > firstVersionInt) { isSupported = YES; *stop=YES; } //if the second number is lower if (currentVersionInt < firstVersionInt) { isSupported = NO; *stop=YES; } break; case 2: //if the third number is higger or equal if (currentVersionInt >= firstVersionInt) { isSupported = YES; *stop=YES; } else { //if the third number is lower isSupported = NO; *stop=YES; } break; default: break; } } else { isSupported = NO; *stop=YES; } }]; return isSupported; } #pragma mark - Share Permissions + (NSInteger) getPermissionsValueByCanEdit:(BOOL)canEdit andCanCreate:(BOOL)canCreate andCanChange:(BOOL)canChange andCanDelete:(BOOL)canDelete andCanShare:(BOOL)canShare andIsFolder:(BOOL) isFolder { NSInteger permissionsValue = k_read_share_permission; if (canEdit && !isFolder) { permissionsValue = permissionsValue + k_update_share_permission; } if (canCreate & isFolder) { permissionsValue = permissionsValue + k_create_share_permission; } if (canChange && isFolder) { permissionsValue = permissionsValue + k_update_share_permission; } if (canDelete & isFolder) { permissionsValue = permissionsValue + k_delete_share_permission; } if (canShare) { permissionsValue = permissionsValue + k_share_share_permission; } return permissionsValue; } + (BOOL) isPermissionToCanCreate:(NSInteger) permissionValue { BOOL canCreate = ((permissionValue & k_create_share_permission) > 0); return canCreate; } + (BOOL) isPermissionToCanChange:(NSInteger) permissionValue { BOOL canChange = ((permissionValue & k_update_share_permission) > 0); return canChange; } + (BOOL) isPermissionToCanDelete:(NSInteger) permissionValue { BOOL canDelete = ((permissionValue & k_delete_share_permission) > 0); return canDelete; } + (BOOL) isPermissionToCanShare:(NSInteger) permissionValue { BOOL canShare = ((permissionValue & k_share_share_permission) > 0); return canShare; } + (BOOL) isAnyPermissionToEdit:(NSInteger) permissionValue { BOOL canCreate = [self isPermissionToCanCreate:permissionValue]; BOOL canChange = [self isPermissionToCanChange:permissionValue]; BOOL canDelete = [self isPermissionToCanDelete:permissionValue]; BOOL canEdit = (canCreate || canChange || canDelete); return canEdit; } + (BOOL) isPermissionToRead:(NSInteger) permissionValue { BOOL canRead = ((permissionValue & k_read_share_permission) > 0); return canRead; } + (BOOL) isPermissionToReadCreateUpdate:(NSInteger) permissionValue { BOOL canRead = [self isPermissionToRead:permissionValue]; BOOL canCreate = [self isPermissionToCanCreate:permissionValue]; BOOL canChange = [self isPermissionToCanChange:permissionValue]; BOOL canEdit = (canCreate && canChange && canRead); return canEdit; } @end