209 lines
6.0 KiB
Objective-C
209 lines
6.0 KiB
Objective-C
/*
|
|
Copyright 2020 Vector Creations Ltd
|
|
|
|
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 "UniversalLink.h"
|
|
#import "NSArray+Element.h"
|
|
|
|
@implementation UniversalLink
|
|
|
|
- (id)initWithUrl:(NSURL *)url
|
|
{
|
|
self = [super init];
|
|
if (self)
|
|
{
|
|
_url = url;
|
|
|
|
// Extract required parameters from the link
|
|
[self parsePathAndQueryParamsForURL:url];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (id)initWithUrl:(NSURL *)url updatedFragment:(NSString *)fragment
|
|
{
|
|
self = [super init];
|
|
if (self)
|
|
{
|
|
_url = url;
|
|
|
|
// Update the url with the fragment
|
|
NSURLComponents *components = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO];
|
|
components.fragment = fragment;
|
|
[self parsePathAndQueryParamsForURL:components.URL];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
/**
|
|
Extract params from the URL fragment part (after '#') of a vector.im Universal link:
|
|
|
|
The fragment can contain a '?'. So there are two kinds of parameters: path params and query params.
|
|
It is in the form of /[pathParam1]/[pathParam2]?[queryParam1Key]=[queryParam1Value]&[queryParam2Key]=[queryParam2Value]
|
|
*/
|
|
- (void)parsePathAndQueryParamsForURL:(NSURL *)url
|
|
{
|
|
NSArray<NSString*> *pathParams;
|
|
NSMutableDictionary *queryParams = [NSMutableDictionary dictionary];
|
|
|
|
NSArray<NSString*> *fragments = [url.fragment componentsSeparatedByString:@"?"];
|
|
|
|
// Extract path params
|
|
pathParams = [[fragments[0] stringByRemovingPercentEncoding] componentsSeparatedByString:@"/"];
|
|
|
|
// Remove the first empty path param string
|
|
pathParams = [pathParams filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"length > 0"]];
|
|
|
|
// URL decode each path param
|
|
pathParams = [pathParams vc_map:^id _Nonnull(NSString * _Nonnull item) {
|
|
return [item stringByRemovingPercentEncoding];
|
|
}];
|
|
|
|
// Extract query params
|
|
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
|
for (NSURLQueryItem *item in components.queryItems)
|
|
{
|
|
if (item.value)
|
|
{
|
|
NSString *key = item.name;
|
|
NSString *value = item.value;
|
|
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
|
value = [value stringByRemovingPercentEncoding];
|
|
|
|
if ([key isEqualToString:@"via"])
|
|
{
|
|
// Special case the via parameter
|
|
// As we can have several of them, store each value into an array
|
|
if (!queryParams[key])
|
|
{
|
|
queryParams[key] = [NSMutableArray array];
|
|
}
|
|
|
|
[queryParams[key] addObject:value];
|
|
}
|
|
else
|
|
{
|
|
queryParams[key] = value;
|
|
}
|
|
}
|
|
}
|
|
// Query params are in the form [queryParam1Key]=[queryParam1Value], so the
|
|
// presence of at least one '=' character is mandatory
|
|
if (fragments.count == 2 && (NSNotFound != [fragments[1] rangeOfString:@"="].location))
|
|
{
|
|
for (NSString *keyValue in [fragments[1] componentsSeparatedByString:@"&"])
|
|
{
|
|
// Get the parameter name
|
|
NSString *key = [keyValue componentsSeparatedByString:@"="][0];
|
|
|
|
// Get the parameter value
|
|
NSString *value = [keyValue componentsSeparatedByString:@"="][1];
|
|
if (value.length)
|
|
{
|
|
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
|
value = [value stringByRemovingPercentEncoding];
|
|
|
|
if ([key isEqualToString:@"via"])
|
|
{
|
|
// Special case the via parameter
|
|
// As we can have several of them, store each value into an array
|
|
if (!queryParams[key])
|
|
{
|
|
queryParams[key] = [NSMutableArray array];
|
|
}
|
|
|
|
if (![queryParams[key] containsObject:value])
|
|
{
|
|
[queryParams[key] addObject:value];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
queryParams[key] = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_pathParams = pathParams;
|
|
_queryParams = queryParams;
|
|
}
|
|
|
|
- (NSString *)homeserverUrl
|
|
{
|
|
return _queryParams[@"hs_url"];
|
|
}
|
|
|
|
- (NSString *)identityServerUrl
|
|
{
|
|
return _queryParams[@"is_url"];
|
|
}
|
|
|
|
- (NSArray<NSString *> *)via
|
|
{
|
|
NSArray<NSString *> *result = _queryParams[@"via"];
|
|
if (!result)
|
|
{
|
|
return @[];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
- (BOOL)isEqual:(id)other
|
|
{
|
|
if (other == self)
|
|
return YES;
|
|
|
|
if (![other isKindOfClass:UniversalLink.class])
|
|
return NO;
|
|
|
|
UniversalLink *otherLink = (UniversalLink *)other;
|
|
|
|
return [_url isEqual:otherLink.url]
|
|
&& [_pathParams isEqualToArray:otherLink.pathParams]
|
|
&& [_queryParams isEqualToDictionary:otherLink.queryParams];
|
|
}
|
|
|
|
- (NSUInteger)hash
|
|
{
|
|
NSUInteger prime = 31;
|
|
NSUInteger result = 1;
|
|
|
|
result = prime * result + [_url hash];
|
|
result = prime * result + [_pathParams hash];
|
|
result = prime * result + [_queryParams hash];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (NSString *)description
|
|
{
|
|
return [NSString stringWithFormat:@"<UniversalLink: %@>", _url.absoluteString];
|
|
}
|
|
|
|
#pragma mark - NSCopying
|
|
- (id)copyWithZone:(NSZone *)zone
|
|
{
|
|
UniversalLink *link = [[self.class allocWithZone:zone] init];
|
|
|
|
link->_url = [_url copyWithZone:zone];
|
|
link->_pathParams = [_pathParams copyWithZone:zone];
|
|
link->_queryParams = [_queryParams copyWithZone:zone];
|
|
|
|
return link;
|
|
}
|
|
|
|
@end
|