mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-10 22:45:59 +05:45
200 lines
6.2 KiB
Objective-C
200 lines
6.2 KiB
Objective-C
// Copyright 2017 Google
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#import "FirebaseStorage/Sources/FIRStoragePath.h"
|
|
|
|
#import "FirebaseStorage/Sources/FIRStorageConstants_Private.h"
|
|
|
|
@implementation FIRStoragePath
|
|
|
|
#pragma mark - Class methods
|
|
|
|
+ (nullable FIRStoragePath *)pathFromString:(NSString *)string {
|
|
if ([string hasPrefix:@"gs://"]) {
|
|
// "gs://bucket/path/to/object"
|
|
return [FIRStoragePath pathFromGSURI:string];
|
|
} else if ([string hasPrefix:@"http://"] || [string hasPrefix:@"https://"]) {
|
|
// "http[s]://firebasestorage.googleapis.com/bucket/path/to/object?signed_url_params"
|
|
return [FIRStoragePath pathFromHTTPURL:string];
|
|
} else {
|
|
// Invalid scheme, raise an exception!
|
|
[NSException raise:NSInternalInconsistencyException
|
|
format:@"URL scheme must be one of gs://, http://, or https:// "];
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
+ (nullable FIRStoragePath *)pathFromGSURI:(NSString *)aURIString {
|
|
NSString *bucketName;
|
|
NSString *objectName;
|
|
NSScanner *scanner = [NSScanner scannerWithString:aURIString];
|
|
BOOL isGSURI = [scanner scanString:@"gs://" intoString:NULL];
|
|
BOOL hasBucket = [scanner scanUpToString:@"/" intoString:&bucketName];
|
|
[scanner scanString:@"/" intoString:NULL];
|
|
[scanner scanUpToString:@"\n" intoString:&objectName];
|
|
|
|
if (!isGSURI || !hasBucket) {
|
|
[NSException raise:NSInternalInconsistencyException
|
|
format:@"URI must be in the form of gs://<bucket>/<path/to/object>"];
|
|
return nil;
|
|
}
|
|
|
|
return [[self alloc] initWithBucket:bucketName object:objectName];
|
|
}
|
|
|
|
+ (nullable FIRStoragePath *)pathFromHTTPURL:(NSString *)aURLString {
|
|
NSString *bucketName;
|
|
NSString *objectName;
|
|
NSURL *httpsURL = [NSURL URLWithString:aURLString];
|
|
NSArray *pathComponents = httpsURL.pathComponents; // [/, v0, b, <bucket>, o, <objects/...>]
|
|
|
|
if ([httpsURL.host isEqual:kFIRStorageHost]) {
|
|
// Have a bucket name
|
|
if ([pathComponents count] > 3) {
|
|
bucketName = pathComponents[3];
|
|
}
|
|
|
|
// Have an object name
|
|
if ([pathComponents count] > 5) {
|
|
NSRange objectRange = NSMakeRange(5, [pathComponents count] - 5);
|
|
objectName = [[pathComponents subarrayWithRange:objectRange] componentsJoinedByString:@"/"];
|
|
}
|
|
}
|
|
|
|
if (bucketName.length == 0) {
|
|
[NSException raise:NSInternalInconsistencyException
|
|
format:@"URL must be in the form of "
|
|
@"http[s]://firebasestorage.googleapis.com/v0/b/<bucket>/o/<path/to/"
|
|
@"object>[?token=signed_url_params]"];
|
|
return nil;
|
|
}
|
|
|
|
if (objectName.length == 0) {
|
|
objectName = nil;
|
|
}
|
|
|
|
return [[self alloc] initWithBucket:bucketName object:objectName];
|
|
}
|
|
|
|
#pragma mark - Initializers
|
|
|
|
- (instancetype)initWithBucket:(NSString *)bucket object:(nullable NSString *)object {
|
|
self = [super init];
|
|
if (self) {
|
|
_bucket = [bucket copy];
|
|
_object = [self standardizedPathForString:[object copy]];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
#pragma mark - NSObject overrides
|
|
|
|
- (instancetype)copyWithZone:(NSZone *)zone {
|
|
return [[[self class] allocWithZone:zone] initWithBucket:_bucket object:_object];
|
|
}
|
|
|
|
- (BOOL)isEqual:(id)object {
|
|
if (self == object) {
|
|
return YES;
|
|
}
|
|
|
|
if (![object isKindOfClass:[FIRStoragePath class]]) {
|
|
return NO;
|
|
}
|
|
|
|
BOOL isObjectEqual = [self isEqualToFIRStoragePath:(FIRStoragePath *)object];
|
|
return isObjectEqual;
|
|
}
|
|
|
|
- (BOOL)isEqualToFIRStoragePath:(FIRStoragePath *)path {
|
|
BOOL isBucketEqual = _bucket == nil && path->_bucket == nil;
|
|
BOOL isObjectEqual = _object == nil && path->_object == nil;
|
|
|
|
if (_bucket && path->_bucket) {
|
|
isBucketEqual = [_bucket isEqual:path->_bucket];
|
|
}
|
|
|
|
if (_object && path.object) {
|
|
isObjectEqual = [_object isEqual:path->_object];
|
|
}
|
|
|
|
BOOL isEqual = isBucketEqual && isObjectEqual;
|
|
return isEqual;
|
|
}
|
|
|
|
- (NSUInteger)hash {
|
|
// "...because in those days, you could XOR anything with anything and get something useful..."
|
|
// https://www.usenix.org/system/files/1309_14-17_mickens.pdf
|
|
NSUInteger hash = [_bucket hash] ^ [_object hash];
|
|
return hash;
|
|
}
|
|
|
|
- (NSString *)description {
|
|
return [NSString stringWithFormat:@"%@ %p: %@", [self class], self, [self stringValue]];
|
|
}
|
|
|
|
- (NSString *)stringValue {
|
|
return [NSString stringWithFormat:@"gs://%@/%@", _bucket, _object ?: @""];
|
|
}
|
|
|
|
#pragma mark - Public methods
|
|
|
|
- (FIRStoragePath *)child:(NSString *)path {
|
|
if (path.length == 0) {
|
|
return [self copy]; // Return a copy of the same path, nothing happened
|
|
}
|
|
|
|
NSString *childObject;
|
|
if (_object == nil) {
|
|
childObject = path;
|
|
} else {
|
|
childObject = [_object stringByAppendingPathComponent:path];
|
|
}
|
|
|
|
FIRStoragePath *childPath = [[FIRStoragePath alloc] initWithBucket:_bucket object:childObject];
|
|
return childPath;
|
|
}
|
|
|
|
- (nullable FIRStoragePath *)parent {
|
|
if (_object.length == 0) {
|
|
return nil;
|
|
}
|
|
|
|
NSString *parentObject = [_object stringByDeletingLastPathComponent];
|
|
FIRStoragePath *parentPath = [[FIRStoragePath alloc] initWithBucket:_bucket object:parentObject];
|
|
return parentPath;
|
|
}
|
|
|
|
- (FIRStoragePath *)root {
|
|
FIRStoragePath *rootPath = [[FIRStoragePath alloc] initWithBucket:_bucket object:nil];
|
|
return rootPath;
|
|
}
|
|
|
|
#pragma mark - Private methods
|
|
|
|
// Removes leading and trailing slashes, and compresses multiple slashes
|
|
// to create a canonical representation.
|
|
// Example: /foo//bar///baz//// -> foo/bar/baz
|
|
- (NSString *)standardizedPathForString:(NSString *)string {
|
|
NSMutableArray *components = [[string componentsSeparatedByString:@"/"] mutableCopy];
|
|
NSIndexSet *removedPaths =
|
|
[components indexesOfObjectsPassingTest:^BOOL(NSString *string, NSUInteger idx, BOOL *stop) {
|
|
return (string.length == 0);
|
|
}];
|
|
[components removeObjectsAtIndexes:removedPaths];
|
|
return [components componentsJoinedByString:@"/"];
|
|
}
|
|
|
|
@end
|