AutorÃa | Ultima modificación | Ver Log |
// Copyright 2019 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 "Crashlytics/Shared/FIRCLSNetworking/FIRCLSMultipartMimeStreamEncoder.h"#import "Crashlytics/Crashlytics/Helpers/FIRCLSLogger.h"#import "Crashlytics/Shared/FIRCLSByteUtility.h"#import "Crashlytics/Shared/FIRCLSUUID.h"@interface FIRCLSMultipartMimeStreamEncoder () <NSStreamDelegate>@property(nonatomic) NSUInteger length;@property(nonatomic, copy) NSString *boundary;@property(nonatomic, copy, readonly) NSData *headerData;@property(nonatomic, copy, readonly) NSData *footerData;@property(nonatomic, strong) NSOutputStream *outputStream;@end@implementation FIRCLSMultipartMimeStreamEncoder+ (void)populateRequest:(NSMutableURLRequest *)requestwithDataFromEncoder:(void (^)(FIRCLSMultipartMimeStreamEncoder *encoder))block {NSString *boundary = [self generateBoundary];NSOutputStream *stream = [NSOutputStream outputStreamToMemory];FIRCLSMultipartMimeStreamEncoder *encoder =[[FIRCLSMultipartMimeStreamEncoder alloc] initWithStream:stream andBoundary:boundary];[encoder encode:^{block(encoder);}];[request setValue:encoder.contentTypeHTTPHeaderValue forHTTPHeaderField:@"Content-Type"];[request setValue:encoder.contentLengthHTTPHeaderValue forHTTPHeaderField:@"Content-Length"];NSData *data = [stream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];request.HTTPBody = data;}+ (NSString *)contentTypeHTTPHeaderValueWithBoundary:(NSString *)boundary {return [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];}+ (instancetype)encoderWithStream:(NSOutputStream *)stream andBoundary:(NSString *)boundary {return [[self alloc] initWithStream:stream andBoundary:boundary];}+ (NSString *)generateBoundary {return FIRCLSGenerateUUID();}- (instancetype)initWithStream:(NSOutputStream *)stream andBoundary:(NSString *)boundary {self = [super init];if (!self) {return nil;}self.outputStream = stream;if (!boundary) {boundary = [FIRCLSMultipartMimeStreamEncoder generateBoundary];}_boundary = boundary;return self;}- (void)encode:(void (^)(void))block {[self beginEncoding];block();[self endEncoding];}- (NSString *)contentTypeHTTPHeaderValue {return [[self class] contentTypeHTTPHeaderValueWithBoundary:self.boundary];}- (NSString *)contentLengthHTTPHeaderValue {return [NSString stringWithFormat:@"%lu", (unsigned long)_length];}#pragma - mark MIME part API- (void)beginEncoding {_length = 0;[self.outputStream open];[self writeData:self.headerData];}- (void)endEncoding {[self writeData:self.footerData];[self.outputStream close];}- (NSData *)headerData {return [@"MIME-Version: 1.0\r\n" dataUsingEncoding:NSUTF8StringEncoding];}- (NSData *)footerData {return [[NSString stringWithFormat:@"--%@--\r\n", self.boundary]dataUsingEncoding:NSUTF8StringEncoding];}- (void)addFileData:(NSData *)datafileName:(NSString *)fileNamemimeType:(NSString *)mimeTypefieldName:(NSString *)name {if ([data length] == 0) {FIRCLSErrorLog(@"Unable to MIME encode data with zero length (%@)", name);return;}if ([name length] == 0 || [fileName length] == 0) {FIRCLSErrorLog(@"name (%@) or fieldname (%@) is invalid", name, fileName);return;}NSMutableString *string;string = [NSMutableStringstringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n",self.boundary, name, fileName];if (mimeType) {[string appendFormat:@"Content-Type: %@\r\n", mimeType];[string appendString:@"Content-Transfer-Encoding: binary\r\n\r\n"];} else {[string appendString:@"Content-Type: application/octet-stream\r\n\r\n"];}[self writeData:[string dataUsingEncoding:NSUTF8StringEncoding]];[self writeData:data];[self writeData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];}- (void)addValue:(id)value fieldName:(NSString *)name {if ([name length] == 0 || !value || value == NSNull.null) {FIRCLSErrorLog(@"name (%@) or value (%@) is invalid", name, value);return;}NSMutableString *string;string =[NSMutableString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n",self.boundary, name];[string appendString:@"Content-Type: text/plain\r\n\r\n"];[string appendFormat:@"%@\r\n", value];[self writeData:[string dataUsingEncoding:NSUTF8StringEncoding]];}- (void)addFile:(NSURL *)fileURLfileName:(NSString *)fileNamemimeType:(NSString *)mimeTypefieldName:(NSString *)name {NSData *data = [NSData dataWithContentsOfURL:fileURL];[self addFileData:data fileName:fileName mimeType:mimeType fieldName:name];}- (BOOL)writeBytes:(const void *)bytes ofLength:(NSUInteger)length {if ([self.outputStream write:bytes maxLength:length] != length) {FIRCLSErrorLog(@"Failed to write bytes to stream");return NO;}_length += length;return YES;}- (void)writeData:(NSData *)data {FIRCLSEnumerateByteRangesOfNSDataUsingBlock(data, ^(const void *bytes, NSRange byteRange, BOOL *stop) {NSUInteger length = byteRange.length;if ([self.outputStream write:bytes maxLength:length] != length) {FIRCLSErrorLog(@"Failed to write data to stream");*stop = YES;return;}self->_length += length;});}@end