├── hopper_api.py ├── hopper_helper ├── HopperTweakStudio │ ├── Info.plist │ └── launch_shim.m ├── Podfile ├── Podfile.lock ├── Pods │ ├── GCDWebServer │ │ ├── GCDWebServer │ │ │ ├── Core │ │ │ │ ├── GCDWebServer.h │ │ │ │ ├── GCDWebServer.m │ │ │ │ ├── GCDWebServerConnection.h │ │ │ │ ├── GCDWebServerConnection.m │ │ │ │ ├── GCDWebServerFunctions.h │ │ │ │ ├── GCDWebServerFunctions.m │ │ │ │ ├── GCDWebServerHTTPStatusCodes.h │ │ │ │ ├── GCDWebServerPrivate.h │ │ │ │ ├── GCDWebServerRequest.h │ │ │ │ ├── GCDWebServerRequest.m │ │ │ │ ├── GCDWebServerResponse.h │ │ │ │ └── GCDWebServerResponse.m │ │ │ ├── Requests │ │ │ │ ├── GCDWebServerDataRequest.h │ │ │ │ ├── GCDWebServerDataRequest.m │ │ │ │ ├── GCDWebServerFileRequest.h │ │ │ │ ├── GCDWebServerFileRequest.m │ │ │ │ ├── GCDWebServerMultiPartFormRequest.h │ │ │ │ ├── GCDWebServerMultiPartFormRequest.m │ │ │ │ ├── GCDWebServerURLEncodedFormRequest.h │ │ │ │ └── GCDWebServerURLEncodedFormRequest.m │ │ │ └── Responses │ │ │ │ ├── GCDWebServerDataResponse.h │ │ │ │ ├── GCDWebServerDataResponse.m │ │ │ │ ├── GCDWebServerErrorResponse.h │ │ │ │ ├── GCDWebServerErrorResponse.m │ │ │ │ ├── GCDWebServerFileResponse.h │ │ │ │ ├── GCDWebServerFileResponse.m │ │ │ │ ├── GCDWebServerStreamedResponse.h │ │ │ │ └── GCDWebServerStreamedResponse.m │ │ ├── LICENSE │ │ └── README.md │ ├── Headers │ │ ├── Private │ │ │ └── GCDWebServer │ │ │ │ ├── GCDWebServer.h │ │ │ │ ├── GCDWebServerConnection.h │ │ │ │ ├── GCDWebServerDataRequest.h │ │ │ │ ├── GCDWebServerDataResponse.h │ │ │ │ ├── GCDWebServerErrorResponse.h │ │ │ │ ├── GCDWebServerFileRequest.h │ │ │ │ ├── GCDWebServerFileResponse.h │ │ │ │ ├── GCDWebServerFunctions.h │ │ │ │ ├── GCDWebServerHTTPStatusCodes.h │ │ │ │ ├── GCDWebServerMultiPartFormRequest.h │ │ │ │ ├── GCDWebServerPrivate.h │ │ │ │ ├── GCDWebServerRequest.h │ │ │ │ ├── GCDWebServerResponse.h │ │ │ │ ├── GCDWebServerStreamedResponse.h │ │ │ │ └── GCDWebServerURLEncodedFormRequest.h │ │ └── Public │ │ │ └── GCDWebServer │ │ │ ├── GCDWebServer.h │ │ │ ├── GCDWebServerConnection.h │ │ │ ├── GCDWebServerDataRequest.h │ │ │ ├── GCDWebServerDataResponse.h │ │ │ ├── GCDWebServerErrorResponse.h │ │ │ ├── GCDWebServerFileRequest.h │ │ │ ├── GCDWebServerFileResponse.h │ │ │ ├── GCDWebServerFunctions.h │ │ │ ├── GCDWebServerHTTPStatusCodes.h │ │ │ ├── GCDWebServerMultiPartFormRequest.h │ │ │ ├── GCDWebServerRequest.h │ │ │ ├── GCDWebServerResponse.h │ │ │ ├── GCDWebServerStreamedResponse.h │ │ │ └── GCDWebServerURLEncodedFormRequest.h │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ ├── project.pbxproj │ │ └── xcuserdata │ │ │ └── ethanarbuckle.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── GCDWebServer.xcscheme │ │ │ ├── Pods-hopper_helper.xcscheme │ │ │ └── xcschememanagement.plist │ └── Target Support Files │ │ ├── GCDWebServer │ │ ├── GCDWebServer-dummy.m │ │ ├── GCDWebServer-prefix.pch │ │ ├── GCDWebServer.debug.xcconfig │ │ └── GCDWebServer.release.xcconfig │ │ └── Pods-hopper_helper │ │ ├── Pods-hopper_helper-acknowledgements.markdown │ │ ├── Pods-hopper_helper-acknowledgements.plist │ │ ├── Pods-hopper_helper-dummy.m │ │ ├── Pods-hopper_helper.debug.xcconfig │ │ └── Pods-hopper_helper.release.xcconfig ├── README.md ├── hopper_helper.xcodeproj │ └── project.pbxproj ├── hopper_helper.xcworkspace │ └── contents.xcworkspacedata └── plugin │ ├── handlers.h │ ├── handlers.m │ └── hopper_backend.m ├── hopper_proxy.py ├── lzssdec ├── patch_tcc_db.py └── run_hopper.py /hopper_helper/HopperTweakStudio/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | HopperTweakStudio_shim 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | CFPlugInDynamicRegistration 22 | NO 23 | CFPlugInFactories 24 | 25 | 00000000-0000-0000-0000-000000000000 26 | MyFactoryFunction 27 | 28 | CFPlugInTypes 29 | 30 | 00000000-0000-0000-0000-000000000000 31 | 32 | 00000000-0000-0000-0000-000000000000 33 | 34 | 35 | CFPlugInUnloadFunction 36 | 37 | NSHumanReadableCopyright 38 | Copyright © Ethan Arbuckle. All rights reserved. 39 | NSPrincipalClass 40 | MIPSCPU 41 | 42 | 43 | -------------------------------------------------------------------------------- /hopper_helper/HopperTweakStudio/launch_shim.m: -------------------------------------------------------------------------------- 1 | // 2 | // launch_shim.m 3 | // launch_shim 4 | // 5 | // Created by Ethan Arbuckle on 4/10/21. 6 | // 7 | 8 | #include 9 | #include 10 | 11 | // Hopper will refuse to load plugins that have unexpected load commands or utilize common posix symbols. 12 | // To work around this, this minimal binary is loaded by Hopper, and from here the real plugin is opened. 13 | 14 | __attribute__((constructor)) void init(void) { 15 | 16 | NSLog(@"TweakStudio loader injected"); 17 | 18 | // Hopper does not allow plugins to use dlopen(), but it does not restrict dlsym(). 19 | // Use dlsym to get the dlopen pointer, then load the real plugin dylib 20 | void *(*sym_dlopen)(const char *, int) = dlsym(RTLD_DEFAULT, "dlopen"); 21 | const char *dylib = [[@"~/Library/Application Support/Hopper/PlugIns/v4/CPUs/HopperTweakStudio.hopperCPU/HopperTweakStudio" stringByExpandingTildeInPath] UTF8String]; 22 | sym_dlopen(dylib, RTLD_NOW); 23 | } 24 | -------------------------------------------------------------------------------- /hopper_helper/Podfile: -------------------------------------------------------------------------------- 1 | target 'pluginBackend' do 2 | pod "GCDWebServer", "~> 3.0" 3 | end 4 | -------------------------------------------------------------------------------- /hopper_helper/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - GCDWebServer (3.5.4): 3 | - GCDWebServer/Core (= 3.5.4) 4 | - GCDWebServer/Core (3.5.4) 5 | 6 | DEPENDENCIES: 7 | - GCDWebServer (~> 3.0) 8 | 9 | SPEC REPOS: 10 | trunk: 11 | - GCDWebServer 12 | 13 | SPEC CHECKSUMS: 14 | GCDWebServer: 2c156a56c8226e2d5c0c3f208a3621ccffbe3ce4 15 | 16 | PODFILE CHECKSUM: 278b91d4fba272f8f3d2e61a0855a98125a6b524 17 | 18 | COCOAPODS: 1.10.1 19 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerConnection.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServer.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | @class GCDWebServerHandler; 33 | 34 | /** 35 | * The GCDWebServerConnection class is instantiated by GCDWebServer to handle 36 | * each new HTTP connection. Each instance stays alive until the connection is 37 | * closed. 38 | * 39 | * You cannot use this class directly, but it is made public so you can 40 | * subclass it to override some hooks. Use the GCDWebServerOption_ConnectionClass 41 | * option for GCDWebServer to install your custom subclass. 42 | * 43 | * @warning The GCDWebServerConnection retains the GCDWebServer until the 44 | * connection is closed. 45 | */ 46 | @interface GCDWebServerConnection : NSObject 47 | 48 | /** 49 | * Returns the GCDWebServer that owns the connection. 50 | */ 51 | @property(nonatomic, readonly) GCDWebServer* server; 52 | 53 | /** 54 | * Returns YES if the connection is using IPv6. 55 | */ 56 | @property(nonatomic, readonly, getter=isUsingIPv6) BOOL usingIPv6; 57 | 58 | /** 59 | * Returns the address of the local peer (i.e. server) of the connection 60 | * as a raw "struct sockaddr". 61 | */ 62 | @property(nonatomic, readonly) NSData* localAddressData; 63 | 64 | /** 65 | * Returns the address of the local peer (i.e. server) of the connection 66 | * as a string. 67 | */ 68 | @property(nonatomic, readonly) NSString* localAddressString; 69 | 70 | /** 71 | * Returns the address of the remote peer (i.e. client) of the connection 72 | * as a raw "struct sockaddr". 73 | */ 74 | @property(nonatomic, readonly) NSData* remoteAddressData; 75 | 76 | /** 77 | * Returns the address of the remote peer (i.e. client) of the connection 78 | * as a string. 79 | */ 80 | @property(nonatomic, readonly) NSString* remoteAddressString; 81 | 82 | /** 83 | * Returns the total number of bytes received from the remote peer (i.e. client) 84 | * so far. 85 | */ 86 | @property(nonatomic, readonly) NSUInteger totalBytesRead; 87 | 88 | /** 89 | * Returns the total number of bytes sent to the remote peer (i.e. client) so far. 90 | */ 91 | @property(nonatomic, readonly) NSUInteger totalBytesWritten; 92 | 93 | @end 94 | 95 | /** 96 | * Hooks to customize the behavior of GCDWebServer HTTP connections. 97 | * 98 | * @warning These methods can be called on any GCD thread. 99 | * Be sure to also call "super" when overriding them. 100 | */ 101 | @interface GCDWebServerConnection (Subclassing) 102 | 103 | /** 104 | * This method is called when the connection is opened. 105 | * 106 | * Return NO to reject the connection e.g. after validating the local 107 | * or remote address. 108 | */ 109 | - (BOOL)open; 110 | 111 | /** 112 | * This method is called whenever data has been received 113 | * from the remote peer (i.e. client). 114 | * 115 | * @warning Do not attempt to modify this data. 116 | */ 117 | - (void)didReadBytes:(const void*)bytes length:(NSUInteger)length; 118 | 119 | /** 120 | * This method is called whenever data has been sent 121 | * to the remote peer (i.e. client). 122 | * 123 | * @warning Do not attempt to modify this data. 124 | */ 125 | - (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length; 126 | 127 | /** 128 | * This method is called after the HTTP headers have been received to 129 | * allow replacing the request URL by another one. 130 | * 131 | * The default implementation returns the original URL. 132 | */ 133 | - (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers; 134 | 135 | /** 136 | * Assuming a valid HTTP request was received, this method is called before 137 | * the request is processed. 138 | * 139 | * Return a non-nil GCDWebServerResponse to bypass the request processing entirely. 140 | * 141 | * The default implementation checks for HTTP authentication if applicable 142 | * and returns a barebone 401 status code response if authentication failed. 143 | */ 144 | - (nullable GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request; 145 | 146 | /** 147 | * Assuming a valid HTTP request was received and -preflightRequest: returned nil, 148 | * this method is called to process the request by executing the handler's 149 | * process block. 150 | */ 151 | - (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion; 152 | 153 | /** 154 | * Assuming a valid HTTP request was received and either -preflightRequest: 155 | * or -processRequest:completion: returned a non-nil GCDWebServerResponse, 156 | * this method is called to override the response. 157 | * 158 | * You can either modify the current response and return it, or return a 159 | * completely new one. 160 | * 161 | * The default implementation replaces any response matching the "ETag" or 162 | * "Last-Modified-Date" header of the request by a barebone "Not-Modified" (304) 163 | * one. 164 | */ 165 | - (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request; 166 | 167 | /** 168 | * This method is called if any error happens while validing or processing 169 | * the request or if no GCDWebServerResponse was generated during processing. 170 | * 171 | * @warning If the request was invalid (e.g. the HTTP headers were malformed), 172 | * the "request" argument will be nil. 173 | */ 174 | - (void)abortRequest:(nullable GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode; 175 | 176 | /** 177 | * Called when the connection is closed. 178 | */ 179 | - (void)close; 180 | 181 | @end 182 | 183 | NS_ASSUME_NONNULL_END 184 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerFunctions.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | /** 37 | * Converts a file extension to the corresponding MIME type. 38 | * If there is no match, "application/octet-stream" is returned. 39 | * 40 | * Overrides allow to customize the built-in mapping from extensions to MIME 41 | * types. Keys of the dictionary must be lowercased file extensions without 42 | * the period, and the values must be the corresponding MIME types. 43 | */ 44 | NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* _Nullable overrides); 45 | 46 | /** 47 | * Add percent-escapes to a string so it can be used in a URL. 48 | * The legal characters ":@/?&=+" are also escaped to ensure compatibility 49 | * with URL encoded forms and URL queries. 50 | */ 51 | NSString* _Nullable GCDWebServerEscapeURLString(NSString* string); 52 | 53 | /** 54 | * Unescapes a URL percent-encoded string. 55 | */ 56 | NSString* _Nullable GCDWebServerUnescapeURLString(NSString* string); 57 | 58 | /** 59 | * Extracts the unescaped names and values from an 60 | * "application/x-www-form-urlencoded" form. 61 | * http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 62 | */ 63 | NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form); 64 | 65 | /** 66 | * On OS X, returns the IPv4 or IPv6 address as a string of the primary 67 | * connected service or nil if not available. 68 | * 69 | * On iOS, returns the IPv4 or IPv6 address as a string of the WiFi 70 | * interface if connected or nil otherwise. 71 | */ 72 | NSString* _Nullable GCDWebServerGetPrimaryIPAddress(BOOL useIPv6); 73 | 74 | /** 75 | * Converts a date into a string using RFC822 formatting. 76 | * https://tools.ietf.org/html/rfc822#section-5 77 | * https://tools.ietf.org/html/rfc1123#section-5.2.14 78 | */ 79 | NSString* GCDWebServerFormatRFC822(NSDate* date); 80 | 81 | /** 82 | * Converts a RFC822 formatted string into a date. 83 | * https://tools.ietf.org/html/rfc822#section-5 84 | * https://tools.ietf.org/html/rfc1123#section-5.2.14 85 | * 86 | * @warning Timezones other than GMT are not supported by this function. 87 | */ 88 | NSDate* _Nullable GCDWebServerParseRFC822(NSString* string); 89 | 90 | /** 91 | * Converts a date into a string using IOS 8601 formatting. 92 | * http://tools.ietf.org/html/rfc3339#section-5.6 93 | */ 94 | NSString* GCDWebServerFormatISO8601(NSDate* date); 95 | 96 | /** 97 | * Converts a ISO 8601 formatted string into a date. 98 | * http://tools.ietf.org/html/rfc3339#section-5.6 99 | * 100 | * @warning Only "calendar" variant is supported at this time and timezones 101 | * other than GMT are not supported either. 102 | */ 103 | NSDate* _Nullable GCDWebServerParseISO8601(NSString* string); 104 | 105 | /** 106 | * Removes "//", "/./" and "/../" components from path as well as any trailing slash. 107 | */ 108 | NSString* GCDWebServerNormalizePath(NSString* path); 109 | 110 | #ifdef __cplusplus 111 | } 112 | #endif 113 | 114 | NS_ASSUME_NONNULL_END 115 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerFunctions.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import 33 | #if TARGET_OS_IPHONE 34 | #import 35 | #else 36 | #import 37 | #endif 38 | #import 39 | 40 | #import 41 | #import 42 | #import 43 | 44 | #import "GCDWebServerPrivate.h" 45 | 46 | static NSDateFormatter* _dateFormatterRFC822 = nil; 47 | static NSDateFormatter* _dateFormatterISO8601 = nil; 48 | static dispatch_queue_t _dateFormatterQueue = NULL; 49 | 50 | // TODO: Handle RFC 850 and ANSI C's asctime() format 51 | void GCDWebServerInitializeFunctions() { 52 | GWS_DCHECK([NSThread isMainThread]); // NSDateFormatter should be initialized on main thread 53 | if (_dateFormatterRFC822 == nil) { 54 | _dateFormatterRFC822 = [[NSDateFormatter alloc] init]; 55 | _dateFormatterRFC822.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; 56 | _dateFormatterRFC822.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'"; 57 | _dateFormatterRFC822.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; 58 | GWS_DCHECK(_dateFormatterRFC822); 59 | } 60 | if (_dateFormatterISO8601 == nil) { 61 | _dateFormatterISO8601 = [[NSDateFormatter alloc] init]; 62 | _dateFormatterISO8601.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; 63 | _dateFormatterISO8601.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'+00:00'"; 64 | _dateFormatterISO8601.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; 65 | GWS_DCHECK(_dateFormatterISO8601); 66 | } 67 | if (_dateFormatterQueue == NULL) { 68 | _dateFormatterQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 69 | GWS_DCHECK(_dateFormatterQueue); 70 | } 71 | } 72 | 73 | NSString* GCDWebServerNormalizeHeaderValue(NSString* value) { 74 | if (value) { 75 | NSRange range = [value rangeOfString:@";"]; // Assume part before ";" separator is case-insensitive 76 | if (range.location != NSNotFound) { 77 | value = [[[value substringToIndex:range.location] lowercaseString] stringByAppendingString:[value substringFromIndex:range.location]]; 78 | } else { 79 | value = [value lowercaseString]; 80 | } 81 | } 82 | return value; 83 | } 84 | 85 | NSString* GCDWebServerTruncateHeaderValue(NSString* value) { 86 | if (value) { 87 | NSRange range = [value rangeOfString:@";"]; 88 | if (range.location != NSNotFound) { 89 | return [value substringToIndex:range.location]; 90 | } 91 | } 92 | return value; 93 | } 94 | 95 | NSString* GCDWebServerExtractHeaderValueParameter(NSString* value, NSString* name) { 96 | NSString* parameter = nil; 97 | if (value) { 98 | NSScanner* scanner = [[NSScanner alloc] initWithString:value]; 99 | [scanner setCaseSensitive:NO]; // Assume parameter names are case-insensitive 100 | NSString* string = [NSString stringWithFormat:@"%@=", name]; 101 | if ([scanner scanUpToString:string intoString:NULL]) { 102 | [scanner scanString:string intoString:NULL]; 103 | if ([scanner scanString:@"\"" intoString:NULL]) { 104 | [scanner scanUpToString:@"\"" intoString:¶meter]; 105 | } else { 106 | [scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:¶meter]; 107 | } 108 | } 109 | } 110 | return parameter; 111 | } 112 | 113 | // http://www.w3schools.com/tags/ref_charactersets.asp 114 | NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset) { 115 | NSStringEncoding encoding = kCFStringEncodingInvalidId; 116 | if (charset) { 117 | encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)charset)); 118 | } 119 | return (encoding != kCFStringEncodingInvalidId ? encoding : NSUTF8StringEncoding); 120 | } 121 | 122 | NSString* GCDWebServerFormatRFC822(NSDate* date) { 123 | __block NSString* string; 124 | dispatch_sync(_dateFormatterQueue, ^{ 125 | string = [_dateFormatterRFC822 stringFromDate:date]; 126 | }); 127 | return string; 128 | } 129 | 130 | NSDate* GCDWebServerParseRFC822(NSString* string) { 131 | __block NSDate* date; 132 | dispatch_sync(_dateFormatterQueue, ^{ 133 | date = [_dateFormatterRFC822 dateFromString:string]; 134 | }); 135 | return date; 136 | } 137 | 138 | NSString* GCDWebServerFormatISO8601(NSDate* date) { 139 | __block NSString* string; 140 | dispatch_sync(_dateFormatterQueue, ^{ 141 | string = [_dateFormatterISO8601 stringFromDate:date]; 142 | }); 143 | return string; 144 | } 145 | 146 | NSDate* GCDWebServerParseISO8601(NSString* string) { 147 | __block NSDate* date; 148 | dispatch_sync(_dateFormatterQueue, ^{ 149 | date = [_dateFormatterISO8601 dateFromString:string]; 150 | }); 151 | return date; 152 | } 153 | 154 | BOOL GCDWebServerIsTextContentType(NSString* type) { 155 | return ([type hasPrefix:@"text/"] || [type hasPrefix:@"application/json"] || [type hasPrefix:@"application/xml"]); 156 | } 157 | 158 | NSString* GCDWebServerDescribeData(NSData* data, NSString* type) { 159 | if (GCDWebServerIsTextContentType(type)) { 160 | NSString* charset = GCDWebServerExtractHeaderValueParameter(type, @"charset"); 161 | NSString* string = [[NSString alloc] initWithData:data encoding:GCDWebServerStringEncodingFromCharset(charset)]; 162 | if (string) { 163 | return string; 164 | } 165 | } 166 | return [NSString stringWithFormat:@"<%lu bytes>", (unsigned long)data.length]; 167 | } 168 | 169 | NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* overrides) { 170 | NSDictionary* builtInOverrides = @{@"css" : @"text/css"}; 171 | NSString* mimeType = nil; 172 | extension = [extension lowercaseString]; 173 | if (extension.length) { 174 | mimeType = [overrides objectForKey:extension]; 175 | if (mimeType == nil) { 176 | mimeType = [builtInOverrides objectForKey:extension]; 177 | } 178 | if (mimeType == nil) { 179 | CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); 180 | if (uti) { 181 | mimeType = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)); 182 | CFRelease(uti); 183 | } 184 | } 185 | } 186 | return mimeType ? mimeType : kGCDWebServerDefaultMimeType; 187 | } 188 | 189 | NSString* GCDWebServerEscapeURLString(NSString* string) { 190 | #pragma clang diagnostic push 191 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 192 | return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":@/?&=+"), kCFStringEncodingUTF8)); 193 | #pragma clang diagnostic pop 194 | } 195 | 196 | NSString* GCDWebServerUnescapeURLString(NSString* string) { 197 | #pragma clang diagnostic push 198 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 199 | return CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8)); 200 | #pragma clang diagnostic pop 201 | } 202 | 203 | NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) { 204 | NSMutableDictionary* parameters = [NSMutableDictionary dictionary]; 205 | NSScanner* scanner = [[NSScanner alloc] initWithString:form]; 206 | [scanner setCharactersToBeSkipped:nil]; 207 | while (1) { 208 | NSString* key = nil; 209 | if (![scanner scanUpToString:@"=" intoString:&key] || [scanner isAtEnd]) { 210 | break; 211 | } 212 | [scanner setScanLocation:([scanner scanLocation] + 1)]; 213 | 214 | NSString* value = nil; 215 | [scanner scanUpToString:@"&" intoString:&value]; 216 | if (value == nil) { 217 | value = @""; 218 | } 219 | 220 | key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "]; 221 | NSString* unescapedKey = key ? GCDWebServerUnescapeURLString(key) : nil; 222 | value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "]; 223 | NSString* unescapedValue = value ? GCDWebServerUnescapeURLString(value) : nil; 224 | if (unescapedKey && unescapedValue) { 225 | [parameters setObject:unescapedValue forKey:unescapedKey]; 226 | } else { 227 | GWS_LOG_WARNING(@"Failed parsing URL encoded form for key \"%@\" and value \"%@\"", key, value); 228 | GWS_DNOT_REACHED(); 229 | } 230 | 231 | if ([scanner isAtEnd]) { 232 | break; 233 | } 234 | [scanner setScanLocation:([scanner scanLocation] + 1)]; 235 | } 236 | return parameters; 237 | } 238 | 239 | NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService) { 240 | char hostBuffer[NI_MAXHOST]; 241 | char serviceBuffer[NI_MAXSERV]; 242 | if (getnameinfo(addr, addr->sa_len, hostBuffer, sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN) != 0) { 243 | #if DEBUG 244 | GWS_DNOT_REACHED(); 245 | #else 246 | return @""; 247 | #endif 248 | } 249 | return includeService ? [NSString stringWithFormat:@"%s:%s", hostBuffer, serviceBuffer] : (NSString*)[NSString stringWithUTF8String:hostBuffer]; 250 | } 251 | 252 | NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) { 253 | NSString* address = nil; 254 | #if TARGET_OS_IPHONE 255 | #if !TARGET_IPHONE_SIMULATOR && !TARGET_OS_TV 256 | const char* primaryInterface = "en0"; // WiFi interface on iOS 257 | #endif 258 | #else 259 | const char* primaryInterface = NULL; 260 | SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("GCDWebServer"), NULL, NULL); 261 | if (store) { 262 | CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4")); // There is no equivalent for IPv6 but the primary interface should be the same 263 | if (info) { 264 | NSString* interface = [(__bridge NSDictionary*)info objectForKey:@"PrimaryInterface"]; 265 | if (interface) { 266 | primaryInterface = [[NSString stringWithString:interface] UTF8String]; // Copy string to auto-release pool 267 | } 268 | CFRelease(info); 269 | } 270 | CFRelease(store); 271 | } 272 | if (primaryInterface == NULL) { 273 | primaryInterface = "lo0"; 274 | } 275 | #endif 276 | struct ifaddrs* list; 277 | if (getifaddrs(&list) >= 0) { 278 | for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) { 279 | #if TARGET_IPHONE_SIMULATOR || TARGET_OS_TV 280 | // Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator 281 | // Assumption holds for Apple TV running tvOS 282 | if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1")) 283 | #else 284 | if (strcmp(ifap->ifa_name, primaryInterface)) 285 | #endif 286 | { 287 | continue; 288 | } 289 | if ((ifap->ifa_flags & IFF_UP) && ((!useIPv6 && (ifap->ifa_addr->sa_family == AF_INET)) || (useIPv6 && (ifap->ifa_addr->sa_family == AF_INET6)))) { 290 | address = GCDWebServerStringFromSockAddr(ifap->ifa_addr, NO); 291 | break; 292 | } 293 | } 294 | freeifaddrs(list); 295 | } 296 | return address; 297 | } 298 | 299 | NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) { 300 | va_list arguments; 301 | va_start(arguments, format); 302 | const char* string = [[[NSString alloc] initWithFormat:format arguments:arguments] UTF8String]; 303 | va_end(arguments); 304 | unsigned char md5[CC_MD5_DIGEST_LENGTH]; 305 | #pragma clang diagnostic push 306 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 307 | CC_MD5(string, (CC_LONG)strlen(string), md5); 308 | #pragma clang diagnostic pop 309 | char buffer[2 * CC_MD5_DIGEST_LENGTH + 1]; 310 | for (int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) { 311 | unsigned char byte = md5[i]; 312 | unsigned char byteHi = (byte & 0xF0) >> 4; 313 | buffer[2 * i + 0] = byteHi >= 10 ? 'a' + byteHi - 10 : '0' + byteHi; 314 | unsigned char byteLo = byte & 0x0F; 315 | buffer[2 * i + 1] = byteLo >= 10 ? 'a' + byteLo - 10 : '0' + byteLo; 316 | } 317 | buffer[2 * CC_MD5_DIGEST_LENGTH] = 0; 318 | return (NSString*)[NSString stringWithUTF8String:buffer]; 319 | } 320 | 321 | NSString* GCDWebServerNormalizePath(NSString* path) { 322 | NSMutableArray* components = [[NSMutableArray alloc] init]; 323 | for (NSString* component in [path componentsSeparatedByString:@"/"]) { 324 | if ([component isEqualToString:@".."]) { 325 | [components removeLastObject]; 326 | } else if (component.length && ![component isEqualToString:@"."]) { 327 | [components addObject:component]; 328 | } 329 | } 330 | if (path.length && ([path characterAtIndex:0] == '/')) { 331 | return [@"/" stringByAppendingString:[components componentsJoinedByString:@"/"]]; // Preserve initial slash 332 | } 333 | return [components componentsJoinedByString:@"/"]; 334 | } 335 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerHTTPStatusCodes.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 29 | // http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml 30 | 31 | #import 32 | 33 | /** 34 | * Convenience constants for "informational" HTTP status codes. 35 | */ 36 | typedef NS_ENUM(NSInteger, GCDWebServerInformationalHTTPStatusCode) { 37 | kGCDWebServerHTTPStatusCode_Continue = 100, 38 | kGCDWebServerHTTPStatusCode_SwitchingProtocols = 101, 39 | kGCDWebServerHTTPStatusCode_Processing = 102 40 | }; 41 | 42 | /** 43 | * Convenience constants for "successful" HTTP status codes. 44 | */ 45 | typedef NS_ENUM(NSInteger, GCDWebServerSuccessfulHTTPStatusCode) { 46 | kGCDWebServerHTTPStatusCode_OK = 200, 47 | kGCDWebServerHTTPStatusCode_Created = 201, 48 | kGCDWebServerHTTPStatusCode_Accepted = 202, 49 | kGCDWebServerHTTPStatusCode_NonAuthoritativeInformation = 203, 50 | kGCDWebServerHTTPStatusCode_NoContent = 204, 51 | kGCDWebServerHTTPStatusCode_ResetContent = 205, 52 | kGCDWebServerHTTPStatusCode_PartialContent = 206, 53 | kGCDWebServerHTTPStatusCode_MultiStatus = 207, 54 | kGCDWebServerHTTPStatusCode_AlreadyReported = 208 55 | }; 56 | 57 | /** 58 | * Convenience constants for "redirection" HTTP status codes. 59 | */ 60 | typedef NS_ENUM(NSInteger, GCDWebServerRedirectionHTTPStatusCode) { 61 | kGCDWebServerHTTPStatusCode_MultipleChoices = 300, 62 | kGCDWebServerHTTPStatusCode_MovedPermanently = 301, 63 | kGCDWebServerHTTPStatusCode_Found = 302, 64 | kGCDWebServerHTTPStatusCode_SeeOther = 303, 65 | kGCDWebServerHTTPStatusCode_NotModified = 304, 66 | kGCDWebServerHTTPStatusCode_UseProxy = 305, 67 | kGCDWebServerHTTPStatusCode_TemporaryRedirect = 307, 68 | kGCDWebServerHTTPStatusCode_PermanentRedirect = 308 69 | }; 70 | 71 | /** 72 | * Convenience constants for "client error" HTTP status codes. 73 | */ 74 | typedef NS_ENUM(NSInteger, GCDWebServerClientErrorHTTPStatusCode) { 75 | kGCDWebServerHTTPStatusCode_BadRequest = 400, 76 | kGCDWebServerHTTPStatusCode_Unauthorized = 401, 77 | kGCDWebServerHTTPStatusCode_PaymentRequired = 402, 78 | kGCDWebServerHTTPStatusCode_Forbidden = 403, 79 | kGCDWebServerHTTPStatusCode_NotFound = 404, 80 | kGCDWebServerHTTPStatusCode_MethodNotAllowed = 405, 81 | kGCDWebServerHTTPStatusCode_NotAcceptable = 406, 82 | kGCDWebServerHTTPStatusCode_ProxyAuthenticationRequired = 407, 83 | kGCDWebServerHTTPStatusCode_RequestTimeout = 408, 84 | kGCDWebServerHTTPStatusCode_Conflict = 409, 85 | kGCDWebServerHTTPStatusCode_Gone = 410, 86 | kGCDWebServerHTTPStatusCode_LengthRequired = 411, 87 | kGCDWebServerHTTPStatusCode_PreconditionFailed = 412, 88 | kGCDWebServerHTTPStatusCode_RequestEntityTooLarge = 413, 89 | kGCDWebServerHTTPStatusCode_RequestURITooLong = 414, 90 | kGCDWebServerHTTPStatusCode_UnsupportedMediaType = 415, 91 | kGCDWebServerHTTPStatusCode_RequestedRangeNotSatisfiable = 416, 92 | kGCDWebServerHTTPStatusCode_ExpectationFailed = 417, 93 | kGCDWebServerHTTPStatusCode_UnprocessableEntity = 422, 94 | kGCDWebServerHTTPStatusCode_Locked = 423, 95 | kGCDWebServerHTTPStatusCode_FailedDependency = 424, 96 | kGCDWebServerHTTPStatusCode_UpgradeRequired = 426, 97 | kGCDWebServerHTTPStatusCode_PreconditionRequired = 428, 98 | kGCDWebServerHTTPStatusCode_TooManyRequests = 429, 99 | kGCDWebServerHTTPStatusCode_RequestHeaderFieldsTooLarge = 431 100 | }; 101 | 102 | /** 103 | * Convenience constants for "server error" HTTP status codes. 104 | */ 105 | typedef NS_ENUM(NSInteger, GCDWebServerServerErrorHTTPStatusCode) { 106 | kGCDWebServerHTTPStatusCode_InternalServerError = 500, 107 | kGCDWebServerHTTPStatusCode_NotImplemented = 501, 108 | kGCDWebServerHTTPStatusCode_BadGateway = 502, 109 | kGCDWebServerHTTPStatusCode_ServiceUnavailable = 503, 110 | kGCDWebServerHTTPStatusCode_GatewayTimeout = 504, 111 | kGCDWebServerHTTPStatusCode_HTTPVersionNotSupported = 505, 112 | kGCDWebServerHTTPStatusCode_InsufficientStorage = 507, 113 | kGCDWebServerHTTPStatusCode_LoopDetected = 508, 114 | kGCDWebServerHTTPStatusCode_NotExtended = 510, 115 | kGCDWebServerHTTPStatusCode_NetworkAuthenticationRequired = 511 116 | }; 117 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerPrivate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | #import 30 | 31 | /** 32 | * All GCDWebServer headers. 33 | */ 34 | 35 | #import "GCDWebServerHTTPStatusCodes.h" 36 | #import "GCDWebServerFunctions.h" 37 | 38 | #import "GCDWebServer.h" 39 | #import "GCDWebServerConnection.h" 40 | 41 | #import "GCDWebServerDataRequest.h" 42 | #import "GCDWebServerFileRequest.h" 43 | #import "GCDWebServerMultiPartFormRequest.h" 44 | #import "GCDWebServerURLEncodedFormRequest.h" 45 | 46 | #import "GCDWebServerDataResponse.h" 47 | #import "GCDWebServerErrorResponse.h" 48 | #import "GCDWebServerFileResponse.h" 49 | #import "GCDWebServerStreamedResponse.h" 50 | 51 | /** 52 | * Check if a custom logging facility should be used instead. 53 | */ 54 | 55 | #if defined(__GCDWEBSERVER_LOGGING_HEADER__) 56 | 57 | #define __GCDWEBSERVER_LOGGING_FACILITY_CUSTOM__ 58 | 59 | #import __GCDWEBSERVER_LOGGING_HEADER__ 60 | 61 | /** 62 | * Automatically detect if XLFacility is available and if so use it as a 63 | * logging facility. 64 | */ 65 | 66 | #elif defined(__has_include) && __has_include("XLFacilityMacros.h") 67 | 68 | #define __GCDWEBSERVER_LOGGING_FACILITY_XLFACILITY__ 69 | 70 | #undef XLOG_TAG 71 | #define XLOG_TAG @"gcdwebserver.internal" 72 | 73 | #import "XLFacilityMacros.h" 74 | 75 | #define GWS_LOG_DEBUG(...) XLOG_DEBUG(__VA_ARGS__) 76 | #define GWS_LOG_VERBOSE(...) XLOG_VERBOSE(__VA_ARGS__) 77 | #define GWS_LOG_INFO(...) XLOG_INFO(__VA_ARGS__) 78 | #define GWS_LOG_WARNING(...) XLOG_WARNING(__VA_ARGS__) 79 | #define GWS_LOG_ERROR(...) XLOG_ERROR(__VA_ARGS__) 80 | 81 | #define GWS_DCHECK(__CONDITION__) XLOG_DEBUG_CHECK(__CONDITION__) 82 | #define GWS_DNOT_REACHED() XLOG_DEBUG_UNREACHABLE() 83 | 84 | /** 85 | * If all of the above fail, then use GCDWebServer built-in 86 | * logging facility. 87 | */ 88 | 89 | #else 90 | 91 | #define __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__ 92 | 93 | typedef NS_ENUM(int, GCDWebServerLoggingLevel) { 94 | kGCDWebServerLoggingLevel_Debug = 0, 95 | kGCDWebServerLoggingLevel_Verbose, 96 | kGCDWebServerLoggingLevel_Info, 97 | kGCDWebServerLoggingLevel_Warning, 98 | kGCDWebServerLoggingLevel_Error 99 | }; 100 | 101 | extern GCDWebServerLoggingLevel GCDWebServerLogLevel; 102 | extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* _Nonnull format, ...) NS_FORMAT_FUNCTION(2, 3); 103 | 104 | #if DEBUG 105 | #define GWS_LOG_DEBUG(...) \ 106 | do { \ 107 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Debug) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Debug, __VA_ARGS__); \ 108 | } while (0) 109 | #else 110 | #define GWS_LOG_DEBUG(...) 111 | #endif 112 | #define GWS_LOG_VERBOSE(...) \ 113 | do { \ 114 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Verbose) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Verbose, __VA_ARGS__); \ 115 | } while (0) 116 | #define GWS_LOG_INFO(...) \ 117 | do { \ 118 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Info) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Info, __VA_ARGS__); \ 119 | } while (0) 120 | #define GWS_LOG_WARNING(...) \ 121 | do { \ 122 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Warning) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Warning, __VA_ARGS__); \ 123 | } while (0) 124 | #define GWS_LOG_ERROR(...) \ 125 | do { \ 126 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Error) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Error, __VA_ARGS__); \ 127 | } while (0) 128 | 129 | #endif 130 | 131 | /** 132 | * Consistency check macros used when building Debug only. 133 | */ 134 | 135 | #if !defined(GWS_DCHECK) || !defined(GWS_DNOT_REACHED) 136 | 137 | #if DEBUG 138 | 139 | #define GWS_DCHECK(__CONDITION__) \ 140 | do { \ 141 | if (!(__CONDITION__)) { \ 142 | abort(); \ 143 | } \ 144 | } while (0) 145 | #define GWS_DNOT_REACHED() abort() 146 | 147 | #else 148 | 149 | #define GWS_DCHECK(__CONDITION__) 150 | #define GWS_DNOT_REACHED() 151 | 152 | #endif 153 | 154 | #endif 155 | 156 | NS_ASSUME_NONNULL_BEGIN 157 | 158 | /** 159 | * GCDWebServer internal constants and APIs. 160 | */ 161 | 162 | #define kGCDWebServerDefaultMimeType @"application/octet-stream" 163 | #define kGCDWebServerErrorDomain @"GCDWebServerErrorDomain" 164 | 165 | static inline BOOL GCDWebServerIsValidByteRange(NSRange range) { 166 | return ((range.location != NSUIntegerMax) || (range.length > 0)); 167 | } 168 | 169 | static inline NSError* GCDWebServerMakePosixError(int code) { 170 | return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : (NSString*)[NSString stringWithUTF8String:strerror(code)]}]; 171 | } 172 | 173 | extern void GCDWebServerInitializeFunctions(void); 174 | extern NSString* _Nullable GCDWebServerNormalizeHeaderValue(NSString* _Nullable value); 175 | extern NSString* _Nullable GCDWebServerTruncateHeaderValue(NSString* _Nullable value); 176 | extern NSString* _Nullable GCDWebServerExtractHeaderValueParameter(NSString* _Nullable value, NSString* attribute); 177 | extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset); 178 | extern BOOL GCDWebServerIsTextContentType(NSString* type); 179 | extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType); 180 | extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2); 181 | extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService); 182 | 183 | @interface GCDWebServerConnection () 184 | - (instancetype)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket; 185 | @end 186 | 187 | @interface GCDWebServer () 188 | @property(nonatomic, readonly) NSMutableArray* handlers; 189 | @property(nonatomic, readonly, nullable) NSString* serverName; 190 | @property(nonatomic, readonly, nullable) NSString* authenticationRealm; 191 | @property(nonatomic, readonly, nullable) NSMutableDictionary* authenticationBasicAccounts; 192 | @property(nonatomic, readonly, nullable) NSMutableDictionary* authenticationDigestAccounts; 193 | @property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET; 194 | @property(nonatomic, readonly) dispatch_queue_priority_t dispatchQueuePriority; 195 | - (void)willStartConnection:(GCDWebServerConnection*)connection; 196 | - (void)didEndConnection:(GCDWebServerConnection*)connection; 197 | @end 198 | 199 | @interface GCDWebServerHandler : NSObject 200 | @property(nonatomic, readonly) GCDWebServerMatchBlock matchBlock; 201 | @property(nonatomic, readonly) GCDWebServerAsyncProcessBlock asyncProcessBlock; 202 | @end 203 | 204 | @interface GCDWebServerRequest () 205 | @property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; 206 | @property(nonatomic) NSData* localAddressData; 207 | @property(nonatomic) NSData* remoteAddressData; 208 | - (void)prepareForWriting; 209 | - (BOOL)performOpen:(NSError**)error; 210 | - (BOOL)performWriteData:(NSData*)data error:(NSError**)error; 211 | - (BOOL)performClose:(NSError**)error; 212 | - (void)setAttribute:(nullable id)attribute forKey:(NSString*)key; 213 | @end 214 | 215 | @interface GCDWebServerResponse () 216 | @property(nonatomic, readonly) NSDictionary* additionalHeaders; 217 | @property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; 218 | - (void)prepareForReading; 219 | - (BOOL)performOpen:(NSError**)error; 220 | - (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block; 221 | - (void)performClose; 222 | @end 223 | 224 | NS_ASSUME_NONNULL_END 225 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * Attribute key to retrieve an NSArray containing NSStrings from a GCDWebServerRequest 34 | * with the contents of any regular expression captures done on the request path. 35 | * 36 | * @warning This attribute will only be set on the request if adding a handler using 37 | * -addHandlerForMethod:pathRegex:requestClass:processBlock:. 38 | */ 39 | extern NSString* const GCDWebServerRequestAttribute_RegexCaptures; 40 | 41 | /** 42 | * This protocol is used by the GCDWebServerConnection to communicate with 43 | * the GCDWebServerRequest and write the received HTTP body data. 44 | * 45 | * Note that multiple GCDWebServerBodyWriter objects can be chained together 46 | * internally e.g. to automatically decode gzip encoded content before 47 | * passing it on to the GCDWebServerRequest. 48 | * 49 | * @warning These methods can be called on any GCD thread. 50 | */ 51 | @protocol GCDWebServerBodyWriter 52 | 53 | /** 54 | * This method is called before any body data is received. 55 | * 56 | * It should return YES on success or NO on failure and set the "error" argument 57 | * which is guaranteed to be non-NULL. 58 | */ 59 | - (BOOL)open:(NSError**)error; 60 | 61 | /** 62 | * This method is called whenever body data has been received. 63 | * 64 | * It should return YES on success or NO on failure and set the "error" argument 65 | * which is guaranteed to be non-NULL. 66 | */ 67 | - (BOOL)writeData:(NSData*)data error:(NSError**)error; 68 | 69 | /** 70 | * This method is called after all body data has been received. 71 | * 72 | * It should return YES on success or NO on failure and set the "error" argument 73 | * which is guaranteed to be non-NULL. 74 | */ 75 | - (BOOL)close:(NSError**)error; 76 | 77 | @end 78 | 79 | /** 80 | * The GCDWebServerRequest class is instantiated by the GCDWebServerConnection 81 | * after the HTTP headers have been received. Each instance wraps a single HTTP 82 | * request. If a body is present, the methods from the GCDWebServerBodyWriter 83 | * protocol will be called by the GCDWebServerConnection to receive it. 84 | * 85 | * The default implementation of the GCDWebServerBodyWriter protocol on the class 86 | * simply ignores the body data. 87 | * 88 | * @warning GCDWebServerRequest instances can be created and used on any GCD thread. 89 | */ 90 | @interface GCDWebServerRequest : NSObject 91 | 92 | /** 93 | * Returns the HTTP method for the request. 94 | */ 95 | @property(nonatomic, readonly) NSString* method; 96 | 97 | /** 98 | * Returns the URL for the request. 99 | */ 100 | @property(nonatomic, readonly) NSURL* URL; 101 | 102 | /** 103 | * Returns the HTTP headers for the request. 104 | */ 105 | @property(nonatomic, readonly) NSDictionary* headers; 106 | 107 | /** 108 | * Returns the path component of the URL for the request. 109 | */ 110 | @property(nonatomic, readonly) NSString* path; 111 | 112 | /** 113 | * Returns the parsed and unescaped query component of the URL for the request. 114 | * 115 | * @warning This property will be nil if there is no query in the URL. 116 | */ 117 | @property(nonatomic, readonly, nullable) NSDictionary* query; 118 | 119 | /** 120 | * Returns the content type for the body of the request parsed from the 121 | * "Content-Type" header. 122 | * 123 | * This property will be nil if the request has no body or set to 124 | * "application/octet-stream" if a body is present but there was no 125 | * "Content-Type" header. 126 | */ 127 | @property(nonatomic, readonly, nullable) NSString* contentType; 128 | 129 | /** 130 | * Returns the content length for the body of the request parsed from the 131 | * "Content-Length" header. 132 | * 133 | * This property will be set to "NSUIntegerMax" if the request has no body or 134 | * if there is a body but no "Content-Length" header, typically because 135 | * chunked transfer encoding is used. 136 | */ 137 | @property(nonatomic, readonly) NSUInteger contentLength; 138 | 139 | /** 140 | * Returns the parsed "If-Modified-Since" header or nil if absent or malformed. 141 | */ 142 | @property(nonatomic, readonly, nullable) NSDate* ifModifiedSince; 143 | 144 | /** 145 | * Returns the parsed "If-None-Match" header or nil if absent or malformed. 146 | */ 147 | @property(nonatomic, readonly, nullable) NSString* ifNoneMatch; 148 | 149 | /** 150 | * Returns the parsed "Range" header or (NSUIntegerMax, 0) if absent or malformed. 151 | * The range will be set to (offset, length) if expressed from the beginning 152 | * of the entity body, or (NSUIntegerMax, length) if expressed from its end. 153 | */ 154 | @property(nonatomic, readonly) NSRange byteRange; 155 | 156 | /** 157 | * Returns YES if the client supports gzip content encoding according to the 158 | * "Accept-Encoding" header. 159 | */ 160 | @property(nonatomic, readonly) BOOL acceptsGzipContentEncoding; 161 | 162 | /** 163 | * Returns the address of the local peer (i.e. server) for the request 164 | * as a raw "struct sockaddr". 165 | */ 166 | @property(nonatomic, readonly) NSData* localAddressData; 167 | 168 | /** 169 | * Returns the address of the local peer (i.e. server) for the request 170 | * as a string. 171 | */ 172 | @property(nonatomic, readonly) NSString* localAddressString; 173 | 174 | /** 175 | * Returns the address of the remote peer (i.e. client) for the request 176 | * as a raw "struct sockaddr". 177 | */ 178 | @property(nonatomic, readonly) NSData* remoteAddressData; 179 | 180 | /** 181 | * Returns the address of the remote peer (i.e. client) for the request 182 | * as a string. 183 | */ 184 | @property(nonatomic, readonly) NSString* remoteAddressString; 185 | 186 | /** 187 | * This method is the designated initializer for the class. 188 | */ 189 | - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(nullable NSDictionary*)query; 190 | 191 | /** 192 | * Convenience method that checks if the contentType property is defined. 193 | */ 194 | - (BOOL)hasBody; 195 | 196 | /** 197 | * Convenience method that checks if the byteRange property is defined. 198 | */ 199 | - (BOOL)hasByteRange; 200 | 201 | /** 202 | * Retrieves an attribute associated with this request using the given key. 203 | * 204 | * @return The attribute value for the key. 205 | */ 206 | - (nullable id)attributeForKey:(NSString*)key; 207 | 208 | @end 209 | 210 | NS_ASSUME_NONNULL_END 211 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import 33 | 34 | #import "GCDWebServerPrivate.h" 35 | 36 | NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerRequestAttribute_RegexCaptures"; 37 | 38 | #define kZlibErrorDomain @"ZlibErrorDomain" 39 | #define kGZipInitialBufferSize (256 * 1024) 40 | 41 | @interface GCDWebServerBodyDecoder : NSObject 42 | @end 43 | 44 | @interface GCDWebServerGZipDecoder : GCDWebServerBodyDecoder 45 | @end 46 | 47 | @implementation GCDWebServerBodyDecoder { 48 | GCDWebServerRequest* __unsafe_unretained _request; 49 | id __unsafe_unretained _writer; 50 | } 51 | 52 | - (instancetype)initWithRequest:(GCDWebServerRequest* _Nonnull)request writer:(id _Nonnull)writer { 53 | if ((self = [super init])) { 54 | _request = request; 55 | _writer = writer; 56 | } 57 | return self; 58 | } 59 | 60 | - (BOOL)open:(NSError**)error { 61 | return [_writer open:error]; 62 | } 63 | 64 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 65 | return [_writer writeData:data error:error]; 66 | } 67 | 68 | - (BOOL)close:(NSError**)error { 69 | return [_writer close:error]; 70 | } 71 | 72 | @end 73 | 74 | @implementation GCDWebServerGZipDecoder { 75 | z_stream _stream; 76 | BOOL _finished; 77 | } 78 | 79 | - (BOOL)open:(NSError**)error { 80 | int result = inflateInit2(&_stream, 15 + 16); 81 | if (result != Z_OK) { 82 | if (error) { 83 | *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; 84 | } 85 | return NO; 86 | } 87 | if (![super open:error]) { 88 | inflateEnd(&_stream); 89 | return NO; 90 | } 91 | return YES; 92 | } 93 | 94 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 95 | GWS_DCHECK(!_finished); 96 | _stream.next_in = (Bytef*)data.bytes; 97 | _stream.avail_in = (uInt)data.length; 98 | NSMutableData* decodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize]; 99 | if (decodedData == nil) { 100 | GWS_DNOT_REACHED(); 101 | return NO; 102 | } 103 | NSUInteger length = 0; 104 | while (1) { 105 | NSUInteger maxLength = decodedData.length - length; 106 | _stream.next_out = (Bytef*)((char*)decodedData.mutableBytes + length); 107 | _stream.avail_out = (uInt)maxLength; 108 | int result = inflate(&_stream, Z_NO_FLUSH); 109 | if ((result != Z_OK) && (result != Z_STREAM_END)) { 110 | if (error) { 111 | *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; 112 | } 113 | return NO; 114 | } 115 | length += maxLength - _stream.avail_out; 116 | if (_stream.avail_out > 0) { 117 | if (result == Z_STREAM_END) { 118 | _finished = YES; 119 | } 120 | break; 121 | } 122 | decodedData.length = 2 * decodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available 123 | } 124 | decodedData.length = length; 125 | BOOL success = length ? [super writeData:decodedData error:error] : YES; // No need to call writer if we have no data yet 126 | return success; 127 | } 128 | 129 | - (BOOL)close:(NSError**)error { 130 | GWS_DCHECK(_finished); 131 | inflateEnd(&_stream); 132 | return [super close:error]; 133 | } 134 | 135 | @end 136 | 137 | @implementation GCDWebServerRequest { 138 | BOOL _opened; 139 | NSMutableArray* _decoders; 140 | id __unsafe_unretained _writer; 141 | NSMutableDictionary* _attributes; 142 | } 143 | 144 | - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { 145 | if ((self = [super init])) { 146 | _method = [method copy]; 147 | _URL = url; 148 | _headers = headers; 149 | _path = [path copy]; 150 | _query = query; 151 | 152 | _contentType = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]); 153 | _usesChunkedTransferEncoding = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"]; 154 | NSString* lengthHeader = [_headers objectForKey:@"Content-Length"]; 155 | if (lengthHeader) { 156 | NSInteger length = [lengthHeader integerValue]; 157 | if (_usesChunkedTransferEncoding || (length < 0)) { 158 | GWS_LOG_WARNING(@"Invalid 'Content-Length' header '%@' for '%@' request on \"%@\"", lengthHeader, _method, _URL); 159 | GWS_DNOT_REACHED(); 160 | return nil; 161 | } 162 | _contentLength = length; 163 | if (_contentType == nil) { 164 | _contentType = kGCDWebServerDefaultMimeType; 165 | } 166 | } else if (_usesChunkedTransferEncoding) { 167 | if (_contentType == nil) { 168 | _contentType = kGCDWebServerDefaultMimeType; 169 | } 170 | _contentLength = NSUIntegerMax; 171 | } else { 172 | if (_contentType) { 173 | GWS_LOG_WARNING(@"Ignoring 'Content-Type' header for '%@' request on \"%@\"", _method, _URL); 174 | _contentType = nil; // Content-Type without Content-Length or chunked-encoding doesn't make sense 175 | } 176 | _contentLength = NSUIntegerMax; 177 | } 178 | 179 | NSString* modifiedHeader = [_headers objectForKey:@"If-Modified-Since"]; 180 | if (modifiedHeader) { 181 | _ifModifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy]; 182 | } 183 | _ifNoneMatch = [_headers objectForKey:@"If-None-Match"]; 184 | 185 | _byteRange = NSMakeRange(NSUIntegerMax, 0); 186 | NSString* rangeHeader = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Range"]); 187 | if (rangeHeader) { 188 | if ([rangeHeader hasPrefix:@"bytes="]) { 189 | NSArray* components = [[rangeHeader substringFromIndex:6] componentsSeparatedByString:@","]; 190 | if (components.count == 1) { 191 | components = [(NSString*)[components firstObject] componentsSeparatedByString:@"-"]; 192 | if (components.count == 2) { 193 | NSString* startString = [components objectAtIndex:0]; 194 | NSInteger startValue = [startString integerValue]; 195 | NSString* endString = [components objectAtIndex:1]; 196 | NSInteger endValue = [endString integerValue]; 197 | if (startString.length && (startValue >= 0) && endString.length && (endValue >= startValue)) { // The second 500 bytes: "500-999" 198 | _byteRange.location = startValue; 199 | _byteRange.length = endValue - startValue + 1; 200 | } else if (startString.length && (startValue >= 0)) { // The bytes after 9500 bytes: "9500-" 201 | _byteRange.location = startValue; 202 | _byteRange.length = NSUIntegerMax; 203 | } else if (endString.length && (endValue > 0)) { // The final 500 bytes: "-500" 204 | _byteRange.location = NSUIntegerMax; 205 | _byteRange.length = endValue; 206 | } 207 | } 208 | } 209 | } 210 | if ((_byteRange.location == NSUIntegerMax) && (_byteRange.length == 0)) { // Ignore "Range" header if syntactically invalid 211 | GWS_LOG_WARNING(@"Failed to parse 'Range' header \"%@\" for url: %@", rangeHeader, url); 212 | } 213 | } 214 | 215 | if ([[_headers objectForKey:@"Accept-Encoding"] rangeOfString:@"gzip"].location != NSNotFound) { 216 | _acceptsGzipContentEncoding = YES; 217 | } 218 | 219 | _decoders = [[NSMutableArray alloc] init]; 220 | _attributes = [[NSMutableDictionary alloc] init]; 221 | } 222 | return self; 223 | } 224 | 225 | - (BOOL)hasBody { 226 | return _contentType ? YES : NO; 227 | } 228 | 229 | - (BOOL)hasByteRange { 230 | return GCDWebServerIsValidByteRange(_byteRange); 231 | } 232 | 233 | - (id)attributeForKey:(NSString*)key { 234 | return [_attributes objectForKey:key]; 235 | } 236 | 237 | - (BOOL)open:(NSError**)error { 238 | return YES; 239 | } 240 | 241 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 242 | return YES; 243 | } 244 | 245 | - (BOOL)close:(NSError**)error { 246 | return YES; 247 | } 248 | 249 | - (void)prepareForWriting { 250 | _writer = self; 251 | if ([GCDWebServerNormalizeHeaderValue([self.headers objectForKey:@"Content-Encoding"]) isEqualToString:@"gzip"]) { 252 | GCDWebServerGZipDecoder* decoder = [[GCDWebServerGZipDecoder alloc] initWithRequest:self writer:_writer]; 253 | [_decoders addObject:decoder]; 254 | _writer = decoder; 255 | } 256 | } 257 | 258 | - (BOOL)performOpen:(NSError**)error { 259 | GWS_DCHECK(_contentType); 260 | GWS_DCHECK(_writer); 261 | if (_opened) { 262 | GWS_DNOT_REACHED(); 263 | return NO; 264 | } 265 | _opened = YES; 266 | return [_writer open:error]; 267 | } 268 | 269 | - (BOOL)performWriteData:(NSData*)data error:(NSError**)error { 270 | GWS_DCHECK(_opened); 271 | return [_writer writeData:data error:error]; 272 | } 273 | 274 | - (BOOL)performClose:(NSError**)error { 275 | GWS_DCHECK(_opened); 276 | return [_writer close:error]; 277 | } 278 | 279 | - (void)setAttribute:(id)attribute forKey:(NSString*)key { 280 | [_attributes setValue:attribute forKey:key]; 281 | } 282 | 283 | - (NSString*)localAddressString { 284 | return GCDWebServerStringFromSockAddr(_localAddressData.bytes, YES); 285 | } 286 | 287 | - (NSString*)remoteAddressString { 288 | return GCDWebServerStringFromSockAddr(_remoteAddressData.bytes, YES); 289 | } 290 | 291 | - (NSString*)description { 292 | NSMutableString* description = [NSMutableString stringWithFormat:@"%@ %@", _method, _path]; 293 | for (NSString* argument in [[_query allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 294 | [description appendFormat:@"\n %@ = %@", argument, [_query objectForKey:argument]]; 295 | } 296 | [description appendString:@"\n"]; 297 | for (NSString* header in [[_headers allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 298 | [description appendFormat:@"\n%@: %@", header, [_headers objectForKey:header]]; 299 | } 300 | return description; 301 | } 302 | 303 | @end 304 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerBodyReaderCompletionBlock is passed by GCDWebServer to the 34 | * GCDWebServerBodyReader object when reading data from it asynchronously. 35 | */ 36 | typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* _Nullable data, NSError* _Nullable error); 37 | 38 | /** 39 | * This protocol is used by the GCDWebServerConnection to communicate with 40 | * the GCDWebServerResponse and read the HTTP body data to send. 41 | * 42 | * Note that multiple GCDWebServerBodyReader objects can be chained together 43 | * internally e.g. to automatically apply gzip encoding to the content before 44 | * passing it on to the GCDWebServerResponse. 45 | * 46 | * @warning These methods can be called on any GCD thread. 47 | */ 48 | @protocol GCDWebServerBodyReader 49 | 50 | @required 51 | 52 | /** 53 | * This method is called before any body data is sent. 54 | * 55 | * It should return YES on success or NO on failure and set the "error" argument 56 | * which is guaranteed to be non-NULL. 57 | */ 58 | - (BOOL)open:(NSError**)error; 59 | 60 | /** 61 | * This method is called whenever body data is sent. 62 | * 63 | * It should return a non-empty NSData if there is body data available, 64 | * or an empty NSData there is no more body data, or nil on error and set 65 | * the "error" argument which is guaranteed to be non-NULL. 66 | */ 67 | - (nullable NSData*)readData:(NSError**)error; 68 | 69 | /** 70 | * This method is called after all body data has been sent. 71 | */ 72 | - (void)close; 73 | 74 | @optional 75 | 76 | /** 77 | * If this method is implemented, it will be preferred over -readData:. 78 | * 79 | * It must call the passed block when data is available, passing a non-empty 80 | * NSData if there is body data available, or an empty NSData there is no more 81 | * body data, or nil on error and pass an NSError along. 82 | */ 83 | - (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block; 84 | 85 | @end 86 | 87 | /** 88 | * The GCDWebServerResponse class is used to wrap a single HTTP response. 89 | * It is instantiated by the handler of the GCDWebServer that handled the request. 90 | * If a body is present, the methods from the GCDWebServerBodyReader protocol 91 | * will be called by the GCDWebServerConnection to send it. 92 | * 93 | * The default implementation of the GCDWebServerBodyReader protocol 94 | * on the class simply returns an empty body. 95 | * 96 | * @warning GCDWebServerResponse instances can be created and used on any GCD thread. 97 | */ 98 | @interface GCDWebServerResponse : NSObject 99 | 100 | /** 101 | * Sets the content type for the body of the response. 102 | * 103 | * The default value is nil i.e. the response has no body. 104 | * 105 | * @warning This property must be set if a body is present. 106 | */ 107 | @property(nonatomic, copy, nullable) NSString* contentType; 108 | 109 | /** 110 | * Sets the content length for the body of the response. If a body is present 111 | * but this property is set to "NSUIntegerMax", this means the length of the body 112 | * cannot be known ahead of time. Chunked transfer encoding will be 113 | * automatically enabled by the GCDWebServerConnection to comply with HTTP/1.1 114 | * specifications. 115 | * 116 | * The default value is "NSUIntegerMax" i.e. the response has no body or its length 117 | * is undefined. 118 | */ 119 | @property(nonatomic) NSUInteger contentLength; 120 | 121 | /** 122 | * Sets the HTTP status code for the response. 123 | * 124 | * The default value is 200 i.e. "OK". 125 | */ 126 | @property(nonatomic) NSInteger statusCode; 127 | 128 | /** 129 | * Sets the caching hint for the response using the "Cache-Control" header. 130 | * This value is expressed in seconds. 131 | * 132 | * The default value is 0 i.e. "no-cache". 133 | */ 134 | @property(nonatomic) NSUInteger cacheControlMaxAge; 135 | 136 | /** 137 | * Sets the last modified date for the response using the "Last-Modified" header. 138 | * 139 | * The default value is nil. 140 | */ 141 | @property(nonatomic, nullable) NSDate* lastModifiedDate; 142 | 143 | /** 144 | * Sets the ETag for the response using the "ETag" header. 145 | * 146 | * The default value is nil. 147 | */ 148 | @property(nonatomic, copy, nullable) NSString* eTag; 149 | 150 | /** 151 | * Enables gzip encoding for the response body. 152 | * 153 | * The default value is NO. 154 | * 155 | * @warning Enabling gzip encoding will remove any "Content-Length" header 156 | * since the length of the body is not known anymore. The client will still 157 | * be able to determine the body length when connection is closed per 158 | * HTTP/1.1 specifications. 159 | */ 160 | @property(nonatomic, getter=isGZipContentEncodingEnabled) BOOL gzipContentEncodingEnabled; 161 | 162 | /** 163 | * Creates an empty response. 164 | */ 165 | + (instancetype)response; 166 | 167 | /** 168 | * This method is the designated initializer for the class. 169 | */ 170 | - (instancetype)init; 171 | 172 | /** 173 | * Sets an additional HTTP header on the response. 174 | * Pass a nil value to remove an additional header. 175 | * 176 | * @warning Do not attempt to override the primary headers used 177 | * by GCDWebServerResponse like "Content-Type", "ETag", etc... 178 | */ 179 | - (void)setValue:(nullable NSString*)value forAdditionalHeader:(NSString*)header; 180 | 181 | /** 182 | * Convenience method that checks if the contentType property is defined. 183 | */ 184 | - (BOOL)hasBody; 185 | 186 | @end 187 | 188 | @interface GCDWebServerResponse (Extensions) 189 | 190 | /** 191 | * Creates a empty response with a specific HTTP status code. 192 | */ 193 | + (instancetype)responseWithStatusCode:(NSInteger)statusCode; 194 | 195 | /** 196 | * Creates an HTTP redirect response to a new URL. 197 | */ 198 | + (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent; 199 | 200 | /** 201 | * Initializes an empty response with a specific HTTP status code. 202 | */ 203 | - (instancetype)initWithStatusCode:(NSInteger)statusCode; 204 | 205 | /** 206 | * Initializes an HTTP redirect response to a new URL. 207 | */ 208 | - (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent; 209 | 210 | @end 211 | 212 | NS_ASSUME_NONNULL_END 213 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import 33 | 34 | #import "GCDWebServerPrivate.h" 35 | 36 | #define kZlibErrorDomain @"ZlibErrorDomain" 37 | #define kGZipInitialBufferSize (256 * 1024) 38 | 39 | @interface GCDWebServerBodyEncoder : NSObject 40 | @end 41 | 42 | @interface GCDWebServerGZipEncoder : GCDWebServerBodyEncoder 43 | @end 44 | 45 | @implementation GCDWebServerBodyEncoder { 46 | GCDWebServerResponse* __unsafe_unretained _response; 47 | id __unsafe_unretained _reader; 48 | } 49 | 50 | - (instancetype)initWithResponse:(GCDWebServerResponse* _Nonnull)response reader:(id _Nonnull)reader { 51 | if ((self = [super init])) { 52 | _response = response; 53 | _reader = reader; 54 | } 55 | return self; 56 | } 57 | 58 | - (BOOL)open:(NSError**)error { 59 | return [_reader open:error]; 60 | } 61 | 62 | - (NSData*)readData:(NSError**)error { 63 | return [_reader readData:error]; 64 | } 65 | 66 | - (void)close { 67 | [_reader close]; 68 | } 69 | 70 | @end 71 | 72 | @implementation GCDWebServerGZipEncoder { 73 | z_stream _stream; 74 | BOOL _finished; 75 | } 76 | 77 | - (instancetype)initWithResponse:(GCDWebServerResponse* _Nonnull)response reader:(id _Nonnull)reader { 78 | if ((self = [super initWithResponse:response reader:reader])) { 79 | response.contentLength = NSUIntegerMax; // Make sure "Content-Length" header is not set since we don't know it 80 | [response setValue:@"gzip" forAdditionalHeader:@"Content-Encoding"]; 81 | } 82 | return self; 83 | } 84 | 85 | - (BOOL)open:(NSError**)error { 86 | int result = deflateInit2(&_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); 87 | if (result != Z_OK) { 88 | if (error) { 89 | *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; 90 | } 91 | return NO; 92 | } 93 | if (![super open:error]) { 94 | deflateEnd(&_stream); 95 | return NO; 96 | } 97 | return YES; 98 | } 99 | 100 | - (NSData*)readData:(NSError**)error { 101 | NSMutableData* encodedData; 102 | if (_finished) { 103 | encodedData = [[NSMutableData alloc] init]; 104 | } else { 105 | encodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize]; 106 | if (encodedData == nil) { 107 | GWS_DNOT_REACHED(); 108 | return nil; 109 | } 110 | NSUInteger length = 0; 111 | do { 112 | NSData* data = [super readData:error]; 113 | if (data == nil) { 114 | return nil; 115 | } 116 | _stream.next_in = (Bytef*)data.bytes; 117 | _stream.avail_in = (uInt)data.length; 118 | while (1) { 119 | NSUInteger maxLength = encodedData.length - length; 120 | _stream.next_out = (Bytef*)((char*)encodedData.mutableBytes + length); 121 | _stream.avail_out = (uInt)maxLength; 122 | int result = deflate(&_stream, data.length ? Z_NO_FLUSH : Z_FINISH); 123 | if (result == Z_STREAM_END) { 124 | _finished = YES; 125 | } else if (result != Z_OK) { 126 | if (error) { 127 | *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; 128 | } 129 | return nil; 130 | } 131 | length += maxLength - _stream.avail_out; 132 | if (_stream.avail_out > 0) { 133 | break; 134 | } 135 | encodedData.length = 2 * encodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available 136 | } 137 | GWS_DCHECK(_stream.avail_in == 0); 138 | } while (length == 0); // Make sure we don't return an empty NSData if not in finished state 139 | encodedData.length = length; 140 | } 141 | return encodedData; 142 | } 143 | 144 | - (void)close { 145 | deflateEnd(&_stream); 146 | [super close]; 147 | } 148 | 149 | @end 150 | 151 | @implementation GCDWebServerResponse { 152 | BOOL _opened; 153 | NSMutableArray* _encoders; 154 | id __unsafe_unretained _reader; 155 | } 156 | 157 | + (instancetype)response { 158 | return [(GCDWebServerResponse*)[[self class] alloc] init]; 159 | } 160 | 161 | - (instancetype)init { 162 | if ((self = [super init])) { 163 | _contentType = nil; 164 | _contentLength = NSUIntegerMax; 165 | _statusCode = kGCDWebServerHTTPStatusCode_OK; 166 | _cacheControlMaxAge = 0; 167 | _additionalHeaders = [[NSMutableDictionary alloc] init]; 168 | _encoders = [[NSMutableArray alloc] init]; 169 | } 170 | return self; 171 | } 172 | 173 | - (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header { 174 | [_additionalHeaders setValue:value forKey:header]; 175 | } 176 | 177 | - (BOOL)hasBody { 178 | return _contentType ? YES : NO; 179 | } 180 | 181 | - (BOOL)usesChunkedTransferEncoding { 182 | return (_contentType != nil) && (_contentLength == NSUIntegerMax); 183 | } 184 | 185 | - (BOOL)open:(NSError**)error { 186 | return YES; 187 | } 188 | 189 | - (NSData*)readData:(NSError**)error { 190 | return [NSData data]; 191 | } 192 | 193 | - (void)close { 194 | ; 195 | } 196 | 197 | - (void)prepareForReading { 198 | _reader = self; 199 | if (_gzipContentEncodingEnabled) { 200 | GCDWebServerGZipEncoder* encoder = [[GCDWebServerGZipEncoder alloc] initWithResponse:self reader:_reader]; 201 | [_encoders addObject:encoder]; 202 | _reader = encoder; 203 | } 204 | } 205 | 206 | - (BOOL)performOpen:(NSError**)error { 207 | GWS_DCHECK(_contentType); 208 | GWS_DCHECK(_reader); 209 | if (_opened) { 210 | GWS_DNOT_REACHED(); 211 | return NO; 212 | } 213 | _opened = YES; 214 | return [_reader open:error]; 215 | } 216 | 217 | - (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { 218 | GWS_DCHECK(_opened); 219 | if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) { 220 | [_reader asyncReadDataWithCompletion:[block copy]]; 221 | } else { 222 | NSError* error = nil; 223 | NSData* data = [_reader readData:&error]; 224 | block(data, error); 225 | } 226 | } 227 | 228 | - (void)performClose { 229 | GWS_DCHECK(_opened); 230 | [_reader close]; 231 | } 232 | 233 | - (NSString*)description { 234 | NSMutableString* description = [NSMutableString stringWithFormat:@"Status Code = %i", (int)_statusCode]; 235 | if (_contentType) { 236 | [description appendFormat:@"\nContent Type = %@", _contentType]; 237 | } 238 | if (_contentLength != NSUIntegerMax) { 239 | [description appendFormat:@"\nContent Length = %lu", (unsigned long)_contentLength]; 240 | } 241 | [description appendFormat:@"\nCache Control Max Age = %lu", (unsigned long)_cacheControlMaxAge]; 242 | if (_lastModifiedDate) { 243 | [description appendFormat:@"\nLast Modified Date = %@", _lastModifiedDate]; 244 | } 245 | if (_eTag) { 246 | [description appendFormat:@"\nETag = %@", _eTag]; 247 | } 248 | if (_additionalHeaders.count) { 249 | [description appendString:@"\n"]; 250 | for (NSString* header in [[_additionalHeaders allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 251 | [description appendFormat:@"\n%@: %@", header, [_additionalHeaders objectForKey:header]]; 252 | } 253 | } 254 | return description; 255 | } 256 | 257 | @end 258 | 259 | @implementation GCDWebServerResponse (Extensions) 260 | 261 | + (instancetype)responseWithStatusCode:(NSInteger)statusCode { 262 | return [(GCDWebServerResponse*)[self alloc] initWithStatusCode:statusCode]; 263 | } 264 | 265 | + (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent { 266 | return [(GCDWebServerResponse*)[self alloc] initWithRedirect:location permanent:permanent]; 267 | } 268 | 269 | - (instancetype)initWithStatusCode:(NSInteger)statusCode { 270 | if ((self = [self init])) { 271 | self.statusCode = statusCode; 272 | } 273 | return self; 274 | } 275 | 276 | - (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent { 277 | if ((self = [self init])) { 278 | self.statusCode = permanent ? kGCDWebServerHTTPStatusCode_MovedPermanently : kGCDWebServerHTTPStatusCode_TemporaryRedirect; 279 | [self setValue:[location absoluteString] forAdditionalHeader:@"Location"]; 280 | } 281 | return self; 282 | } 283 | 284 | @end 285 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerDataRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerRequest.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerDataRequest subclass of GCDWebServerRequest stores the body 34 | * of the HTTP request in memory. 35 | */ 36 | @interface GCDWebServerDataRequest : GCDWebServerRequest 37 | 38 | /** 39 | * Returns the data for the request body. 40 | */ 41 | @property(nonatomic, readonly) NSData* data; 42 | 43 | @end 44 | 45 | @interface GCDWebServerDataRequest (Extensions) 46 | 47 | /** 48 | * Returns the data for the request body interpreted as text. If the content 49 | * type of the body is not a text one, or if an error occurs, nil is returned. 50 | * 51 | * The text encoding used to interpret the data is extracted from the 52 | * "Content-Type" header or defaults to UTF-8. 53 | */ 54 | @property(nonatomic, readonly, nullable) NSString* text; 55 | 56 | /** 57 | * Returns the data for the request body interpreted as a JSON object. If the 58 | * content type of the body is not JSON, or if an error occurs, nil is returned. 59 | */ 60 | @property(nonatomic, readonly, nullable) id jsonObject; 61 | 62 | @end 63 | 64 | NS_ASSUME_NONNULL_END 65 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerDataRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @interface GCDWebServerDataRequest () 35 | @property(nonatomic) NSMutableData* data; 36 | @end 37 | 38 | @implementation GCDWebServerDataRequest { 39 | NSString* _text; 40 | id _jsonObject; 41 | } 42 | 43 | - (BOOL)open:(NSError**)error { 44 | if (self.contentLength != NSUIntegerMax) { 45 | _data = [[NSMutableData alloc] initWithCapacity:self.contentLength]; 46 | } else { 47 | _data = [[NSMutableData alloc] init]; 48 | } 49 | if (_data == nil) { 50 | if (error) { 51 | *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Failed allocating memory"}]; 52 | } 53 | return NO; 54 | } 55 | return YES; 56 | } 57 | 58 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 59 | [_data appendData:data]; 60 | return YES; 61 | } 62 | 63 | - (BOOL)close:(NSError**)error { 64 | return YES; 65 | } 66 | 67 | - (NSString*)description { 68 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 69 | if (_data) { 70 | [description appendString:@"\n\n"]; 71 | [description appendString:GCDWebServerDescribeData(_data, (NSString*)self.contentType)]; 72 | } 73 | return description; 74 | } 75 | 76 | @end 77 | 78 | @implementation GCDWebServerDataRequest (Extensions) 79 | 80 | - (NSString*)text { 81 | if (_text == nil) { 82 | if ([self.contentType hasPrefix:@"text/"]) { 83 | NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); 84 | _text = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)]; 85 | } else { 86 | GWS_DNOT_REACHED(); 87 | } 88 | } 89 | return _text; 90 | } 91 | 92 | - (id)jsonObject { 93 | if (_jsonObject == nil) { 94 | NSString* mimeType = GCDWebServerTruncateHeaderValue(self.contentType); 95 | if ([mimeType isEqualToString:@"application/json"] || [mimeType isEqualToString:@"text/json"] || [mimeType isEqualToString:@"text/javascript"]) { 96 | _jsonObject = [NSJSONSerialization JSONObjectWithData:_data options:0 error:NULL]; 97 | } else { 98 | GWS_DNOT_REACHED(); 99 | } 100 | } 101 | return _jsonObject; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerFileRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerRequest.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerFileRequest subclass of GCDWebServerRequest stores the body 34 | * of the HTTP request to a file on disk. 35 | */ 36 | @interface GCDWebServerFileRequest : GCDWebServerRequest 37 | 38 | /** 39 | * Returns the path to the temporary file containing the request body. 40 | * 41 | * @warning This temporary file will be automatically deleted when the 42 | * GCDWebServerFileRequest is deallocated. If you want to preserve this file, 43 | * you must move it to a different location beforehand. 44 | */ 45 | @property(nonatomic, readonly) NSString* temporaryPath; 46 | 47 | @end 48 | 49 | NS_ASSUME_NONNULL_END 50 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerFileRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @implementation GCDWebServerFileRequest { 35 | int _file; 36 | } 37 | 38 | - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { 39 | if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) { 40 | _temporaryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; 41 | } 42 | return self; 43 | } 44 | 45 | - (void)dealloc { 46 | unlink([_temporaryPath fileSystemRepresentation]); 47 | } 48 | 49 | - (BOOL)open:(NSError**)error { 50 | _file = open([_temporaryPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 51 | if (_file <= 0) { 52 | if (error) { 53 | *error = GCDWebServerMakePosixError(errno); 54 | } 55 | return NO; 56 | } 57 | return YES; 58 | } 59 | 60 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 61 | if (write(_file, data.bytes, data.length) != (ssize_t)data.length) { 62 | if (error) { 63 | *error = GCDWebServerMakePosixError(errno); 64 | } 65 | return NO; 66 | } 67 | return YES; 68 | } 69 | 70 | - (BOOL)close:(NSError**)error { 71 | if (close(_file) < 0) { 72 | if (error) { 73 | *error = GCDWebServerMakePosixError(errno); 74 | } 75 | return NO; 76 | } 77 | #ifdef __GCDWEBSERVER_ENABLE_TESTING__ 78 | NSString* creationDateHeader = [self.headers objectForKey:@"X-GCDWebServer-CreationDate"]; 79 | if (creationDateHeader) { 80 | NSDate* date = GCDWebServerParseISO8601(creationDateHeader); 81 | if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileCreationDate : date} ofItemAtPath:_temporaryPath error:error]) { 82 | return NO; 83 | } 84 | } 85 | NSString* modifiedDateHeader = [self.headers objectForKey:@"X-GCDWebServer-ModifiedDate"]; 86 | if (modifiedDateHeader) { 87 | NSDate* date = GCDWebServerParseRFC822(modifiedDateHeader); 88 | if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate : date} ofItemAtPath:_temporaryPath error:error]) { 89 | return NO; 90 | } 91 | } 92 | #endif 93 | return YES; 94 | } 95 | 96 | - (NSString*)description { 97 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 98 | [description appendFormat:@"\n\n{%@}", _temporaryPath]; 99 | return description; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerRequest.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerMultiPart class is an abstract class that wraps the content 34 | * of a part. 35 | */ 36 | @interface GCDWebServerMultiPart : NSObject 37 | 38 | /** 39 | * Returns the control name retrieved from the part headers. 40 | */ 41 | @property(nonatomic, readonly) NSString* controlName; 42 | 43 | /** 44 | * Returns the content type retrieved from the part headers or "text/plain" 45 | * if not available (per HTTP specifications). 46 | */ 47 | @property(nonatomic, readonly) NSString* contentType; 48 | 49 | /** 50 | * Returns the MIME type component of the content type for the part. 51 | */ 52 | @property(nonatomic, readonly) NSString* mimeType; 53 | 54 | @end 55 | 56 | /** 57 | * The GCDWebServerMultiPartArgument subclass of GCDWebServerMultiPart wraps 58 | * the content of a part as data in memory. 59 | */ 60 | @interface GCDWebServerMultiPartArgument : GCDWebServerMultiPart 61 | 62 | /** 63 | * Returns the data for the part. 64 | */ 65 | @property(nonatomic, readonly) NSData* data; 66 | 67 | /** 68 | * Returns the data for the part interpreted as text. If the content 69 | * type of the part is not a text one, or if an error occurs, nil is returned. 70 | * 71 | * The text encoding used to interpret the data is extracted from the 72 | * "Content-Type" header or defaults to UTF-8. 73 | */ 74 | @property(nonatomic, readonly, nullable) NSString* string; 75 | 76 | @end 77 | 78 | /** 79 | * The GCDWebServerMultiPartFile subclass of GCDWebServerMultiPart wraps 80 | * the content of a part as a file on disk. 81 | */ 82 | @interface GCDWebServerMultiPartFile : GCDWebServerMultiPart 83 | 84 | /** 85 | * Returns the file name retrieved from the part headers. 86 | */ 87 | @property(nonatomic, readonly) NSString* fileName; 88 | 89 | /** 90 | * Returns the path to the temporary file containing the part data. 91 | * 92 | * @warning This temporary file will be automatically deleted when the 93 | * GCDWebServerMultiPartFile is deallocated. If you want to preserve this file, 94 | * you must move it to a different location beforehand. 95 | */ 96 | @property(nonatomic, readonly) NSString* temporaryPath; 97 | 98 | @end 99 | 100 | /** 101 | * The GCDWebServerMultiPartFormRequest subclass of GCDWebServerRequest 102 | * parses the body of the HTTP request as a multipart encoded form. 103 | */ 104 | @interface GCDWebServerMultiPartFormRequest : GCDWebServerRequest 105 | 106 | /** 107 | * Returns the argument parts from the multipart encoded form as 108 | * name / GCDWebServerMultiPartArgument pairs. 109 | */ 110 | @property(nonatomic, readonly) NSArray* arguments; 111 | 112 | /** 113 | * Returns the files parts from the multipart encoded form as 114 | * name / GCDWebServerMultiPartFile pairs. 115 | */ 116 | @property(nonatomic, readonly) NSArray* files; 117 | 118 | /** 119 | * Returns the MIME type for multipart encoded forms 120 | * i.e. "multipart/form-data". 121 | */ 122 | + (NSString*)mimeType; 123 | 124 | /** 125 | * Returns the first argument for a given control name or nil if not found. 126 | */ 127 | - (nullable GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name; 128 | 129 | /** 130 | * Returns the first file for a given control name or nil if not found. 131 | */ 132 | - (nullable GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name; 133 | 134 | @end 135 | 136 | NS_ASSUME_NONNULL_END 137 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | #define kMultiPartBufferSize (256 * 1024) 35 | 36 | typedef enum { 37 | kParserState_Undefined = 0, 38 | kParserState_Start, 39 | kParserState_Headers, 40 | kParserState_Content, 41 | kParserState_End 42 | } ParserState; 43 | 44 | @interface GCDWebServerMIMEStreamParser : NSObject 45 | @end 46 | 47 | static NSData* _newlineData = nil; 48 | static NSData* _newlinesData = nil; 49 | static NSData* _dashNewlineData = nil; 50 | 51 | @implementation GCDWebServerMultiPart 52 | 53 | - (instancetype)initWithControlName:(NSString* _Nonnull)name contentType:(NSString* _Nonnull)type { 54 | if ((self = [super init])) { 55 | _controlName = [name copy]; 56 | _contentType = [type copy]; 57 | _mimeType = (NSString*)GCDWebServerTruncateHeaderValue(_contentType); 58 | } 59 | return self; 60 | } 61 | 62 | @end 63 | 64 | @implementation GCDWebServerMultiPartArgument 65 | 66 | - (instancetype)initWithControlName:(NSString* _Nonnull)name contentType:(NSString* _Nonnull)type data:(NSData* _Nonnull)data { 67 | if ((self = [super initWithControlName:name contentType:type])) { 68 | _data = data; 69 | 70 | if ([self.contentType hasPrefix:@"text/"]) { 71 | NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); 72 | _string = [[NSString alloc] initWithData:_data encoding:GCDWebServerStringEncodingFromCharset(charset)]; 73 | } 74 | } 75 | return self; 76 | } 77 | 78 | - (NSString*)description { 79 | return [NSString stringWithFormat:@"<%@ | '%@' | %lu bytes>", [self class], self.mimeType, (unsigned long)_data.length]; 80 | } 81 | 82 | @end 83 | 84 | @implementation GCDWebServerMultiPartFile 85 | 86 | - (instancetype)initWithControlName:(NSString* _Nonnull)name contentType:(NSString* _Nonnull)type fileName:(NSString* _Nonnull)fileName temporaryPath:(NSString* _Nonnull)temporaryPath { 87 | if ((self = [super initWithControlName:name contentType:type])) { 88 | _fileName = [fileName copy]; 89 | _temporaryPath = [temporaryPath copy]; 90 | } 91 | return self; 92 | } 93 | 94 | - (void)dealloc { 95 | unlink([_temporaryPath fileSystemRepresentation]); 96 | } 97 | 98 | - (NSString*)description { 99 | return [NSString stringWithFormat:@"<%@ | '%@' | '%@>'", [self class], self.mimeType, _fileName]; 100 | } 101 | 102 | @end 103 | 104 | @implementation GCDWebServerMIMEStreamParser { 105 | NSData* _boundary; 106 | NSString* _defaultcontrolName; 107 | ParserState _state; 108 | NSMutableData* _data; 109 | NSMutableArray* _arguments; 110 | NSMutableArray* _files; 111 | 112 | NSString* _controlName; 113 | NSString* _fileName; 114 | NSString* _contentType; 115 | NSString* _tmpPath; 116 | int _tmpFile; 117 | GCDWebServerMIMEStreamParser* _subParser; 118 | } 119 | 120 | + (void)initialize { 121 | if (_newlineData == nil) { 122 | _newlineData = [[NSData alloc] initWithBytes:"\r\n" length:2]; 123 | GWS_DCHECK(_newlineData); 124 | } 125 | if (_newlinesData == nil) { 126 | _newlinesData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; 127 | GWS_DCHECK(_newlinesData); 128 | } 129 | if (_dashNewlineData == nil) { 130 | _dashNewlineData = [[NSData alloc] initWithBytes:"--\r\n" length:4]; 131 | GWS_DCHECK(_dashNewlineData); 132 | } 133 | } 134 | 135 | - (instancetype)initWithBoundary:(NSString* _Nonnull)boundary defaultControlName:(NSString* _Nullable)name arguments:(NSMutableArray* _Nonnull)arguments files:(NSMutableArray* _Nonnull)files { 136 | NSData* data = boundary.length ? [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding] : nil; 137 | if (data == nil) { 138 | GWS_DNOT_REACHED(); 139 | return nil; 140 | } 141 | if ((self = [super init])) { 142 | _boundary = data; 143 | _defaultcontrolName = name; 144 | _arguments = arguments; 145 | _files = files; 146 | _data = [[NSMutableData alloc] initWithCapacity:kMultiPartBufferSize]; 147 | _state = kParserState_Start; 148 | } 149 | return self; 150 | } 151 | 152 | - (void)dealloc { 153 | if (_tmpFile > 0) { 154 | close(_tmpFile); 155 | unlink([_tmpPath fileSystemRepresentation]); 156 | } 157 | } 158 | 159 | // http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 160 | - (BOOL)_parseData { 161 | BOOL success = YES; 162 | 163 | if (_state == kParserState_Headers) { 164 | NSRange range = [_data rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _data.length)]; 165 | if (range.location != NSNotFound) { 166 | _controlName = nil; 167 | _fileName = nil; 168 | _contentType = nil; 169 | _tmpPath = nil; 170 | _subParser = nil; 171 | NSString* headers = [[NSString alloc] initWithData:[_data subdataWithRange:NSMakeRange(0, range.location)] encoding:NSUTF8StringEncoding]; 172 | if (headers) { 173 | for (NSString* header in [headers componentsSeparatedByString:@"\r\n"]) { 174 | NSRange subRange = [header rangeOfString:@":"]; 175 | if (subRange.location != NSNotFound) { 176 | NSString* name = [header substringToIndex:subRange.location]; 177 | NSString* value = [[header substringFromIndex:(subRange.location + subRange.length)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 178 | if ([name caseInsensitiveCompare:@"Content-Type"] == NSOrderedSame) { 179 | _contentType = GCDWebServerNormalizeHeaderValue(value); 180 | } else if ([name caseInsensitiveCompare:@"Content-Disposition"] == NSOrderedSame) { 181 | NSString* contentDisposition = GCDWebServerNormalizeHeaderValue(value); 182 | if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"form-data"]) { 183 | _controlName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"name"); 184 | _fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename"); 185 | } else if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"file"]) { 186 | _controlName = _defaultcontrolName; 187 | _fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename"); 188 | } 189 | } 190 | } else { 191 | GWS_DNOT_REACHED(); 192 | } 193 | } 194 | if (_contentType == nil) { 195 | _contentType = @"text/plain"; 196 | } 197 | } else { 198 | GWS_LOG_ERROR(@"Failed decoding headers in part of 'multipart/form-data'"); 199 | GWS_DNOT_REACHED(); 200 | } 201 | if (_controlName) { 202 | if ([GCDWebServerTruncateHeaderValue(_contentType) isEqualToString:@"multipart/mixed"]) { 203 | NSString* boundary = GCDWebServerExtractHeaderValueParameter(_contentType, @"boundary"); 204 | _subParser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:_controlName arguments:_arguments files:_files]; 205 | if (_subParser == nil) { 206 | GWS_DNOT_REACHED(); 207 | success = NO; 208 | } 209 | } else if (_fileName) { 210 | NSString* path = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; 211 | _tmpFile = open([path fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 212 | if (_tmpFile > 0) { 213 | _tmpPath = [path copy]; 214 | } else { 215 | GWS_DNOT_REACHED(); 216 | success = NO; 217 | } 218 | } 219 | } else { 220 | GWS_DNOT_REACHED(); 221 | success = NO; 222 | } 223 | 224 | [_data replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0]; 225 | _state = kParserState_Content; 226 | } 227 | } 228 | 229 | if ((_state == kParserState_Start) || (_state == kParserState_Content)) { 230 | NSRange range = [_data rangeOfData:_boundary options:0 range:NSMakeRange(0, _data.length)]; 231 | if (range.location != NSNotFound) { 232 | NSRange subRange = NSMakeRange(range.location + range.length, _data.length - range.location - range.length); 233 | NSRange subRange1 = [_data rangeOfData:_newlineData options:NSDataSearchAnchored range:subRange]; 234 | NSRange subRange2 = [_data rangeOfData:_dashNewlineData options:NSDataSearchAnchored range:subRange]; 235 | if ((subRange1.location != NSNotFound) || (subRange2.location != NSNotFound)) { 236 | if (_state == kParserState_Content) { 237 | const void* dataBytes = _data.bytes; 238 | NSUInteger dataLength = range.location - 2; 239 | if (_subParser) { 240 | if (![_subParser appendBytes:dataBytes length:(dataLength + 2)] || ![_subParser isAtEnd]) { 241 | GWS_DNOT_REACHED(); 242 | success = NO; 243 | } 244 | _subParser = nil; 245 | } else if (_tmpPath) { 246 | ssize_t result = write(_tmpFile, dataBytes, dataLength); 247 | if (result == (ssize_t)dataLength) { 248 | if (close(_tmpFile) == 0) { 249 | _tmpFile = 0; 250 | GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithControlName:_controlName contentType:_contentType fileName:_fileName temporaryPath:_tmpPath]; 251 | [_files addObject:file]; 252 | } else { 253 | GWS_DNOT_REACHED(); 254 | success = NO; 255 | } 256 | } else { 257 | GWS_DNOT_REACHED(); 258 | success = NO; 259 | } 260 | _tmpPath = nil; 261 | } else { 262 | NSData* data = [[NSData alloc] initWithBytes:(void*)dataBytes length:dataLength]; 263 | GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithControlName:_controlName contentType:_contentType data:data]; 264 | [_arguments addObject:argument]; 265 | } 266 | } 267 | 268 | if (subRange1.location != NSNotFound) { 269 | [_data replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0]; 270 | _state = kParserState_Headers; 271 | success = [self _parseData]; 272 | } else { 273 | _state = kParserState_End; 274 | } 275 | } 276 | } else { 277 | NSUInteger margin = 2 * _boundary.length; 278 | if (_data.length > margin) { 279 | NSUInteger length = _data.length - margin; 280 | if (_subParser) { 281 | if ([_subParser appendBytes:_data.bytes length:length]) { 282 | [_data replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0]; 283 | } else { 284 | GWS_DNOT_REACHED(); 285 | success = NO; 286 | } 287 | } else if (_tmpPath) { 288 | ssize_t result = write(_tmpFile, _data.bytes, length); 289 | if (result == (ssize_t)length) { 290 | [_data replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0]; 291 | } else { 292 | GWS_DNOT_REACHED(); 293 | success = NO; 294 | } 295 | } 296 | } 297 | } 298 | } 299 | 300 | return success; 301 | } 302 | 303 | - (BOOL)appendBytes:(const void*)bytes length:(NSUInteger)length { 304 | [_data appendBytes:bytes length:length]; 305 | return [self _parseData]; 306 | } 307 | 308 | - (BOOL)isAtEnd { 309 | return (_state == kParserState_End); 310 | } 311 | 312 | @end 313 | 314 | @interface GCDWebServerMultiPartFormRequest () 315 | @property(nonatomic) NSMutableArray* arguments; 316 | @property(nonatomic) NSMutableArray* files; 317 | @end 318 | 319 | @implementation GCDWebServerMultiPartFormRequest { 320 | GCDWebServerMIMEStreamParser* _parser; 321 | } 322 | 323 | + (NSString*)mimeType { 324 | return @"multipart/form-data"; 325 | } 326 | 327 | - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { 328 | if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) { 329 | _arguments = [[NSMutableArray alloc] init]; 330 | _files = [[NSMutableArray alloc] init]; 331 | } 332 | return self; 333 | } 334 | 335 | - (BOOL)open:(NSError**)error { 336 | NSString* boundary = GCDWebServerExtractHeaderValueParameter(self.contentType, @"boundary"); 337 | _parser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:nil arguments:_arguments files:_files]; 338 | if (_parser == nil) { 339 | if (error) { 340 | *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Failed starting to parse multipart form data"}]; 341 | } 342 | return NO; 343 | } 344 | return YES; 345 | } 346 | 347 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 348 | if (![_parser appendBytes:data.bytes length:data.length]) { 349 | if (error) { 350 | *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Failed continuing to parse multipart form data"}]; 351 | } 352 | return NO; 353 | } 354 | return YES; 355 | } 356 | 357 | - (BOOL)close:(NSError**)error { 358 | BOOL atEnd = [_parser isAtEnd]; 359 | _parser = nil; 360 | if (!atEnd) { 361 | if (error) { 362 | *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Failed finishing to parse multipart form data"}]; 363 | } 364 | return NO; 365 | } 366 | return YES; 367 | } 368 | 369 | - (GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name { 370 | for (GCDWebServerMultiPartArgument* argument in _arguments) { 371 | if ([argument.controlName isEqualToString:name]) { 372 | return argument; 373 | } 374 | } 375 | return nil; 376 | } 377 | 378 | - (GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name { 379 | for (GCDWebServerMultiPartFile* file in _files) { 380 | if ([file.controlName isEqualToString:name]) { 381 | return file; 382 | } 383 | } 384 | return nil; 385 | } 386 | 387 | - (NSString*)description { 388 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 389 | if (_arguments.count) { 390 | [description appendString:@"\n"]; 391 | for (GCDWebServerMultiPartArgument* argument in _arguments) { 392 | [description appendFormat:@"\n%@ (%@)\n", argument.controlName, argument.contentType]; 393 | [description appendString:GCDWebServerDescribeData(argument.data, argument.contentType)]; 394 | } 395 | } 396 | if (_files.count) { 397 | [description appendString:@"\n"]; 398 | for (GCDWebServerMultiPartFile* file in _files) { 399 | [description appendFormat:@"\n%@ (%@): %@\n{%@}", file.controlName, file.contentType, file.fileName, file.temporaryPath]; 400 | } 401 | } 402 | return description; 403 | } 404 | 405 | @end 406 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerDataRequest.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerURLEncodedFormRequest subclass of GCDWebServerRequest 34 | * parses the body of the HTTP request as a URL encoded form using 35 | * GCDWebServerParseURLEncodedForm(). 36 | */ 37 | @interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest 38 | 39 | /** 40 | * Returns the unescaped control names and values for the URL encoded form. 41 | * 42 | * The text encoding used to interpret the data is extracted from the 43 | * "Content-Type" header or defaults to UTF-8. 44 | */ 45 | @property(nonatomic, readonly) NSDictionary* arguments; 46 | 47 | /** 48 | * Returns the MIME type for URL encoded forms 49 | * i.e. "application/x-www-form-urlencoded". 50 | */ 51 | + (NSString*)mimeType; 52 | 53 | @end 54 | 55 | NS_ASSUME_NONNULL_END 56 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @implementation GCDWebServerURLEncodedFormRequest 35 | 36 | + (NSString*)mimeType { 37 | return @"application/x-www-form-urlencoded"; 38 | } 39 | 40 | - (BOOL)close:(NSError**)error { 41 | if (![super close:error]) { 42 | return NO; 43 | } 44 | 45 | NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); 46 | NSString* string = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)]; 47 | _arguments = GCDWebServerParseURLEncodedForm(string); 48 | return YES; 49 | } 50 | 51 | - (NSString*)description { 52 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 53 | [description appendString:@"\n"]; 54 | for (NSString* argument in [[_arguments allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 55 | [description appendFormat:@"\n%@ = %@", argument, [_arguments objectForKey:argument]]; 56 | } 57 | return description; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerDataResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerResponse.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerDataResponse subclass of GCDWebServerResponse reads the body 34 | * of the HTTP response from memory. 35 | */ 36 | @interface GCDWebServerDataResponse : GCDWebServerResponse 37 | @property(nonatomic, copy) NSString* contentType; // Redeclare as non-null 38 | 39 | /** 40 | * Creates a response with data in memory and a given content type. 41 | */ 42 | + (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type; 43 | 44 | /** 45 | * This method is the designated initializer for the class. 46 | */ 47 | - (instancetype)initWithData:(NSData*)data contentType:(NSString*)type; 48 | 49 | @end 50 | 51 | @interface GCDWebServerDataResponse (Extensions) 52 | 53 | /** 54 | * Creates a data response from text encoded using UTF-8. 55 | */ 56 | + (nullable instancetype)responseWithText:(NSString*)text; 57 | 58 | /** 59 | * Creates a data response from HTML encoded using UTF-8. 60 | */ 61 | + (nullable instancetype)responseWithHTML:(NSString*)html; 62 | 63 | /** 64 | * Creates a data response from an HTML template encoded using UTF-8. 65 | * See -initWithHTMLTemplate:variables: for details. 66 | */ 67 | + (nullable instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; 68 | 69 | /** 70 | * Creates a data response from a serialized JSON object and the default 71 | * "application/json" content type. 72 | */ 73 | + (nullable instancetype)responseWithJSONObject:(id)object; 74 | 75 | /** 76 | * Creates a data response from a serialized JSON object and a custom 77 | * content type. 78 | */ 79 | + (nullable instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type; 80 | 81 | /** 82 | * Initializes a data response from text encoded using UTF-8. 83 | */ 84 | - (nullable instancetype)initWithText:(NSString*)text; 85 | 86 | /** 87 | * Initializes a data response from HTML encoded using UTF-8. 88 | */ 89 | - (nullable instancetype)initWithHTML:(NSString*)html; 90 | 91 | /** 92 | * Initializes a data response from an HTML template encoded using UTF-8. 93 | * 94 | * All occurences of "%variable%" within the HTML template are replaced with 95 | * their corresponding values. 96 | */ 97 | - (nullable instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; 98 | 99 | /** 100 | * Initializes a data response from a serialized JSON object and the default 101 | * "application/json" content type. 102 | */ 103 | - (nullable instancetype)initWithJSONObject:(id)object; 104 | 105 | /** 106 | * Initializes a data response from a serialized JSON object and a custom 107 | * content type. 108 | */ 109 | - (nullable instancetype)initWithJSONObject:(id)object contentType:(NSString*)type; 110 | 111 | @end 112 | 113 | NS_ASSUME_NONNULL_END 114 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerDataResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @implementation GCDWebServerDataResponse { 35 | NSData* _data; 36 | BOOL _done; 37 | } 38 | 39 | @dynamic contentType; 40 | 41 | + (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type { 42 | return [(GCDWebServerDataResponse*)[[self class] alloc] initWithData:data contentType:type]; 43 | } 44 | 45 | - (instancetype)initWithData:(NSData*)data contentType:(NSString*)type { 46 | if ((self = [super init])) { 47 | _data = data; 48 | 49 | self.contentType = type; 50 | self.contentLength = data.length; 51 | } 52 | return self; 53 | } 54 | 55 | - (NSData*)readData:(NSError**)error { 56 | NSData* data; 57 | if (_done) { 58 | data = [NSData data]; 59 | } else { 60 | data = _data; 61 | _done = YES; 62 | } 63 | return data; 64 | } 65 | 66 | - (NSString*)description { 67 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 68 | [description appendString:@"\n\n"]; 69 | [description appendString:GCDWebServerDescribeData(_data, self.contentType)]; 70 | return description; 71 | } 72 | 73 | @end 74 | 75 | @implementation GCDWebServerDataResponse (Extensions) 76 | 77 | + (instancetype)responseWithText:(NSString*)text { 78 | return [(GCDWebServerDataResponse*)[self alloc] initWithText:text]; 79 | } 80 | 81 | + (instancetype)responseWithHTML:(NSString*)html { 82 | return [(GCDWebServerDataResponse*)[self alloc] initWithHTML:html]; 83 | } 84 | 85 | + (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables { 86 | return [(GCDWebServerDataResponse*)[self alloc] initWithHTMLTemplate:path variables:variables]; 87 | } 88 | 89 | + (instancetype)responseWithJSONObject:(id)object { 90 | return [(GCDWebServerDataResponse*)[self alloc] initWithJSONObject:object]; 91 | } 92 | 93 | + (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type { 94 | return [(GCDWebServerDataResponse*)[self alloc] initWithJSONObject:object contentType:type]; 95 | } 96 | 97 | - (instancetype)initWithText:(NSString*)text { 98 | NSData* data = [text dataUsingEncoding:NSUTF8StringEncoding]; 99 | if (data == nil) { 100 | GWS_DNOT_REACHED(); 101 | return nil; 102 | } 103 | return [self initWithData:data contentType:@"text/plain; charset=utf-8"]; 104 | } 105 | 106 | - (instancetype)initWithHTML:(NSString*)html { 107 | NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding]; 108 | if (data == nil) { 109 | GWS_DNOT_REACHED(); 110 | return nil; 111 | } 112 | return [self initWithData:data contentType:@"text/html; charset=utf-8"]; 113 | } 114 | 115 | - (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables { 116 | NSMutableString* html = [[NSMutableString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; 117 | [variables enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) { 118 | [html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)]; 119 | }]; 120 | return [self initWithHTML:html]; 121 | } 122 | 123 | - (instancetype)initWithJSONObject:(id)object { 124 | return [self initWithJSONObject:object contentType:@"application/json"]; 125 | } 126 | 127 | - (instancetype)initWithJSONObject:(id)object contentType:(NSString*)type { 128 | NSData* data = [NSJSONSerialization dataWithJSONObject:object options:0 error:NULL]; 129 | if (data == nil) { 130 | GWS_DNOT_REACHED(); 131 | return nil; 132 | } 133 | return [self initWithData:data contentType:type]; 134 | } 135 | 136 | @end 137 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerErrorResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerDataResponse.h" 29 | #import "GCDWebServerHTTPStatusCodes.h" 30 | 31 | NS_ASSUME_NONNULL_BEGIN 32 | 33 | /** 34 | * The GCDWebServerDataResponse subclass of GCDWebServerDataResponse generates 35 | * an HTML body from an HTTP status code and an error message. 36 | */ 37 | @interface GCDWebServerErrorResponse : GCDWebServerDataResponse 38 | 39 | /** 40 | * Creates a client error response with the corresponding HTTP status code. 41 | */ 42 | + (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); 43 | 44 | /** 45 | * Creates a server error response with the corresponding HTTP status code. 46 | */ 47 | + (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); 48 | 49 | /** 50 | * Creates a client error response with the corresponding HTTP status code 51 | * and an underlying NSError. 52 | */ 53 | + (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); 54 | 55 | /** 56 | * Creates a server error response with the corresponding HTTP status code 57 | * and an underlying NSError. 58 | */ 59 | + (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); 60 | 61 | /** 62 | * Initializes a client error response with the corresponding HTTP status code. 63 | */ 64 | - (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); 65 | 66 | /** 67 | * Initializes a server error response with the corresponding HTTP status code. 68 | */ 69 | - (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); 70 | 71 | /** 72 | * Initializes a client error response with the corresponding HTTP status code 73 | * and an underlying NSError. 74 | */ 75 | - (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); 76 | 77 | /** 78 | * Initializes a server error response with the corresponding HTTP status code 79 | * and an underlying NSError. 80 | */ 81 | - (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); 82 | 83 | @end 84 | 85 | NS_ASSUME_NONNULL_END 86 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerErrorResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @implementation GCDWebServerErrorResponse 35 | 36 | + (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { 37 | GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); 38 | va_list arguments; 39 | va_start(arguments, format); 40 | GCDWebServerErrorResponse* response = [(GCDWebServerErrorResponse*)[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; 41 | va_end(arguments); 42 | return response; 43 | } 44 | 45 | + (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { 46 | GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); 47 | va_list arguments; 48 | va_start(arguments, format); 49 | GCDWebServerErrorResponse* response = [(GCDWebServerErrorResponse*)[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; 50 | va_end(arguments); 51 | return response; 52 | } 53 | 54 | + (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { 55 | GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); 56 | va_list arguments; 57 | va_start(arguments, format); 58 | GCDWebServerErrorResponse* response = [(GCDWebServerErrorResponse*)[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; 59 | va_end(arguments); 60 | return response; 61 | } 62 | 63 | + (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { 64 | GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); 65 | va_list arguments; 66 | va_start(arguments, format); 67 | GCDWebServerErrorResponse* response = [(GCDWebServerErrorResponse*)[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; 68 | va_end(arguments); 69 | return response; 70 | } 71 | 72 | static inline NSString* _EscapeHTMLString(NSString* string) { 73 | return [string stringByReplacingOccurrencesOfString:@"\"" withString:@"""]; 74 | } 75 | 76 | - (instancetype)initWithStatusCode:(NSInteger)statusCode underlyingError:(NSError*)underlyingError messageFormat:(NSString*)format arguments:(va_list)arguments { 77 | NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments]; 78 | NSString* title = [NSString stringWithFormat:@"HTTP Error %i", (int)statusCode]; 79 | NSString* error = underlyingError ? [NSString stringWithFormat:@"[%@] %@ (%li)", underlyingError.domain, _EscapeHTMLString(underlyingError.localizedDescription), (long)underlyingError.code] : @""; 80 | NSString* html = [NSString stringWithFormat:@"%@

%@: %@

%@

", 81 | title, title, _EscapeHTMLString(message), error]; 82 | if ((self = [self initWithHTML:html])) { 83 | self.statusCode = statusCode; 84 | } 85 | return self; 86 | } 87 | 88 | - (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { 89 | GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); 90 | va_list arguments; 91 | va_start(arguments, format); 92 | self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; 93 | va_end(arguments); 94 | return self; 95 | } 96 | 97 | - (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { 98 | GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); 99 | va_list arguments; 100 | va_start(arguments, format); 101 | self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; 102 | va_end(arguments); 103 | return self; 104 | } 105 | 106 | - (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { 107 | GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); 108 | va_list arguments; 109 | va_start(arguments, format); 110 | self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; 111 | va_end(arguments); 112 | return self; 113 | } 114 | 115 | - (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { 116 | GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); 117 | va_list arguments; 118 | va_start(arguments, format); 119 | self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; 120 | va_end(arguments); 121 | return self; 122 | } 123 | 124 | @end 125 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerFileResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerResponse.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerFileResponse subclass of GCDWebServerResponse reads the body 34 | * of the HTTP response from a file on disk. 35 | * 36 | * It will automatically set the contentType, lastModifiedDate and eTag 37 | * properties of the GCDWebServerResponse according to the file extension and 38 | * metadata. 39 | */ 40 | @interface GCDWebServerFileResponse : GCDWebServerResponse 41 | @property(nonatomic, copy) NSString* contentType; // Redeclare as non-null 42 | @property(nonatomic) NSDate* lastModifiedDate; // Redeclare as non-null 43 | @property(nonatomic, copy) NSString* eTag; // Redeclare as non-null 44 | 45 | /** 46 | * Creates a response with the contents of a file. 47 | */ 48 | + (nullable instancetype)responseWithFile:(NSString*)path; 49 | 50 | /** 51 | * Creates a response like +responseWithFile: and sets the "Content-Disposition" 52 | * HTTP header for a download if the "attachment" argument is YES. 53 | */ 54 | + (nullable instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment; 55 | 56 | /** 57 | * Creates a response like +responseWithFile: but restricts the file contents 58 | * to a specific byte range. 59 | * 60 | * See -initWithFile:byteRange: for details. 61 | */ 62 | + (nullable instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range; 63 | 64 | /** 65 | * Creates a response like +responseWithFile:byteRange: and sets the 66 | * "Content-Disposition" HTTP header for a download if the "attachment" 67 | * argument is YES. 68 | */ 69 | + (nullable instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment; 70 | 71 | /** 72 | * Initializes a response with the contents of a file. 73 | */ 74 | - (nullable instancetype)initWithFile:(NSString*)path; 75 | 76 | /** 77 | * Initializes a response like +responseWithFile: and sets the 78 | * "Content-Disposition" HTTP header for a download if the "attachment" 79 | * argument is YES. 80 | */ 81 | - (nullable instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment; 82 | 83 | /** 84 | * Initializes a response like -initWithFile: but restricts the file contents 85 | * to a specific byte range. This range should be set to (NSUIntegerMax, 0) for 86 | * the full file, (offset, length) if expressed from the beginning of the file, 87 | * or (NSUIntegerMax, length) if expressed from the end of the file. The "offset" 88 | * and "length" values will be automatically adjusted to be compatible with the 89 | * actual size of the file. 90 | * 91 | * This argument would typically be set to the value of the byteRange property 92 | * of the current GCDWebServerRequest. 93 | */ 94 | - (nullable instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range; 95 | 96 | /** 97 | * This method is the designated initializer for the class. 98 | * 99 | * If MIME type overrides are specified, they allow to customize the built-in 100 | * mapping from extensions to MIME types. Keys of the dictionary must be lowercased 101 | * file extensions without the period, and the values must be the corresponding 102 | * MIME types. 103 | */ 104 | - (nullable instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(nullable NSDictionary*)overrides; 105 | 106 | @end 107 | 108 | NS_ASSUME_NONNULL_END 109 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerFileResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import 33 | 34 | #import "GCDWebServerPrivate.h" 35 | 36 | #define kFileReadBufferSize (32 * 1024) 37 | 38 | @implementation GCDWebServerFileResponse { 39 | NSString* _path; 40 | NSUInteger _offset; 41 | NSUInteger _size; 42 | int _file; 43 | } 44 | 45 | @dynamic contentType, lastModifiedDate, eTag; 46 | 47 | + (instancetype)responseWithFile:(NSString*)path { 48 | return [(GCDWebServerFileResponse*)[[self class] alloc] initWithFile:path]; 49 | } 50 | 51 | + (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment { 52 | return [(GCDWebServerFileResponse*)[[self class] alloc] initWithFile:path isAttachment:attachment]; 53 | } 54 | 55 | + (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range { 56 | return [(GCDWebServerFileResponse*)[[self class] alloc] initWithFile:path byteRange:range]; 57 | } 58 | 59 | + (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment { 60 | return [(GCDWebServerFileResponse*)[[self class] alloc] initWithFile:path byteRange:range isAttachment:attachment mimeTypeOverrides:nil]; 61 | } 62 | 63 | - (instancetype)initWithFile:(NSString*)path { 64 | return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:NO mimeTypeOverrides:nil]; 65 | } 66 | 67 | - (instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment { 68 | return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:attachment mimeTypeOverrides:nil]; 69 | } 70 | 71 | - (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range { 72 | return [self initWithFile:path byteRange:range isAttachment:NO mimeTypeOverrides:nil]; 73 | } 74 | 75 | static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) { 76 | return [NSDate dateWithTimeIntervalSince1970:((NSTimeInterval)t->tv_sec + (NSTimeInterval)t->tv_nsec / 1000000000.0)]; 77 | } 78 | 79 | - (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(NSDictionary*)overrides { 80 | struct stat info; 81 | if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) { 82 | GWS_DNOT_REACHED(); 83 | return nil; 84 | } 85 | #ifndef __LP64__ 86 | if (info.st_size >= (off_t)4294967295) { // In 32 bit mode, we can't handle files greater than 4 GiBs (don't use "NSUIntegerMax" here to avoid potential unsigned to signed conversion issues) 87 | GWS_DNOT_REACHED(); 88 | return nil; 89 | } 90 | #endif 91 | NSUInteger fileSize = (NSUInteger)info.st_size; 92 | 93 | BOOL hasByteRange = GCDWebServerIsValidByteRange(range); 94 | if (hasByteRange) { 95 | if (range.location != NSUIntegerMax) { 96 | range.location = MIN(range.location, fileSize); 97 | range.length = MIN(range.length, fileSize - range.location); 98 | } else { 99 | range.length = MIN(range.length, fileSize); 100 | range.location = fileSize - range.length; 101 | } 102 | if (range.length == 0) { 103 | return nil; // TODO: Return 416 status code and "Content-Range: bytes */{file length}" header 104 | } 105 | } else { 106 | range.location = 0; 107 | range.length = fileSize; 108 | } 109 | 110 | if ((self = [super init])) { 111 | _path = [path copy]; 112 | _offset = range.location; 113 | _size = range.length; 114 | if (hasByteRange) { 115 | [self setStatusCode:kGCDWebServerHTTPStatusCode_PartialContent]; 116 | [self setValue:[NSString stringWithFormat:@"bytes %lu-%lu/%lu", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), (unsigned long)fileSize] forAdditionalHeader:@"Content-Range"]; 117 | GWS_LOG_DEBUG(@"Using content bytes range [%lu-%lu] for file \"%@\"", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), path); 118 | } 119 | 120 | if (attachment) { 121 | NSString* fileName = [path lastPathComponent]; 122 | NSData* data = [[fileName stringByReplacingOccurrencesOfString:@"\"" withString:@""] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES]; 123 | NSString* lossyFileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil; 124 | if (lossyFileName) { 125 | NSString* value = [NSString stringWithFormat:@"attachment; filename=\"%@\"; filename*=UTF-8''%@", lossyFileName, GCDWebServerEscapeURLString(fileName)]; 126 | [self setValue:value forAdditionalHeader:@"Content-Disposition"]; 127 | } else { 128 | GWS_DNOT_REACHED(); 129 | } 130 | } 131 | 132 | self.contentType = GCDWebServerGetMimeTypeForExtension([_path pathExtension], overrides); 133 | self.contentLength = _size; 134 | self.lastModifiedDate = _NSDateFromTimeSpec(&info.st_mtimespec); 135 | self.eTag = [NSString stringWithFormat:@"%llu/%li/%li", info.st_ino, info.st_mtimespec.tv_sec, info.st_mtimespec.tv_nsec]; 136 | } 137 | return self; 138 | } 139 | 140 | - (BOOL)open:(NSError**)error { 141 | _file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY); 142 | if (_file <= 0) { 143 | if (error) { 144 | *error = GCDWebServerMakePosixError(errno); 145 | } 146 | return NO; 147 | } 148 | if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) { 149 | if (error) { 150 | *error = GCDWebServerMakePosixError(errno); 151 | } 152 | close(_file); 153 | return NO; 154 | } 155 | return YES; 156 | } 157 | 158 | - (NSData*)readData:(NSError**)error { 159 | size_t length = MIN((NSUInteger)kFileReadBufferSize, _size); 160 | NSMutableData* data = [[NSMutableData alloc] initWithLength:length]; 161 | ssize_t result = read(_file, data.mutableBytes, length); 162 | if (result < 0) { 163 | if (error) { 164 | *error = GCDWebServerMakePosixError(errno); 165 | } 166 | return nil; 167 | } 168 | if (result > 0) { 169 | [data setLength:result]; 170 | _size -= result; 171 | } 172 | return data; 173 | } 174 | 175 | - (void)close { 176 | close(_file); 177 | } 178 | 179 | - (NSString*)description { 180 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 181 | [description appendFormat:@"\n\n{%@}", _path]; 182 | return description; 183 | } 184 | 185 | @end 186 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerStreamedResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerResponse.h" 29 | 30 | NS_ASSUME_NONNULL_BEGIN 31 | 32 | /** 33 | * The GCDWebServerStreamBlock is called to stream the data for the HTTP body. 34 | * The block must return either a chunk of data, an empty NSData when done, or 35 | * nil on error and set the "error" argument which is guaranteed to be non-NULL. 36 | */ 37 | typedef NSData* _Nullable (^GCDWebServerStreamBlock)(NSError** error); 38 | 39 | /** 40 | * The GCDWebServerAsyncStreamBlock works like the GCDWebServerStreamBlock 41 | * except the streamed data can be returned at a later time allowing for 42 | * truly asynchronous generation of the data. 43 | * 44 | * The block must call "completionBlock" passing the new chunk of data when ready, 45 | * an empty NSData when done, or nil on error and pass a NSError. 46 | * 47 | * The block cannot call "completionBlock" more than once per invocation. 48 | */ 49 | typedef void (^GCDWebServerAsyncStreamBlock)(GCDWebServerBodyReaderCompletionBlock completionBlock); 50 | 51 | /** 52 | * The GCDWebServerStreamedResponse subclass of GCDWebServerResponse streams 53 | * the body of the HTTP response using a GCD block. 54 | */ 55 | @interface GCDWebServerStreamedResponse : GCDWebServerResponse 56 | @property(nonatomic, copy) NSString* contentType; // Redeclare as non-null 57 | 58 | /** 59 | * Creates a response with streamed data and a given content type. 60 | */ 61 | + (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; 62 | 63 | /** 64 | * Creates a response with async streamed data and a given content type. 65 | */ 66 | + (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block; 67 | 68 | /** 69 | * Initializes a response with streamed data and a given content type. 70 | */ 71 | - (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; 72 | 73 | /** 74 | * This method is the designated initializer for the class. 75 | */ 76 | - (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block; 77 | 78 | @end 79 | 80 | NS_ASSUME_NONNULL_END 81 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerStreamedResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2019, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 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 copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @implementation GCDWebServerStreamedResponse { 35 | GCDWebServerAsyncStreamBlock _block; 36 | } 37 | 38 | @dynamic contentType; 39 | 40 | + (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { 41 | return [(GCDWebServerStreamedResponse*)[[self class] alloc] initWithContentType:type streamBlock:block]; 42 | } 43 | 44 | + (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { 45 | return [(GCDWebServerStreamedResponse*)[[self class] alloc] initWithContentType:type asyncStreamBlock:block]; 46 | } 47 | 48 | - (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { 49 | return [self initWithContentType:type 50 | asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { 51 | NSError* error = nil; 52 | NSData* data = block(&error); 53 | completionBlock(data, error); 54 | }]; 55 | } 56 | 57 | - (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { 58 | if ((self = [super init])) { 59 | _block = [block copy]; 60 | 61 | self.contentType = type; 62 | } 63 | return self; 64 | } 65 | 66 | - (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { 67 | _block(block); 68 | } 69 | 70 | - (NSString*)description { 71 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 72 | [description appendString:@"\n\n"]; 73 | return description; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /hopper_helper/Pods/GCDWebServer/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2014, Pierre-Olivier Latour 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 met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * The name of Pierre-Olivier Latour may not be used to endorse 12 | or promote products derived from this software without specific 13 | prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServer.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServer.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerConnection.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServerConnection.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerDataRequest.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Requests/GCDWebServerDataRequest.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerDataResponse.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Responses/GCDWebServerDataResponse.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerErrorResponse.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Responses/GCDWebServerErrorResponse.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerFileRequest.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Requests/GCDWebServerFileRequest.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerFileResponse.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Responses/GCDWebServerFileResponse.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerFunctions.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServerFunctions.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerHTTPStatusCodes.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServerHTTPStatusCodes.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerMultiPartFormRequest.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerPrivate.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServerPrivate.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerRequest.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServerRequest.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerResponse.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServerResponse.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerStreamedResponse.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Responses/GCDWebServerStreamedResponse.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Private/GCDWebServer/GCDWebServerURLEncodedFormRequest.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServer.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServer.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerConnection.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServerConnection.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerDataRequest.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Requests/GCDWebServerDataRequest.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerDataResponse.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Responses/GCDWebServerDataResponse.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerErrorResponse.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Responses/GCDWebServerErrorResponse.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerFileRequest.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Requests/GCDWebServerFileRequest.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerFileResponse.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Responses/GCDWebServerFileResponse.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerFunctions.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServerFunctions.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerHTTPStatusCodes.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServerHTTPStatusCodes.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerMultiPartFormRequest.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerRequest.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServerRequest.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerResponse.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Core/GCDWebServerResponse.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerStreamedResponse.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Responses/GCDWebServerStreamedResponse.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Headers/Public/GCDWebServer/GCDWebServerURLEncodedFormRequest.h: -------------------------------------------------------------------------------- 1 | ../../../GCDWebServer/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.h -------------------------------------------------------------------------------- /hopper_helper/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - GCDWebServer (3.5.4): 3 | - GCDWebServer/Core (= 3.5.4) 4 | - GCDWebServer/Core (3.5.4) 5 | 6 | DEPENDENCIES: 7 | - GCDWebServer (~> 3.0) 8 | 9 | SPEC REPOS: 10 | trunk: 11 | - GCDWebServer 12 | 13 | SPEC CHECKSUMS: 14 | GCDWebServer: 2c156a56c8226e2d5c0c3f208a3621ccffbe3ce4 15 | 16 | PODFILE CHECKSUM: 278b91d4fba272f8f3d2e61a0855a98125a6b524 17 | 18 | COCOAPODS: 1.10.1 19 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Pods.xcodeproj/xcuserdata/ethanarbuckle.xcuserdatad/xcschemes/GCDWebServer.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Pods.xcodeproj/xcuserdata/ethanarbuckle.xcuserdatad/xcschemes/Pods-hopper_helper.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Pods.xcodeproj/xcuserdata/ethanarbuckle.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | GCDWebServer.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 0 13 | 14 | Pods-hopper_helper.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 1 20 | 21 | 22 | SuppressBuildableAutocreation 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Target Support Files/GCDWebServer/GCDWebServer-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_GCDWebServer : NSObject 3 | @end 4 | @implementation PodsDummy_GCDWebServer 5 | @end 6 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Target Support Files/GCDWebServer/GCDWebServer-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Target Support Files/GCDWebServer/GCDWebServer.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/GCDWebServer" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GCDWebServer" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/GCDWebServer 9 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Target Support Files/GCDWebServer/GCDWebServer.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/GCDWebServer" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GCDWebServer" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/GCDWebServer 9 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Target Support Files/Pods-hopper_helper/Pods-hopper_helper-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## GCDWebServer 5 | 6 | Copyright (c) 2012-2014, Pierre-Olivier Latour 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in the 15 | documentation and/or other materials provided with the distribution. 16 | * The name of Pierre-Olivier Latour may not be used to endorse 17 | or promote products derived from this software without specific 18 | prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 24 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | Generated by CocoaPods - https://cocoapods.org 32 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Target Support Files/Pods-hopper_helper/Pods-hopper_helper-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2012-2014, Pierre-Olivier Latour 18 | All rights reserved. 19 | 20 | Redistribution and use in source and binary forms, with or without 21 | modification, are permitted provided that the following conditions are met: 22 | * Redistributions of source code must retain the above copyright 23 | notice, this list of conditions and the following disclaimer. 24 | * Redistributions in binary form must reproduce the above copyright 25 | notice, this list of conditions and the following disclaimer in the 26 | documentation and/or other materials provided with the distribution. 27 | * The name of Pierre-Olivier Latour may not be used to endorse 28 | or promote products derived from this software without specific 29 | prior written permission. 30 | 31 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 32 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 33 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 34 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 35 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 36 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 38 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 39 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 40 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41 | 42 | License 43 | BSD 44 | Title 45 | GCDWebServer 46 | Type 47 | PSGroupSpecifier 48 | 49 | 50 | FooterText 51 | Generated by CocoaPods - https://cocoapods.org 52 | Title 53 | 54 | Type 55 | PSGroupSpecifier 56 | 57 | 58 | StringsTable 59 | Acknowledgements 60 | Title 61 | Acknowledgements 62 | 63 | 64 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Target Support Files/Pods-hopper_helper/Pods-hopper_helper-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_hopper_helper : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_hopper_helper 5 | @end 6 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Target Support Files/Pods-hopper_helper/Pods-hopper_helper.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GCDWebServer" 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer" 5 | OTHER_LDFLAGS = $(inherited) -ObjC -l"GCDWebServer" -l"z" -framework "SystemConfiguration" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /hopper_helper/Pods/Target Support Files/Pods-hopper_helper/Pods-hopper_helper.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GCDWebServer" 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer" 5 | OTHER_LDFLAGS = $(inherited) -ObjC -l"GCDWebServer" -l"z" -framework "SystemConfiguration" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /hopper_helper/README.md: -------------------------------------------------------------------------------- 1 | Move the produced .hopperCPU bundle to `~/Library/Application Support/Hopper/PlugIns/v4/CPUs/HopperTweakStudio.hopperCPU`. You can make it a symlink to this project's build output for debugging convenience -------------------------------------------------------------------------------- /hopper_helper/hopper_helper.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /hopper_helper/plugin/handlers.h: -------------------------------------------------------------------------------- 1 | // 2 | // handlers.h 3 | // hopper_helper 4 | // 5 | // Created by Ethan Arbuckle on 4/12/21. 6 | // 7 | 8 | #ifndef handlers_h 9 | #define handlers_h 10 | 11 | #import "GCDWebServerDataResponse.h" 12 | 13 | #define objcInvokeT(a, b, t) ((t (*)(id, SEL))objc_msgSend)(a, NSSelectorFromString(b)) 14 | #define objcInvoke(a, b) objcInvokeT(a, b, id) 15 | #define objcInvoke_1(a, b, c) ((id (*)(id, SEL, typeof(c)))objc_msgSend)(a, NSSelectorFromString(b), c) 16 | 17 | 18 | typedef id (^HandlerBlock)(NSDictionary *, id, id); 19 | 20 | extern HandlerBlock StringsHandler; 21 | extern HandlerBlock SegmentsHandler; 22 | extern HandlerBlock ProceduresHandler; 23 | extern HandlerBlock DecompileHandler; 24 | extern HandlerBlock DisassembleHandler; 25 | extern HandlerBlock FilePathHandler; 26 | extern HandlerBlock TerminateHandler; 27 | extern HandlerBlock ProcedureSignatureHandler; 28 | extern HandlerBlock StatusHandler; 29 | extern HandlerBlock XrefsHandler; 30 | extern HandlerBlock LogMessagesHandler; 31 | extern HandlerBlock AllPseudoCodeHandler; 32 | 33 | #endif /* handlers_h */ 34 | -------------------------------------------------------------------------------- /hopper_helper/plugin/handlers.m: -------------------------------------------------------------------------------- 1 | // 2 | // handlers.m 3 | // pluginBackend 4 | // 5 | // Created by Ethan Arbuckle on 4/12/21. 6 | // 7 | 8 | #import 9 | #import "handlers.h" 10 | #include 11 | 12 | 13 | // List all strings in a document 14 | HandlerBlock StringsHandler = ^id(NSDictionary *requestData, id hopperDocument, id disassembledFile) { 15 | 16 | if (!disassembledFile) { 17 | return nil; 18 | } 19 | 20 | return objcInvoke(disassembledFile, @"allStrings"); 21 | }; 22 | 23 | 24 | // List all segments in a document 25 | HandlerBlock SegmentsHandler = ^id(NSDictionary *requestData, id hopperDocument, id disassembledFile) { 26 | 27 | if (!disassembledFile) { 28 | return nil; 29 | } 30 | 31 | NSArray *segments = objcInvoke(disassembledFile, @"segments"); 32 | 33 | NSMutableArray *segmentNames = [[NSMutableArray alloc] init]; 34 | for (id segment in segments) { 35 | NSString *segmentName = objcInvoke(segment, @"segmentName"); 36 | [segmentNames addObject:segmentName]; 37 | } 38 | 39 | return segmentNames; 40 | }; 41 | 42 | 43 | // List all procedures in a document 44 | HandlerBlock ProceduresHandler = ^id(NSDictionary *requestData, id hopperDocument, id disassembledFile) { 45 | 46 | if (!disassembledFile) { 47 | return nil; 48 | } 49 | 50 | NSArray *allNamedAddresses = objcInvoke(disassembledFile, @"allNamedAddresses"); 51 | 52 | NSMutableArray *response = [[NSMutableArray alloc] init]; 53 | for (id address in allNamedAddresses) { 54 | 55 | NSString *demangledName = objcInvoke_1(disassembledFile, @"demangledNameForVirtualAddress:", [address longLongValue]); 56 | NSDictionary *namedEntry = @{ 57 | @"address": @([address unsignedLongLongValue]), 58 | @"label": demangledName, 59 | }; 60 | [response addObject:namedEntry]; 61 | } 62 | 63 | return response; 64 | }; 65 | 66 | 67 | // Get pseudocode text for a specified function start address 68 | HandlerBlock DecompileHandler = ^id(NSDictionary *requestData, id hopperDocument, id disassembledFile) { 69 | 70 | NSString *procedureAddress = requestData[@"procedure_address"]; 71 | if (!disassembledFile || !procedureAddress) { 72 | return nil; 73 | } 74 | 75 | id procedure = objcInvoke_1(disassembledFile, @"procedureAt:", [procedureAddress longLongValue]); 76 | if (!procedure) { 77 | return nil; 78 | } 79 | 80 | id pseudoCode = objcInvoke(procedure, @"completePseudoCode"); 81 | NSString *pseudoCodeString = objcInvoke(pseudoCode, @"string"); 82 | 83 | return pseudoCodeString; 84 | }; 85 | 86 | 87 | // Get assembly text for a specified function start address 88 | HandlerBlock DisassembleHandler = ^id(NSDictionary *requestData, id hopperDocument, id disassembledFile) { 89 | 90 | NSString *procedureAddress = requestData[@"procedure_address"]; 91 | if (!disassembledFile || !procedureAddress) { 92 | return nil; 93 | } 94 | 95 | id procedure = objcInvoke_1(disassembledFile, @"procedureAt:", [procedureAddress longLongValue]); 96 | if (!procedure) { 97 | return nil; 98 | } 99 | NSArray *basicBlocks = objcInvoke(procedure, @"basicBlocks"); 100 | id containingSegment = objcInvoke_1(disassembledFile, @"segmentForVirtualAddress:", [procedureAddress longLongValue]); 101 | 102 | NSMutableString *procedureAssemblyText = [[NSMutableString alloc] init]; 103 | for (id basicBlock in basicBlocks) { 104 | 105 | uint64_t instrCursor = objcInvokeT(basicBlock, @"from", uint64_t); 106 | uint64_t basicBlockEndAddress = objcInvokeT(basicBlock, @"to", uint64_t); 107 | while (instrCursor <= basicBlockEndAddress) { 108 | 109 | NSArray *asmLines = ((id (*)(id, SEL, uint64_t, BOOL, BOOL, BOOL, BOOL, BOOL))objc_msgSend)(containingSegment, NSSelectorFromString(@"stringsForVirtualAddress:includingDecorations:inlineComments:addressField:hexColumn:compactMode:"), instrCursor, YES, YES, YES, NO, NO); 110 | 111 | for (id asmLine in asmLines) { 112 | NSString *assemblyLineText = objcInvoke(asmLine, @"string"); 113 | [procedureAssemblyText appendString:assemblyLineText]; 114 | [procedureAssemblyText appendString:@"\n"]; 115 | } 116 | 117 | instrCursor += ((uint64_t (*)(id, SEL, uint64_t))objc_msgSend)(containingSegment, NSSelectorFromString(@"getByteLengthAtVirtualAddress:"), instrCursor); 118 | } 119 | } 120 | 121 | return procedureAssemblyText; 122 | }; 123 | 124 | 125 | // Get the executable path for a document 126 | HandlerBlock FilePathHandler = ^id(NSDictionary *requestData, id hopperDocument, id disassembledFile) { 127 | 128 | if (!disassembledFile) { 129 | return nil; 130 | } 131 | 132 | return objcInvoke(disassembledFile, @"originalFilePath"); 133 | }; 134 | 135 | 136 | // Terminate Hopper (all documents) 137 | HandlerBlock TerminateHandler = ^id(NSDictionary *requestData, id hopperDocument, id disassembledFile) { 138 | 139 | exit(0); 140 | return nil; 141 | }; 142 | 143 | 144 | // Get the prettified function signature for a specified function start address 145 | HandlerBlock ProcedureSignatureHandler = ^id(NSDictionary *requestData, id hopperDocument, id disassembledFile) { 146 | 147 | NSString *procedureAddress = requestData[@"procedure_address"]; 148 | if (!disassembledFile || !procedureAddress) { 149 | return nil; 150 | } 151 | 152 | id procedure = objcInvoke_1(disassembledFile, @"procedureAt:", [procedureAddress longLongValue]); 153 | if (!procedure) { 154 | return nil; 155 | } 156 | 157 | id signaturePseudoCode = objcInvoke(procedure, @"signaturePseudoCode"); 158 | return objcInvoke(signaturePseudoCode, @"string"); 159 | }; 160 | 161 | 162 | // Query status of a Document's analysis 163 | HandlerBlock StatusHandler = ^id(NSDictionary *requestData, id hopperDocument, id disassembledFile) { 164 | 165 | if (!disassembledFile) { 166 | return nil; 167 | } 168 | 169 | BOOL activeAnalysis = objcInvokeT(disassembledFile, @"analysisInProgress", BOOL); 170 | return @{@"analysis_active": @(activeAnalysis)}; 171 | }; 172 | 173 | 174 | // List cross-references to a specified address 175 | HandlerBlock XrefsHandler = ^id(NSDictionary *requestData, id hopperDocument, id disassembledFile) { 176 | 177 | NSString *procedureAddress = requestData[@"procedure_address"]; 178 | if (!disassembledFile || !procedureAddress) { 179 | return nil; 180 | } 181 | 182 | uint64_t address = [procedureAddress longLongValue]; 183 | id containingSegment = objcInvoke_1(disassembledFile, @"segmentForVirtualAddress:", address); 184 | id xrefAsmLine = objcInvoke_1(containingSegment, @"formatXREFStringForAddress:", address); 185 | return objcInvoke(xrefAsmLine, @"string"); 186 | }; 187 | 188 | 189 | // Get all the log messages in the Log view for a document 190 | HandlerBlock LogMessagesHandler = ^id(NSDictionary *requestData, id hopperDocument, id disassembledFile) { 191 | 192 | if (!disassembledFile) { 193 | return nil; 194 | } 195 | 196 | id logView = objcInvoke(hopperDocument, @"logView"); 197 | return objcInvoke(logView, @"string"); 198 | }; 199 | 200 | 201 | // Produce pseudocode for all procedures in a document 202 | HandlerBlock AllPseudoCodeHandler = ^id(NSDictionary *requestData, id hopperDocument, id disassembledFile) { 203 | 204 | if (!disassembledFile) { 205 | return nil; 206 | } 207 | 208 | NSArray *allProcedures = objcInvoke(disassembledFile, @"allProcedures"); 209 | 210 | dispatch_queue_t exportQueue = dispatch_queue_create("com.cryptic-apps.hopper.pseudo-code.export", DISPATCH_QUEUE_SERIAL); 211 | __block NSMutableString *allPseudoCode = [[NSMutableString alloc] init]; 212 | dispatch_apply([allProcedures count], exportQueue, ^(size_t index) { 213 | 214 | id procedure = allProcedures[index]; 215 | id pseudoCode = ((id (*)(id, SEL, void *, int))objc_msgSend)(procedure, NSSelectorFromString(@"completePseudoCodeWithCancelationBlock:andOptions:"), NULL, 7); 216 | NSString *pseudoCodeString = objcInvoke(pseudoCode, @"string"); 217 | [allPseudoCode appendFormat:@"%@\n", pseudoCodeString]; 218 | }); 219 | 220 | return allPseudoCode; 221 | }; 222 | -------------------------------------------------------------------------------- /hopper_helper/plugin/hopper_backend.m: -------------------------------------------------------------------------------- 1 | // 2 | // hopper_helper.m 3 | // hopper_helper 4 | // 5 | // Created by Ethan Arbuckle on 4/6/21. 6 | // 7 | 8 | #import 9 | #import "GCDWebServerDataRequest.h" 10 | #import "GCDWebServer.h" 11 | #include 12 | #include 13 | #import "handlers.h" 14 | 15 | 16 | static NSDictionary *(^allDocuments)(void) = ^id() { 17 | NSMutableDictionary *docNamesAndDocs = [[NSMutableDictionary alloc] init]; 18 | 19 | NSArray *allDocuments = objcInvoke(NSClassFromString(@"HopperAppDelegate"), @"allDocuments"); 20 | for (id document in allDocuments) { 21 | NSString *documentName = objcInvoke(document, @"documentName"); 22 | docNamesAndDocs[documentName] = document; 23 | } 24 | return docNamesAndDocs; 25 | }; 26 | 27 | __attribute__((constructor)) void init(void) { 28 | 29 | NSLog(@"real TweakStudio plugin injected"); 30 | 31 | // Create server 32 | GCDWebServer* webServer = [[GCDWebServer alloc] init]; 33 | void (^addPostHandler)(NSString *path, HandlerBlock) = ^void(NSString *path, id (^handler)(NSDictionary *requestData, id hopperDocument, id disassembledFile)) { 34 | 35 | [webServer addHandlerForMethod:@"POST" path:path requestClass:[GCDWebServerDataRequest class] processBlock:^GCDWebServerResponse * _Nullable(__kindof GCDWebServerDataRequest * _Nonnull request) { 36 | 37 | NSDictionary *requestBody = [NSJSONSerialization JSONObjectWithData:request.data options:0 error:nil]; 38 | NSString *requestedDocument = requestBody[@"document_name"]; 39 | 40 | id hopperDocument = nil; 41 | id disassembledFile = nil; 42 | if (requestedDocument) { 43 | 44 | // Search for a document with the requested name 45 | NSDictionary *documents = allDocuments(); 46 | for (NSString *documentName in documents) { 47 | if ([documentName isEqualToString:requestedDocument]) { 48 | hopperDocument = documents[documentName]; 49 | break; 50 | } 51 | } 52 | 53 | disassembledFile = objcInvoke(hopperDocument, @"disassembledFile"); 54 | } 55 | 56 | id handlerResponse = handler(requestBody, hopperDocument, disassembledFile); 57 | NSMutableDictionary *wrappedResponse = [[NSMutableDictionary alloc] init]; 58 | 59 | wrappedResponse[@"data"] = handlerResponse; 60 | if (!handlerResponse) { 61 | // todo errors 62 | wrappedResponse[@"error"] = @"nondescript error"; 63 | } 64 | 65 | return [GCDWebServerDataResponse responseWithJSONObject:wrappedResponse]; 66 | }]; 67 | }; 68 | 69 | addPostHandler(@"/strings", StringsHandler); 70 | addPostHandler(@"/segments", SegmentsHandler); 71 | addPostHandler(@"/procedures", ProceduresHandler); 72 | addPostHandler(@"/decompile", DecompileHandler); 73 | addPostHandler(@"/disassemble", DisassembleHandler); 74 | addPostHandler(@"/filepath", FilePathHandler); 75 | addPostHandler(@"/terminate", TerminateHandler); 76 | addPostHandler(@"/procedure_signature", ProcedureSignatureHandler); 77 | addPostHandler(@"/status", StatusHandler); 78 | addPostHandler(@"/xrefs", XrefsHandler); 79 | addPostHandler(@"/logs", LogMessagesHandler); 80 | addPostHandler(@"/all_code", AllPseudoCodeHandler); 81 | 82 | [webServer addHandlerForMethod:@"GET" path:@"/documents" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse * _Nullable(__kindof GCDWebServerRequest * _Nonnull request) { 83 | 84 | NSArray *allDocumentNames = [allDocuments() allKeys]; 85 | return [GCDWebServerDataResponse responseWithJSONObject:@{@"data": allDocumentNames}]; 86 | }]; 87 | 88 | int serverPort = 52349; 89 | if (![webServer startWithPort:serverPort bonjourName:nil]) { 90 | 91 | NSLog(@"Hopper already running on port: %d", serverPort); 92 | exit(0); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /hopper_proxy.py: -------------------------------------------------------------------------------- 1 | # 2 | # hopper_proxy.py 3 | # IDA Objc 4 | # 5 | # Created by Ethan Arbuckle on 2021-03-05 6 | # Copyright (c) 2021 Ethan Arbuckle and Tanner Bennett. All rights reserved. 7 | # 8 | 9 | import json 10 | import subprocess 11 | import typing 12 | from abc import abstractmethod 13 | from http.server import BaseHTTPRequestHandler, HTTPServer 14 | 15 | if typing.TYPE_CHECKING: 16 | from hopper_api import Document 17 | 18 | 19 | class HopperHandler(object): 20 | @abstractmethod 21 | def run(cls): 22 | pass 23 | 24 | @classmethod 25 | def get_document_named(cls, document_name): 26 | for document in Document.getAllDocuments(): 27 | if document.getDocumentName() == document_name: 28 | return document 29 | raise Exception("failed to find specified document") 30 | 31 | 32 | class TerminateHopper(HopperHandler): 33 | PATH = "/terminate" 34 | 35 | @classmethod 36 | def kill_hopper(cls): 37 | """Kill all running Hopper processes""" 38 | ps_output = subprocess.check_output(["ps", "aux"]).decode("utf-8") 39 | for ps_line in ps_output.splitlines(): 40 | # Look for Hopper 41 | if "Hopper" in ps_line: 42 | components = ps_line.split(" ") 43 | # PID is the first number 44 | pid = [component for component in components if component.isnumeric()][0] 45 | # Terminate it 46 | subprocess.check_output(["kill", pid]) 47 | 48 | @classmethod 49 | def run(cls): 50 | TerminateHopper.kill_hopper() 51 | pass 52 | 53 | 54 | class ListSegments(HopperHandler): 55 | PATH = "/segments" 56 | 57 | @classmethod 58 | def run(cls, document_name): 59 | document = cls.get_document_named(document_name) 60 | segments = document.getSegmentsList() 61 | return [segment.getName() for segment in segments] 62 | 63 | 64 | class ListProcedures(HopperHandler): 65 | PATH = "/procedures" 66 | 67 | @classmethod 68 | def run(cls, document_name): 69 | document = cls.get_document_named(document_name) 70 | 71 | named_procedures = [] 72 | for segment_name in ListSegments.run(): 73 | segment = document.getSegmentByName(segment_name) 74 | for label_address in segment.getNamedAddresses(): 75 | named_procedures.append( 76 | { 77 | "label": segment.getDemangledNameAtAddress(label_address), 78 | "address": label_address, 79 | } 80 | ) 81 | 82 | return named_procedures 83 | 84 | 85 | class ListStrings(HopperHandler): 86 | PATH = "/strings" 87 | 88 | @classmethod 89 | def run(cls, document_name): 90 | document = cls.get_document_named(document_name) 91 | 92 | cstrings_sect = document.getSectionByName("__cstring") 93 | text_seg = document.getSegmentByName("__TEXT") 94 | cstring_start = cstrings_sect.getStartingAddress() 95 | 96 | string_cursor = 0 97 | strings = [] 98 | while string_cursor < cstrings_sect.getLength(): 99 | stringlen = text_seg.getObjectLength(cstring_start + string_cursor) 100 | string = text_seg.readBytes(cstring_start + string_cursor, stringlen - 1).strip() 101 | string_cursor += max(stringlen, 1) 102 | strings.append(string) 103 | return strings 104 | 105 | 106 | class DecompileProcedure(HopperHandler): 107 | PATH = "/decompile" 108 | 109 | @classmethod 110 | def run(cls, document_name, procedure_address): 111 | if not procedure_address: 112 | raise Exception("did not specify procedure address") 113 | 114 | document = cls.get_document_named(document_name) 115 | 116 | # Hopper's API requires you to know the segment that contains a procedure (even though procs are 117 | # referenced by their absolute address in the binary). 118 | # Try all known segments 119 | for segment_name in ListSegments.run(): 120 | segment = document.getSegmentByName(segment_name) 121 | procedure_candidate = segment.getProcedureAtAddress(procedure_address) 122 | if procedure_candidate: 123 | return procedure_candidate.decompile() 124 | 125 | raise Exception("Failed to find the specified procedure") 126 | 127 | 128 | class DisassembleProcedure(HopperHandler): 129 | PATH = "/disassemble" 130 | 131 | @classmethod 132 | def run(cls, document_name, procedure_address): 133 | document = cls.get_document_named(document_name) 134 | if not procedure_address: 135 | raise Exception("did not specify procedure address") 136 | 137 | disassembly = "" 138 | 139 | # Hopper's API requires you to know the segment that contains a procedure (even though procs are 140 | # referenced by their absolute address in the binary). 141 | # Try all known segments 142 | for segment_name in ListSegments.run(): 143 | segment = document.getSegmentByName(segment_name) 144 | procedure_candidate = segment.getProcedureAtAddress(procedure_address) 145 | if procedure_candidate: 146 | 147 | for basic_block in procedure_candidate.basicBlockIterator(): 148 | basic_block_start = basic_block.getStartingAddress() 149 | instr_cursor = basic_block_start 150 | while instr_cursor < basic_block.getEndingAddress(): 151 | instr = segment.getInstructionAtAddress(instr_cursor) 152 | instr_args = [instr.getFormattedArgument(i) for i in xrange(instr.getArgumentCount())] 153 | 154 | instr_string = instr.getInstructionString() + " " 155 | instr_string += ", ".join(instr_args) 156 | instr_string += "\n" 157 | disassembly += instr_string 158 | 159 | instr_cursor += instr.getInstructionLength() 160 | # Maybe this should return a list of instructions, instead of combining them into 1 string? 161 | return disassembly 162 | 163 | 164 | class ListDocuments(HopperHandler): 165 | PATH = "/documents" 166 | 167 | @classmethod 168 | def run(cls): 169 | 170 | documents = [] 171 | for document in Document.getAllDocuments(): 172 | documents.append(document.getDocumentName()) 173 | 174 | return documents 175 | 176 | 177 | class BackgroundProcessActive(HopperHandler): 178 | PATH = "/analysis" 179 | 180 | @classmethod 181 | def run(cls, document_name): 182 | document = cls.get_document_named(document_name) 183 | return {"active": document.backgroundProcessActive()} 184 | 185 | 186 | class DocumentFilePath(HopperHandler): 187 | PATH = "/filepath" 188 | 189 | @classmethod 190 | def run(cls, document_name): 191 | document = cls.get_document_named(document_name) 192 | return document.getExecutableFilePath() 193 | 194 | 195 | class RequestHandler(BaseHTTPRequestHandler): 196 | def do_POST(self): 197 | content_length = int(self.headers.get("Content-Length", 0)) 198 | posted_data = json.loads(self.rfile.read(content_length)) if content_length > 0 else {} 199 | 200 | data_response = None 201 | error = None 202 | 203 | for handler in HopperHandler.__subclasses__(): 204 | if self.path == handler.PATH: 205 | 206 | try: 207 | data_response = handler.run(**posted_data) 208 | json.dumps(data_response) 209 | self.send_response(200) 210 | except TypeError as e: 211 | self.send_response(500) 212 | error = str(e) 213 | except Exception as e: 214 | self.send_response(500) 215 | error = str(e) 216 | 217 | response = {"data": data_response} 218 | if error: 219 | response["error"] = error 220 | 221 | self.send_header("Content-type", "application/json") 222 | self.end_headers() 223 | self.wfile.write(json.dumps(response).encode("utf-8")) 224 | 225 | 226 | def start_server(): 227 | 228 | httpd = HTTPServer(("", 52349), RequestHandler) 229 | 230 | try: 231 | httpd.serve_forever() 232 | except KeyboardInterrupt: 233 | pass 234 | 235 | httpd.server_close() 236 | -------------------------------------------------------------------------------- /lzssdec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanArbuckle/Hopper-Disassembler-API/c4740de2bda6a0826e9a94e9df93d7f8d5891557/lzssdec -------------------------------------------------------------------------------- /patch_tcc_db.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import sqlite3 3 | from pathlib import Path 4 | 5 | tcc_db_path = Path("~/Library/Application Support/com.apple.TCC/TCC.db").expanduser() 6 | hopper_identifier = "com.cryptic-apps.hopper-web-4" 7 | vscode_identifier = "com.microsoft.VSCode" 8 | 9 | connection = sqlite3.connect(tcc_db_path.as_posix()) 10 | cursor = connection.cursor() 11 | 12 | hopper_code_obj_identity = None 13 | 14 | cursor.execute("select * from access") 15 | for row in cursor.fetchall(): 16 | entry_indirect_object_identifier = row[8] 17 | if entry_indirect_object_identifier == hopper_identifier: 18 | hopper_code_obj_identity = row[9] 19 | break 20 | 21 | if hopper_code_obj_identity: 22 | cursor.execute( 23 | "insert into access VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", 24 | ( 25 | "kTCCServiceAppleEvents", 26 | vscode_identifier, 27 | 0, 28 | 1, 29 | 0, 30 | None, 31 | None, 32 | 0, 33 | hopper_identifier, 34 | hopper_code_obj_identity, 35 | None, 36 | int(datetime.datetime.now().timestamp()), 37 | ), 38 | ) 39 | 40 | connection.commit() 41 | -------------------------------------------------------------------------------- /run_hopper.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | import subprocess 4 | import time 5 | from http import server 6 | from pathlib import Path 7 | 8 | import requests 9 | 10 | from hopper_proxy import TerminateHopper 11 | 12 | logger = logging.getLogger("hopper_launch") 13 | logger.setLevel(logging.DEBUG) 14 | ch = logging.StreamHandler() 15 | ch.setLevel(logging.DEBUG) 16 | formatter = logging.Formatter("%(asctime)s: %(message)s") 17 | ch.setFormatter(formatter) 18 | logger.addHandler(ch) 19 | 20 | TerminateHopper.kill_hopper() 21 | 22 | hopper_launcher_path = "/Applications/Hopper Disassembler v4.app/Contents/MacOS/hopper" 23 | hopper_path = "/Applications/Hopper Disassembler v4.app/Contents/MacOS/Hopper Disassembler v4" 24 | 25 | 26 | def server_list_documents(port): 27 | endpoint = f"http://localhost:{port}/documents" 28 | response = requests.get(endpoint) 29 | return response.json().get("data") 30 | 31 | 32 | def server_get_doc_filepath(port, document_name): 33 | endpoint = f"http://localhost:{port}/filepath" 34 | try: 35 | response = requests.post(endpoint, data=json.dumps({"document_name": document_name})) 36 | return response.json().get("data") 37 | except Exception as e: 38 | print(e) 39 | return None 40 | 41 | 42 | def _launch_binary_workaround_hopper_bug(binary: Path, command_args): 43 | # Launch Hopper with a test binary and the proxy script 44 | # Work around the "missing loader" bug 45 | for _ in range(2): 46 | try: 47 | subprocess.check_output([hopper_launcher_path, "-A", "-e", binary.as_posix()] + command_args, stderr=subprocess.STDOUT) 48 | break 49 | except subprocess.CalledProcessError as e: 50 | if e.stdout and b"The handler some object is not defined." in e.stdout: 51 | # Workaround the bug 52 | try: 53 | subprocess.check_output([hopper_launcher_path], stderr=subprocess.STDOUT) 54 | except: 55 | pass 56 | time.sleep(1) 57 | else: 58 | print(e.stdout) 59 | raise 60 | 61 | 62 | def _is_binary_fat(binary: Path): 63 | """Is this a FAT macho""" 64 | # Use lipo to determine if the file is FAT 65 | return b"the fat file" in subprocess.check_output(["/usr/bin/lipo", "-info", binary.as_posix()]) 66 | 67 | 68 | def launch_server(): 69 | """Launch Hopper with a dummy document and start a proxy server.""" 70 | # Small dummy binary (TODO: use smaller binary) 71 | dummy_document = Path("lzssdec").resolve() 72 | hopper_args = [ 73 | "-l", 74 | "Mach-O", 75 | "--intel-64", 76 | ] 77 | # Launch the hopper server. This server will handle requests for any subsequent Documents that are opened. 78 | # This is basically idempotent - repeat launches will silently fail to bind the server port 79 | _launch_binary_workaround_hopper_bug(dummy_document, hopper_args) 80 | 81 | 82 | def find_hopper_server_port(): 83 | """ Find the port that Hopper's server is running on 84 | """ 85 | try: 86 | hopper_launch_output = subprocess.check_output([hopper_path], stderr=subprocess.STDOUT) 87 | port = hopper_launch_output.split(b"Hopper already running on port: ")[1] 88 | return int(port) 89 | except: 90 | pass 91 | return None 92 | 93 | 94 | def open_binary_in_hopper(binary: Path, arch_flag): 95 | """Launch a binary in Hopper. (No server is started)""" 96 | hopper_args = [ 97 | "-l", 98 | "Mach-O", 99 | arch_flag, 100 | ] 101 | if _is_binary_fat(binary): 102 | # If its FAT, add the correct loader flag 103 | hopper_args += ["-l", "FAT"] 104 | _launch_binary_workaround_hopper_bug(binary, hopper_args) 105 | 106 | 107 | def wait_for_document(port, document_name): 108 | """Wait for a specific Document to become available""" 109 | while True: 110 | try: 111 | documents = server_list_documents(port) 112 | if document_name in documents: 113 | break 114 | except requests.exceptions.ConnectionError: 115 | pass 116 | 117 | 118 | def wait_for_new_document(port, previous_docs): 119 | """Wait for a previously-unknown Document to become available. 120 | previous_docs: Document names that are already known 121 | """ 122 | while True: 123 | try: 124 | new_documents = server_list_documents(port) 125 | if len(new_documents) > len(previous_docs): 126 | # Find the new item 127 | new_document = [new_doc for new_doc in new_documents if new_doc not in previous_docs][0] 128 | return new_document 129 | except requests.exceptions.ConnectionError: 130 | pass 131 | 132 | 133 | def wait_for_named_document_with_path(port, document_file_path): 134 | """Wait for a Document to become available that: 135 | 1. Has a real name, which indicates it is not still processing 136 | 2. Has a executablePath that matches the provided document_file_path 137 | """ 138 | while True: 139 | try: 140 | new_documents = server_list_documents(port) 141 | for document in new_documents: 142 | # Skip un-named (still analyzing) docs 143 | if "Untitle" in document: 144 | continue 145 | if document_file_path == server_get_doc_filepath(port, document): 146 | return document 147 | 148 | time.sleep(1) 149 | 150 | except requests.exceptions.ConnectionError: 151 | pass 152 | 153 | 154 | testbin_path = Path("/Users/ethanarbuckle/Desktop/decrypt")#Path("/Users/ethanarbuckle/Downloads/app_downloads/Payload 65/app-decrypt-com.cvs.cvspharmacyr1buo3ck.app/CVSOnlineiPhone") 155 | # Launch a dummy document. It will host the server that handles responses for *all* documents 156 | launch_server() 157 | 158 | # Find the port the server is hosting on 159 | server_port = find_hopper_server_port() 160 | if not server_port: 161 | print("failed to find server port!") 162 | exit(0) 163 | 164 | # Wait for the server to launch 165 | wait_for_document(server_port, "lzssdec.hop") 166 | logger.info(f"server launched on port {server_port}") 167 | 168 | # Note the names of the current Hopper documents 169 | current_hopper_docs = server_list_documents(server_port) 170 | 171 | # Open the requested document. 172 | arch_flags = "--aarch64" 173 | open_binary_in_hopper(testbin_path, arch_flags) 174 | 175 | # Wait for the server to acknowledge the new document 176 | # new_doc_name = testbin_path.name + ".hop" 177 | document_name = wait_for_new_document(server_port, current_hopper_docs) 178 | logger.info(f"found new document: {document_name}") 179 | if "Untitle" in document_name: 180 | logger.info("waiting for initial analysis to finish...") 181 | # Wait for a named document to become available 182 | document_name = wait_for_named_document_with_path(server_port, testbin_path.as_posix()) 183 | 184 | logger.info(f"document ready: {document_name}") 185 | --------------------------------------------------------------------------------