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