xref: /aosp_15_r20/external/google-breakpad/src/common/mac/SymbolCollectorClient.m (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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