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