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