1// Copyright (c) 2020, Google Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above 11// copyright notice, this list of conditions and the following disclaimer 12// in the documentation and/or other materials provided with the 13// distribution. 14// * Neither the name of Google Inc. nor the names of its 15// contributors may be used to endorse or promote products derived from 16// this software without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30#import "SymbolCollectorClient.h" 31 32#import "HTTPGetRequest.h" 33#import "HTTPSimplePostRequest.h" 34 35@implementation UploadURLResponse 36 37//============================================================================= 38- (id)initWithUploadURL:(NSString*)uploadURL 39 withUploadKey:(NSString*)uploadKey { 40 if (self = [super init]) { 41 uploadURL_ = [uploadURL copy]; 42 uploadKey_ = [uploadKey copy]; 43 } 44 return self; 45} 46 47//============================================================================= 48- (void)dealloc { 49 [uploadURL_ release]; 50 [uploadKey_ release]; 51 52 [super dealloc]; 53} 54 55//============================================================================= 56- (NSString*)uploadURL { 57 return uploadURL_; 58} 59 60//============================================================================= 61- (NSString*)uploadKey { 62 return uploadKey_; 63} 64@end 65 66@implementation SymbolCollectorClient 67 68//============================================================================= 69+ (SymbolStatus)checkSymbolStatusOnServer:(NSString*)APIURL 70 withAPIKey:(NSString*)APIKey 71 withDebugFile:(NSString*)debugFile 72 withDebugID:(NSString*)debugID { 73 // Note that forward-slash is listed as a character to escape here, for 74 // completeness, however it is illegal in a debugFile input. 75 NSMutableCharacterSet* allowedDebugFileCharacters = [NSMutableCharacterSet 76 characterSetWithCharactersInString:@" \"\\/#%:?@|^`{}<>[]&=;"]; 77 [allowedDebugFileCharacters 78 formUnionWithCharacterSet:[NSCharacterSet controlCharacterSet]]; 79 [allowedDebugFileCharacters invert]; 80 NSString* escapedDebugFile = 81 [debugFile stringByAddingPercentEncodingWithAllowedCharacters: 82 allowedDebugFileCharacters]; 83 84 NSURL* URL = [NSURL 85 URLWithString:[NSString 86 stringWithFormat:@"%@/v1/symbols/%@/%@:checkStatus" 87 @"?key=%@", 88 APIURL, escapedDebugFile, debugID, 89 APIKey]]; 90 91 HTTPGetRequest* getRequest = [[HTTPGetRequest alloc] initWithURL:URL]; 92 NSError* error = nil; 93 NSData* data = [getRequest send:&error]; 94 NSString* result = [[NSString alloc] initWithData:data 95 encoding:NSUTF8StringEncoding]; 96 int responseCode = [[getRequest response] statusCode]; 97 [getRequest release]; 98 99 if (error || responseCode != 200) { 100 fprintf(stdout, "Failed to check symbol status.\n"); 101 fprintf(stdout, "Response code: %d\n", responseCode); 102 fprintf(stdout, "Response:\n"); 103 fprintf(stdout, "%s\n", [result UTF8String]); 104 return SymbolStatusUnknown; 105 } 106 107 error = nil; 108 NSRegularExpression* statusRegex = [NSRegularExpression 109 regularExpressionWithPattern:@"\"status\": \"([^\"]+)\"" 110 options:0 111 error:&error]; 112 NSArray* matches = 113 [statusRegex matchesInString:result 114 options:0 115 range:NSMakeRange(0, [result length])]; 116 if ([matches count] != 1) { 117 fprintf(stdout, "Failed to parse check symbol status response."); 118 fprintf(stdout, "Response:\n"); 119 fprintf(stdout, "%s\n", [result UTF8String]); 120 return SymbolStatusUnknown; 121 } 122 123 NSString* status = [result substringWithRange:[matches[0] rangeAtIndex:1]]; 124 [result release]; 125 126 return [status isEqualToString:@"FOUND"] ? SymbolStatusFound 127 : SymbolStatusMissing; 128} 129 130//============================================================================= 131+ (UploadURLResponse*)createUploadURLOnServer:(NSString*)APIURL 132 withAPIKey:(NSString*)APIKey { 133 NSURL* URL = [NSURL 134 URLWithString:[NSString stringWithFormat:@"%@/v1/uploads:create?key=%@", 135 APIURL, APIKey]]; 136 137 HTTPSimplePostRequest* postRequest = 138 [[HTTPSimplePostRequest alloc] initWithURL:URL]; 139 NSError* error = nil; 140 NSData* data = [postRequest send:&error]; 141 NSString* result = [[NSString alloc] initWithData:data 142 encoding:NSUTF8StringEncoding]; 143 int responseCode = [[postRequest response] statusCode]; 144 [postRequest release]; 145 146 if (error || responseCode != 200) { 147 fprintf(stdout, "Failed to create upload URL.\n"); 148 fprintf(stdout, "Response code: %d\n", responseCode); 149 fprintf(stdout, "Response:\n"); 150 fprintf(stdout, "%s\n", [result UTF8String]); 151 return nil; 152 } 153 154 // Note camel-case rather than underscores. 155 NSRegularExpression* uploadURLRegex = [NSRegularExpression 156 regularExpressionWithPattern:@"\"uploadUrl\": \"([^\"]+)\"" 157 options:0 158 error:&error]; 159 NSRegularExpression* uploadKeyRegex = [NSRegularExpression 160 regularExpressionWithPattern:@"\"uploadKey\": \"([^\"]+)\"" 161 options:0 162 error:&error]; 163 164 NSArray* uploadURLMatches = 165 [uploadURLRegex matchesInString:result 166 options:0 167 range:NSMakeRange(0, [result length])]; 168 NSArray* uploadKeyMatches = 169 [uploadKeyRegex matchesInString:result 170 options:0 171 range:NSMakeRange(0, [result length])]; 172 if ([uploadURLMatches count] != 1 || [uploadKeyMatches count] != 1) { 173 fprintf(stdout, "Failed to parse create url response."); 174 fprintf(stdout, "Response:\n"); 175 fprintf(stdout, "%s\n", [result UTF8String]); 176 return nil; 177 } 178 NSString* uploadURL = 179 [result substringWithRange:[uploadURLMatches[0] rangeAtIndex:1]]; 180 NSString* uploadKey = 181 [result substringWithRange:[uploadKeyMatches[0] rangeAtIndex:1]]; 182 183 return [[UploadURLResponse alloc] initWithUploadURL:uploadURL 184 withUploadKey:uploadKey]; 185} 186 187//============================================================================= 188+ (CompleteUploadResult)completeUploadOnServer:(NSString*)APIURL 189 withAPIKey:(NSString*)APIKey 190 withUploadKey:(NSString*)uploadKey 191 withDebugFile:(NSString*)debugFile 192 withDebugID:(NSString*)debugID 193 withType:(NSString*)type { 194 NSURL* URL = [NSURL 195 URLWithString:[NSString 196 stringWithFormat:@"%@/v1/uploads/%@:complete?key=%@", 197 APIURL, uploadKey, APIKey]]; 198 199 NSDictionary* symbolIdDictionary = 200 [NSDictionary dictionaryWithObjectsAndKeys:debugFile, @"debug_file", 201 debugID, @"debug_id", nil]; 202 NSDictionary* jsonDictionary = [NSDictionary 203 dictionaryWithObjectsAndKeys:symbolIdDictionary, @"symbol_id", type, 204 @"symbol_upload_type", nil]; 205 NSError* error = nil; 206 NSData* jsonData = 207 [NSJSONSerialization dataWithJSONObject:jsonDictionary 208 options:NSJSONWritingPrettyPrinted 209 error:&error]; 210 if (jsonData == nil) { 211 fprintf(stdout, "Error: %s\n", [[error localizedDescription] UTF8String]); 212 fprintf(stdout, 213 "Failed to complete upload. Could not write JSON payload.\n"); 214 return CompleteUploadResultError; 215 } 216 217 NSString* body = [[NSString alloc] initWithData:jsonData 218 encoding:NSUTF8StringEncoding]; 219 HTTPSimplePostRequest* postRequest = 220 [[HTTPSimplePostRequest alloc] initWithURL:URL]; 221 [postRequest setBody:body]; 222 [postRequest setContentType:@"application/json"]; 223 224 NSData* data = [postRequest send:&error]; 225 if (data == nil) { 226 fprintf(stdout, "Error: %s\n", [[error localizedDescription] UTF8String]); 227 fprintf(stdout, "Failed to complete upload URL.\n"); 228 return CompleteUploadResultError; 229 } 230 231 NSString* result = [[NSString alloc] initWithData:data 232 encoding:NSUTF8StringEncoding]; 233 int responseCode = [[postRequest response] statusCode]; 234 [postRequest release]; 235 if (responseCode != 200) { 236 fprintf(stdout, "Failed to complete upload URL.\n"); 237 fprintf(stdout, "Response code: %d\n", responseCode); 238 fprintf(stdout, "Response:\n"); 239 fprintf(stdout, "%s\n", [result UTF8String]); 240 return CompleteUploadResultError; 241 } 242 243 // Note camel-case rather than underscores. 244 NSRegularExpression* completeResultRegex = [NSRegularExpression 245 regularExpressionWithPattern:@"\"result\": \"([^\"]+)\"" 246 options:0 247 error:&error]; 248 249 NSArray* completeResultMatches = 250 [completeResultRegex matchesInString:result 251 options:0 252 range:NSMakeRange(0, [result length])]; 253 254 if ([completeResultMatches count] != 1) { 255 fprintf(stdout, "Failed to parse complete upload response."); 256 fprintf(stdout, "Response:\n"); 257 fprintf(stdout, "%s\n", [result UTF8String]); 258 return CompleteUploadResultError; 259 } 260 NSString* completeResult = 261 [result substringWithRange:[completeResultMatches[0] rangeAtIndex:1]]; 262 [result release]; 263 264 return ([completeResult isEqualToString:@"DUPLICATE_DATA"]) 265 ? CompleteUploadResultDuplicateData 266 : CompleteUploadResultOk; 267} 268@end 269