mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
189 lines
7.7 KiB
Objective-C
Executable File
189 lines
7.7 KiB
Objective-C
Executable File
//
|
|
// STPFormEncoder.m
|
|
// Stripe
|
|
//
|
|
// Created by Jack Flintermann on 1/8/15.
|
|
// Copyright (c) 2015 Stripe, Inc. All rights reserved.
|
|
//
|
|
|
|
#import "STPFormEncoder.h"
|
|
#import "STPCardParams.h"
|
|
|
|
FOUNDATION_EXPORT NSString * STPPercentEscapedStringFromString(NSString *string);
|
|
FOUNDATION_EXPORT NSString * STPQueryStringFromParameters(NSDictionary *parameters);
|
|
|
|
@implementation STPFormEncoder
|
|
|
|
+ (NSString *)stringByReplacingSnakeCaseWithCamelCase:(NSString *)input {
|
|
NSArray *parts = [input componentsSeparatedByString:@"_"];
|
|
NSMutableString *camelCaseParam = [NSMutableString string];
|
|
[parts enumerateObjectsUsingBlock:^(NSString *part, NSUInteger idx, __unused BOOL *stop) {
|
|
[camelCaseParam appendString:(idx == 0 ? part : [part capitalizedString])];
|
|
}];
|
|
|
|
return [camelCaseParam copy];
|
|
}
|
|
|
|
+ (nonnull NSData *)formEncodedDataForObject:(nonnull NSObject<STPFormEncodable> *)object {
|
|
NSDictionary *keyPairs = [self keyPairDictionaryForObject:object];
|
|
NSString *rootObjectName = [object.class rootObjectName];
|
|
NSDictionary *dict = rootObjectName != nil ? @{ rootObjectName: keyPairs } : keyPairs;
|
|
return [STPQueryStringFromParameters(dict) dataUsingEncoding:NSUTF8StringEncoding];
|
|
}
|
|
|
|
+ (NSDictionary *)keyPairDictionaryForObject:(nonnull NSObject<STPFormEncodable> *)object {
|
|
NSMutableDictionary *keyPairs = [NSMutableDictionary dictionary];
|
|
[[object.class propertyNamesToFormFieldNamesMapping] enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull propertyName, NSString * _Nonnull formFieldName, __unused BOOL * _Nonnull stop) {
|
|
id value = [self formEncodableValueForObject:[object valueForKey:propertyName]];
|
|
if (value) {
|
|
keyPairs[formFieldName] = value;
|
|
}
|
|
}];
|
|
[object.additionalAPIParameters enumerateKeysAndObjectsUsingBlock:^(id _Nonnull additionalFieldName, id _Nonnull additionalFieldValue, __unused BOOL * _Nonnull stop) {
|
|
id value = [self formEncodableValueForObject:additionalFieldValue];
|
|
if (value) {
|
|
keyPairs[additionalFieldName] = value;
|
|
}
|
|
}];
|
|
return [keyPairs copy];
|
|
}
|
|
|
|
+ (id)formEncodableValueForObject:(NSObject *)object {
|
|
if ([object conformsToProtocol:@protocol(STPFormEncodable)]) {
|
|
return [self keyPairDictionaryForObject:(NSObject<STPFormEncodable>*)object];
|
|
} else {
|
|
return object;
|
|
}
|
|
}
|
|
|
|
+ (NSString *)stringByURLEncoding:(NSString *)string {
|
|
return STPPercentEscapedStringFromString(string);
|
|
}
|
|
|
|
+ (NSString *)queryStringFromParameters:(NSDictionary *)parameters {
|
|
return STPQueryStringFromParameters(parameters);
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
// This code is adapted from https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFURLRequestSerialization.m . The only modifications are to replace the AF namespace with the STP namespace to avoid collisions with apps that are using both Stripe and AFNetworking.
|
|
NSString * STPPercentEscapedStringFromString(NSString *string) {
|
|
static NSString * const kSTPCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
|
|
static NSString * const kSTPCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
|
|
|
|
NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
|
|
[allowedCharacterSet removeCharactersInString:[kSTPCharactersGeneralDelimitersToEncode stringByAppendingString:kSTPCharactersSubDelimitersToEncode]];
|
|
|
|
// FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028
|
|
// return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
|
|
|
|
static NSUInteger const batchSize = 50;
|
|
|
|
NSUInteger index = 0;
|
|
NSMutableString *escaped = @"".mutableCopy;
|
|
|
|
while (index < string.length) {
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wgnu"
|
|
NSUInteger length = MIN(string.length - index, batchSize);
|
|
#pragma GCC diagnostic pop
|
|
NSRange range = NSMakeRange(index, length);
|
|
|
|
// To avoid breaking up character sequences such as 👴🏻👮🏽
|
|
range = [string rangeOfComposedCharacterSequencesForRange:range];
|
|
|
|
NSString *substring = [string substringWithRange:range];
|
|
NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
|
|
[escaped appendString:encoded];
|
|
|
|
index += range.length;
|
|
}
|
|
|
|
return escaped;
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
@interface STPQueryStringPair : NSObject
|
|
@property (readwrite, nonatomic, strong) id field;
|
|
@property (readwrite, nonatomic, strong) id value;
|
|
|
|
- (instancetype)initWithField:(id)field value:(id)value;
|
|
|
|
- (NSString *)URLEncodedStringValue;
|
|
@end
|
|
|
|
@implementation STPQueryStringPair
|
|
|
|
- (instancetype)initWithField:(id)field value:(id)value {
|
|
self = [super init];
|
|
if (!self) {
|
|
return nil;
|
|
}
|
|
|
|
_field = field;
|
|
_value = value;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (NSString *)URLEncodedStringValue {
|
|
if (!self.value || [self.value isEqual:[NSNull null]]) {
|
|
return STPPercentEscapedStringFromString([self.field description]);
|
|
} else {
|
|
return [NSString stringWithFormat:@"%@=%@", STPPercentEscapedStringFromString([self.field description]), STPPercentEscapedStringFromString([self.value description])];
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
#pragma mark -
|
|
|
|
FOUNDATION_EXPORT NSArray * STPQueryStringPairsFromDictionary(NSDictionary *dictionary);
|
|
FOUNDATION_EXPORT NSArray * STPQueryStringPairsFromKeyAndValue(NSString *key, id value);
|
|
|
|
NSString * STPQueryStringFromParameters(NSDictionary *parameters) {
|
|
NSMutableArray *mutablePairs = [NSMutableArray array];
|
|
for (STPQueryStringPair *pair in STPQueryStringPairsFromDictionary(parameters)) {
|
|
[mutablePairs addObject:[pair URLEncodedStringValue]];
|
|
}
|
|
|
|
return [mutablePairs componentsJoinedByString:@"&"];
|
|
}
|
|
|
|
NSArray * STPQueryStringPairsFromDictionary(NSDictionary *dictionary) {
|
|
return STPQueryStringPairsFromKeyAndValue(nil, dictionary);
|
|
}
|
|
|
|
NSArray * STPQueryStringPairsFromKeyAndValue(NSString *key, id value) {
|
|
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
|
|
NSString *descriptionSelector = NSStringFromSelector(@selector(description));
|
|
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:descriptionSelector ascending:YES selector:@selector(compare:)];
|
|
|
|
if ([value isKindOfClass:[NSDictionary class]]) {
|
|
NSDictionary *dictionary = value;
|
|
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
|
|
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
|
|
id nestedValue = dictionary[nestedKey];
|
|
if (nestedValue) {
|
|
[mutableQueryStringComponents addObjectsFromArray:STPQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
|
|
}
|
|
}
|
|
} else if ([value isKindOfClass:[NSArray class]]) {
|
|
NSArray *array = value;
|
|
for (id nestedValue in array) {
|
|
[mutableQueryStringComponents addObjectsFromArray:STPQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
|
|
}
|
|
} else if ([value isKindOfClass:[NSSet class]]) {
|
|
NSSet *set = value;
|
|
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
|
|
[mutableQueryStringComponents addObjectsFromArray:STPQueryStringPairsFromKeyAndValue(key, obj)];
|
|
}
|
|
} else {
|
|
[mutableQueryStringComponents addObject:[[STPQueryStringPair alloc] initWithField:key value:value]];
|
|
}
|
|
|
|
return mutableQueryStringComponents;
|
|
}
|