mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-27 02:00:41 +00:00
Support for tvOS
- add tvOS target - support code signing tvOS cores by adding an argument to the code signing cores script - use NSCachesDirectory for the documents directory - add some mfi controller handling logic to set non-game controllers to the last index to avoid interfering with operation - autodetect mfi controller for apple tv on startup - added autodetect to hid joypad - added a webserver to transfer files for tvOS - xcode: clean up project, remove unused folders - remove HAVE_MATERIALUI setting for tvos build, make it use XMB as default - added retroarch app icon courtesy of @MrJs - added auto-detect of mfi controller for apple tv
This commit is contained in:
parent
d357cf9986
commit
a18512375b
1
.gitignore
vendored
1
.gitignore
vendored
@ -122,6 +122,7 @@ wiiu/wut/elf2rpl/elf2rpl
|
||||
|
||||
pkg/apple/iOS/build/
|
||||
pkg/apple/iOS/modules/
|
||||
pkg/apple/tvOS/modules/
|
||||
pkg/apple/build/
|
||||
ui/drivers/qt/moc_*
|
||||
ui/drivers/moc_*
|
||||
|
@ -125,7 +125,9 @@ static OSStatus audio_write_cb(void *userdata,
|
||||
static void coreaudio_interrupt_listener(void *data, UInt32 interrupt_state)
|
||||
{
|
||||
(void)data;
|
||||
#if TARGET_OS_IOS
|
||||
g_interrupted = (interrupt_state == kAudioSessionBeginInterruption);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
static void choose_output_device(coreaudio_t *dev, const char* device)
|
||||
@ -218,7 +220,7 @@ static void *coreaudio_init(const char *device,
|
||||
dev->lock = slock_new();
|
||||
dev->cond = scond_new();
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#if TARGET_OS_IOS
|
||||
if (!session_initialized)
|
||||
{
|
||||
session_initialized = true;
|
||||
|
@ -115,7 +115,11 @@ static NSSearchPathDirectory NSConvertFlagsCF(unsigned flags)
|
||||
switch (flags)
|
||||
{
|
||||
case CFDocumentDirectory:
|
||||
#if TARGET_OS_IOS
|
||||
return NSDocumentDirectory;
|
||||
#elif TARGET_OS_TV
|
||||
return NSCachesDirectory;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -363,7 +367,7 @@ static void frontend_darwin_get_environment_settings(int *argc, char *argv[],
|
||||
home_dir_buf, "shaders_glsl",
|
||||
sizeof(g_defaults.dirs[DEFAULT_DIR_SHADER]));
|
||||
#endif
|
||||
#if TARGET_OS_IPHONE
|
||||
#if TARGET_OS_IOS
|
||||
int major, minor;
|
||||
get_ios_version(&major, &minor);
|
||||
if (major >= 10 )
|
||||
@ -372,6 +376,16 @@ static void frontend_darwin_get_environment_settings(int *argc, char *argv[],
|
||||
else
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE],
|
||||
home_dir_buf, "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
|
||||
#elif TARGET_OS_TV
|
||||
printf("tvOS bundle path = %s",bundle_path_buf);
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE],
|
||||
bundle_path_buf, "modules", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
|
||||
NSError *fileError;
|
||||
NSArray *bundleFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[NSString stringWithUTF8String:g_defaults.dirs[DEFAULT_DIR_CORE]] error:&fileError];
|
||||
NSLog(@"tvOS Files in modules:");
|
||||
for (NSString *f in bundleFiles) {
|
||||
NSLog(@"%@",f);
|
||||
}
|
||||
#else
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], home_dir_buf, "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
|
||||
#endif
|
||||
@ -423,7 +437,7 @@ static void frontend_darwin_get_environment_settings(int *argc, char *argv[],
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#if TARGET_OS_IOS
|
||||
char assets_zip_path[PATH_MAX_LENGTH];
|
||||
if (major > 8)
|
||||
strlcpy(g_defaults.path.buildbot_server_url, "http://buildbot.libretro.com/nightly/apple/ios9/latest/", sizeof(g_defaults.path.buildbot_server_url));
|
||||
@ -582,7 +596,7 @@ static enum frontend_powerstate frontend_darwin_get_powerstate(int *seconds, int
|
||||
end:
|
||||
if (blob)
|
||||
CFRelease(blob);
|
||||
#elif defined(IOS)
|
||||
#elif TARGET_OS_IOS
|
||||
float level;
|
||||
UIDevice *uidev = [UIDevice currentDevice];
|
||||
|
||||
|
@ -133,12 +133,16 @@ static void glkitview_init_xibs(void)
|
||||
void *glkitview_init(void)
|
||||
{
|
||||
#if defined(HAVE_COCOATOUCH)
|
||||
#if TARGET_OS_IOS
|
||||
glkitview_init_xibs();
|
||||
#endif
|
||||
|
||||
g_view = [GLKView new];
|
||||
#if TARGET_OS_IOS
|
||||
g_view.multipleTouchEnabled = YES;
|
||||
[g_view addSubview:g_pause_indicator_view];
|
||||
#endif
|
||||
g_view.enableSetNeedsDisplay = NO;
|
||||
[g_view addSubview:g_pause_indicator_view];
|
||||
|
||||
return (BRIDGE void *)((GLKView*)g_view);
|
||||
#else
|
||||
|
@ -38,11 +38,11 @@
|
||||
|
||||
#if defined(HAVE_COCOATOUCH)
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#if TARGET_OS_IOS
|
||||
#include "../ui/drivers/cocoa/cocoatouch_menu.m"
|
||||
#endif
|
||||
|
||||
#include "../ui/drivers/ui_cocoatouch.m"
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE_COCOA)
|
||||
#include "../ui/drivers/ui_cocoa.m"
|
||||
|
@ -19,8 +19,19 @@
|
||||
|
||||
static const hid_driver_t *generic_hid = NULL;
|
||||
|
||||
static void hid_joypad_autodetect_add(unsigned autoconf_pad)
|
||||
{
|
||||
#ifdef TARGET_OS_TV
|
||||
// AppleTV: mfi controllers are HID joypad - add an autodetect here
|
||||
if ( !input_autoconfigure_connect("Mfi Controller", NULL, hid_joypad.ident, autoconf_pad, 0, 0)) {
|
||||
input_config_set_device_name(autoconf_pad, "Mfi Controller");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool hid_joypad_init(void *data)
|
||||
{
|
||||
hid_joypad_autodetect_add(0);
|
||||
generic_hid = input_hid_init_first();
|
||||
if (!generic_hid)
|
||||
return false;
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include <boolean.h>
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
#import "../include/GameController/GameController.h"
|
||||
#import <GameController/GameController.h>
|
||||
|
||||
#ifndef MAX_MFI_CONTROLLERS
|
||||
#define MAX_MFI_CONTROLLERS 4
|
||||
@ -33,6 +33,8 @@ static int16_t mfi_axes[MAX_USERS][4];
|
||||
|
||||
static uint32_t mfi_controllers[MAX_MFI_CONTROLLERS];
|
||||
|
||||
static NSMutableArray *mfiControllers;
|
||||
|
||||
enum
|
||||
{
|
||||
GCCONTROLLER_PLAYER_INDEX_UNSET = -1,
|
||||
@ -162,6 +164,28 @@ static void apple_gamecontroller_joypad_connect(GCController *controller)
|
||||
}
|
||||
}
|
||||
|
||||
[mfiControllers addObject:controller];
|
||||
// move any non-game controllers (like the siri remote) to the end
|
||||
if ( mfiControllers.count > 1 ) {
|
||||
NSInteger connectedNonGameControllerIndex = NSNotFound;
|
||||
NSUInteger index = 0;
|
||||
for (GCController *connectedController in mfiControllers) {
|
||||
if ( connectedController.gamepad == nil && connectedController.extendedGamepad == nil ) {
|
||||
connectedNonGameControllerIndex = index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
if ( connectedNonGameControllerIndex != NSNotFound ) {
|
||||
GCController *nonGameController = [mfiControllers objectAtIndex:connectedNonGameControllerIndex];
|
||||
[mfiControllers removeObjectAtIndex:connectedNonGameControllerIndex];
|
||||
[mfiControllers addObject:nonGameController];
|
||||
}
|
||||
int newPlayerIndex = 0;
|
||||
for (GCController *gc in mfiControllers) {
|
||||
gc.playerIndex = newPlayerIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
apple_gamecontroller_joypad_register(controller.gamepad);
|
||||
}
|
||||
}
|
||||
@ -174,6 +198,7 @@ static void apple_gamecontroller_joypad_disconnect(GCController* controller)
|
||||
return;
|
||||
|
||||
mfi_controllers[pad] = 0;
|
||||
[mfiControllers removeObject:controller];
|
||||
}
|
||||
|
||||
bool apple_gamecontroller_joypad_init(void *data)
|
||||
@ -183,7 +208,7 @@ bool apple_gamecontroller_joypad_init(void *data)
|
||||
return true;
|
||||
if (!apple_gamecontroller_available())
|
||||
return false;
|
||||
|
||||
mfiControllers = [[NSMutableArray alloc] initWithCapacity:MAX_MFI_CONTROLLERS];
|
||||
#ifdef __IPHONE_7_0
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidConnectNotification
|
||||
object:nil
|
||||
|
@ -630,6 +630,28 @@ DECL_AXIS(r_x_minus, -2) \
|
||||
DECL_AXIS(r_y_plus, +3) \
|
||||
DECL_AXIS(r_y_minus, -3)
|
||||
|
||||
#define TVOS_MFI_DEFAULT_BINDS \
|
||||
DECL_BTN(a, 8) \
|
||||
DECL_BTN(b, 0) \
|
||||
DECL_BTN(x, 9) \
|
||||
DECL_BTN(y, 1) \
|
||||
DECL_BTN(up, 4) \
|
||||
DECL_BTN(down, 5) \
|
||||
DECL_BTN(left, 6) \
|
||||
DECL_BTN(right, 7) \
|
||||
DECL_BTN(l, 10) \
|
||||
DECL_BTN(r, 11) \
|
||||
DECL_AXIS(l2, 12) \
|
||||
DECL_AXIS(r2, 13) \
|
||||
DECL_AXIS(l_x_plus, +0) \
|
||||
DECL_AXIS(l_x_minus, -0) \
|
||||
DECL_AXIS(l_y_plus, -1) \
|
||||
DECL_AXIS(l_y_minus, +1) \
|
||||
DECL_AXIS(r_x_plus, +2) \
|
||||
DECL_AXIS(r_x_minus, -2) \
|
||||
DECL_AXIS(r_y_plus, -3) \
|
||||
DECL_AXIS(r_y_minus, +3)
|
||||
|
||||
const char* const input_builtin_autoconfs[] =
|
||||
{
|
||||
#if defined(_WIN32) && defined(_XBOX)
|
||||
@ -704,6 +726,9 @@ const char* const input_builtin_autoconfs[] =
|
||||
#endif
|
||||
#ifdef EMSCRIPTEN
|
||||
DECL_AUTOCONF_PID(1, 1, "rwebpad", EMSCRIPTEN_DEFAULT_BINDS),
|
||||
#endif
|
||||
#ifdef TARGET_OS_TV
|
||||
DECL_AUTOCONF_DEVICE("Mfi Controller", "hid", TVOS_MFI_DEFAULT_BINDS),
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
BIN
pkg/apple/Resources/modules/fceumm_libretro_tvos.dylib
Executable file
BIN
pkg/apple/Resources/modules/fceumm_libretro_tvos.dylib
Executable file
Binary file not shown.
@ -26,17 +26,55 @@
|
||||
9204BE201D319EF300BD49DB /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96AFAE3116C1D4EA009DE44C /* OpenGLES.framework */; };
|
||||
9204BE221D319EF300BD49DB /* iOS/Resources/ic_pause.png in Resources */ = {isa = PBXBuildFile; fileRef = 83D632DB19ECFCC4009E3161 /* iOS/Resources/ic_pause.png */; };
|
||||
9204BE231D319EF300BD49DB /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 967894611788EBD800D6CA69 /* InfoPlist.strings */; };
|
||||
9204BE241D319EF300BD49DB /* iOS/Resources/Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69D31DE31A547EC800EF4C92 /* iOS/Resources/Media.xcassets */; };
|
||||
9204BE251D319EF300BD49DB /* iOS/Resources/PauseIndicatorView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83D632DE19ECFCC4009E3161 /* iOS/Resources/PauseIndicatorView.xib */; };
|
||||
9204BE261D319EF300BD49DB /* iOS/modules in Resources */ = {isa = PBXBuildFile; fileRef = 83EB675F19EEAF050096F441 /* iOS/modules */; };
|
||||
926C77E321FD1E6700103EDE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 926C77E221FD1E6700103EDE /* Assets.xcassets */; };
|
||||
926C77EA21FD20C100103EDE /* griffin_objc.m in Sources */ = {isa = PBXBuildFile; fileRef = 50521A431AA23BF500185CC9 /* griffin_objc.m */; };
|
||||
926C77EB21FD20C400103EDE /* griffin.c in Sources */ = {isa = PBXBuildFile; fileRef = 501232C9192E5FC40063A359 /* griffin.c */; };
|
||||
926C77EF21FD263800103EDE /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 926C77EE21FD263800103EDE /* AudioToolbox.framework */; };
|
||||
926C77F121FD26E800103EDE /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 926C77F021FD26E800103EDE /* GameController.framework */; };
|
||||
9297844F2200227700989A60 /* iOS/Resources/Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69D31DE31A547EC800EF4C92 /* iOS/Resources/Media.xcassets */; };
|
||||
92CC05A221FE3C1700FF79F0 /* GCDWebServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC058221FE3C1700FF79F0 /* GCDWebServerResponse.m */; };
|
||||
92CC05A321FE3C1700FF79F0 /* GCDWebServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC058221FE3C1700FF79F0 /* GCDWebServerResponse.m */; };
|
||||
92CC05A421FE3C1700FF79F0 /* GCDWebServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC058521FE3C1700FF79F0 /* GCDWebServerRequest.m */; };
|
||||
92CC05A521FE3C1700FF79F0 /* GCDWebServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC058521FE3C1700FF79F0 /* GCDWebServerRequest.m */; };
|
||||
92CC05A621FE3C1700FF79F0 /* GCDWebServerFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC058821FE3C1700FF79F0 /* GCDWebServerFunctions.m */; };
|
||||
92CC05A721FE3C1700FF79F0 /* GCDWebServerFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC058821FE3C1700FF79F0 /* GCDWebServerFunctions.m */; };
|
||||
92CC05A821FE3C1700FF79F0 /* GCDWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC058921FE3C1700FF79F0 /* GCDWebServer.m */; };
|
||||
92CC05A921FE3C1700FF79F0 /* GCDWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC058921FE3C1700FF79F0 /* GCDWebServer.m */; };
|
||||
92CC05AA21FE3C1700FF79F0 /* GCDWebServerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC058A21FE3C1700FF79F0 /* GCDWebServerConnection.m */; };
|
||||
92CC05AB21FE3C1700FF79F0 /* GCDWebServerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC058A21FE3C1700FF79F0 /* GCDWebServerConnection.m */; };
|
||||
92CC05AC21FE3C1700FF79F0 /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC058F21FE3C1700FF79F0 /* GCDWebServerErrorResponse.m */; };
|
||||
92CC05AD21FE3C1700FF79F0 /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC058F21FE3C1700FF79F0 /* GCDWebServerErrorResponse.m */; };
|
||||
92CC05AE21FE3C1700FF79F0 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059121FE3C1700FF79F0 /* GCDWebServerFileResponse.m */; };
|
||||
92CC05AF21FE3C1700FF79F0 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059121FE3C1700FF79F0 /* GCDWebServerFileResponse.m */; };
|
||||
92CC05B021FE3C1700FF79F0 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059221FE3C1700FF79F0 /* GCDWebServerDataResponse.m */; };
|
||||
92CC05B121FE3C1700FF79F0 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059221FE3C1700FF79F0 /* GCDWebServerDataResponse.m */; };
|
||||
92CC05B221FE3C1700FF79F0 /* GCDWebServerStreamedResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059421FE3C1700FF79F0 /* GCDWebServerStreamedResponse.m */; };
|
||||
92CC05B321FE3C1700FF79F0 /* GCDWebServerStreamedResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059421FE3C1700FF79F0 /* GCDWebServerStreamedResponse.m */; };
|
||||
92CC05B421FE3C1700FF79F0 /* GCDWebServerURLEncodedFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059921FE3C1700FF79F0 /* GCDWebServerURLEncodedFormRequest.m */; };
|
||||
92CC05B521FE3C1700FF79F0 /* GCDWebServerURLEncodedFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059921FE3C1700FF79F0 /* GCDWebServerURLEncodedFormRequest.m */; };
|
||||
92CC05B621FE3C1700FF79F0 /* GCDWebServerMultiPartFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059A21FE3C1700FF79F0 /* GCDWebServerMultiPartFormRequest.m */; };
|
||||
92CC05B721FE3C1700FF79F0 /* GCDWebServerMultiPartFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059A21FE3C1700FF79F0 /* GCDWebServerMultiPartFormRequest.m */; };
|
||||
92CC05B821FE3C1700FF79F0 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059B21FE3C1700FF79F0 /* GCDWebServerDataRequest.m */; };
|
||||
92CC05B921FE3C1700FF79F0 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059B21FE3C1700FF79F0 /* GCDWebServerDataRequest.m */; };
|
||||
92CC05BA21FE3C1700FF79F0 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059C21FE3C1700FF79F0 /* GCDWebServerFileRequest.m */; };
|
||||
92CC05BB21FE3C1700FF79F0 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059C21FE3C1700FF79F0 /* GCDWebServerFileRequest.m */; };
|
||||
92CC05BC21FE3C1700FF79F0 /* GCDWebUploader.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 92CC059F21FE3C1700FF79F0 /* GCDWebUploader.bundle */; };
|
||||
92CC05BD21FE3C1700FF79F0 /* GCDWebUploader.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 92CC059F21FE3C1700FF79F0 /* GCDWebUploader.bundle */; };
|
||||
92CC05BE21FE3C1700FF79F0 /* GCDWebUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC05A121FE3C1700FF79F0 /* GCDWebUploader.m */; };
|
||||
92CC05BF21FE3C1700FF79F0 /* GCDWebUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC05A121FE3C1700FF79F0 /* GCDWebUploader.m */; };
|
||||
92CC05C221FE3C6D00FF79F0 /* WebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC05C121FE3C6D00FF79F0 /* WebServer.m */; };
|
||||
92CC05C321FE3C6D00FF79F0 /* WebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC05C121FE3C6D00FF79F0 /* WebServer.m */; };
|
||||
92CC05C521FEDC9F00FF79F0 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92CC05C421FEDC9F00FF79F0 /* CFNetwork.framework */; };
|
||||
92CC05C721FEDD0B00FF79F0 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92CC05C621FEDD0B00FF79F0 /* MobileCoreServices.framework */; };
|
||||
92CC05CE21FF798F00FF79F0 /* modules in Resources */ = {isa = PBXBuildFile; fileRef = 92CC05CD21FF798F00FF79F0 /* modules */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0FDA2A921BE1AFA800F2B5DA /* RetroArch_iOS9-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "RetroArch_iOS9-Info.plist"; path = "/Users/buildbot/buildbot/ios/retroarch/pkg/apple/RetroArch_iOS9-Info.plist"; sourceTree = "<absolute>"; };
|
||||
501232C9192E5FC40063A359 /* griffin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = griffin.c; path = ../../griffin/griffin.c; sourceTree = SOURCE_ROOT; };
|
||||
501881EB184BAD6D006F665D /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
501881ED184BB54C006F665D /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
|
||||
503700AE1ACA18E400A51A37 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = iOS/Info.plist; sourceTree = SOURCE_ROOT; };
|
||||
5040F04F1AE47ED4006F6972 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
|
||||
50521A431AA23BF500185CC9 /* griffin_objc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = griffin_objc.m; path = ../../griffin/griffin_objc.m; sourceTree = SOURCE_ROOT; };
|
||||
50C3B1AD1AB1107100F478D3 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
||||
@ -48,6 +86,48 @@
|
||||
83D632DE19ECFCC4009E3161 /* iOS/Resources/PauseIndicatorView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = iOS/Resources/PauseIndicatorView.xib; sourceTree = SOURCE_ROOT; };
|
||||
83EB675F19EEAF050096F441 /* iOS/modules */ = {isa = PBXFileReference; lastKnownFileType = folder; path = iOS/modules; sourceTree = SOURCE_ROOT; };
|
||||
9204BE2B1D319EF300BD49DB /* RetroArchiOS11.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RetroArchiOS11.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
926C77D721FD1E6500103EDE /* RetroArchTV.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RetroArchTV.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
926C77E221FD1E6700103EDE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
926C77E421FD1E6700103EDE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
926C77EC21FD261600103EDE /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.1.sdk/System/Library/Frameworks/CoreAudio.framework; sourceTree = DEVELOPER_DIR; };
|
||||
926C77EE21FD263800103EDE /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.1.sdk/System/Library/Frameworks/AudioToolbox.framework; sourceTree = DEVELOPER_DIR; };
|
||||
926C77F021FD26E800103EDE /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.1.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; };
|
||||
92CC058021FE3C1700FF79F0 /* GCDWebServerFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFunctions.h; sourceTree = "<group>"; };
|
||||
92CC058121FE3C1700FF79F0 /* GCDWebServerPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerPrivate.h; sourceTree = "<group>"; };
|
||||
92CC058221FE3C1700FF79F0 /* GCDWebServerResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerResponse.m; sourceTree = "<group>"; };
|
||||
92CC058321FE3C1700FF79F0 /* GCDWebServerConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerConnection.h; sourceTree = "<group>"; };
|
||||
92CC058421FE3C1700FF79F0 /* GCDWebServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServer.h; sourceTree = "<group>"; };
|
||||
92CC058521FE3C1700FF79F0 /* GCDWebServerRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerRequest.m; sourceTree = "<group>"; };
|
||||
92CC058621FE3C1700FF79F0 /* GCDWebServerHTTPStatusCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerHTTPStatusCodes.h; sourceTree = "<group>"; };
|
||||
92CC058721FE3C1700FF79F0 /* GCDWebServerResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerResponse.h; sourceTree = "<group>"; };
|
||||
92CC058821FE3C1700FF79F0 /* GCDWebServerFunctions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFunctions.m; sourceTree = "<group>"; };
|
||||
92CC058921FE3C1700FF79F0 /* GCDWebServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServer.m; sourceTree = "<group>"; };
|
||||
92CC058A21FE3C1700FF79F0 /* GCDWebServerConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerConnection.m; sourceTree = "<group>"; };
|
||||
92CC058B21FE3C1700FF79F0 /* GCDWebServerRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerRequest.h; sourceTree = "<group>"; };
|
||||
92CC058D21FE3C1700FF79F0 /* GCDWebServerFileResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFileResponse.h; sourceTree = "<group>"; };
|
||||
92CC058E21FE3C1700FF79F0 /* GCDWebServerStreamedResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerStreamedResponse.h; sourceTree = "<group>"; };
|
||||
92CC058F21FE3C1700FF79F0 /* GCDWebServerErrorResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerErrorResponse.m; sourceTree = "<group>"; };
|
||||
92CC059021FE3C1700FF79F0 /* GCDWebServerDataResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerDataResponse.h; sourceTree = "<group>"; };
|
||||
92CC059121FE3C1700FF79F0 /* GCDWebServerFileResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFileResponse.m; sourceTree = "<group>"; };
|
||||
92CC059221FE3C1700FF79F0 /* GCDWebServerDataResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerDataResponse.m; sourceTree = "<group>"; };
|
||||
92CC059321FE3C1700FF79F0 /* GCDWebServerErrorResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerErrorResponse.h; sourceTree = "<group>"; };
|
||||
92CC059421FE3C1700FF79F0 /* GCDWebServerStreamedResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerStreamedResponse.m; sourceTree = "<group>"; };
|
||||
92CC059621FE3C1700FF79F0 /* GCDWebServerDataRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerDataRequest.h; sourceTree = "<group>"; };
|
||||
92CC059721FE3C1700FF79F0 /* GCDWebServerMultiPartFormRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerMultiPartFormRequest.h; sourceTree = "<group>"; };
|
||||
92CC059821FE3C1700FF79F0 /* GCDWebServerFileRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFileRequest.h; sourceTree = "<group>"; };
|
||||
92CC059921FE3C1700FF79F0 /* GCDWebServerURLEncodedFormRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerURLEncodedFormRequest.m; sourceTree = "<group>"; };
|
||||
92CC059A21FE3C1700FF79F0 /* GCDWebServerMultiPartFormRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerMultiPartFormRequest.m; sourceTree = "<group>"; };
|
||||
92CC059B21FE3C1700FF79F0 /* GCDWebServerDataRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerDataRequest.m; sourceTree = "<group>"; };
|
||||
92CC059C21FE3C1700FF79F0 /* GCDWebServerFileRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFileRequest.m; sourceTree = "<group>"; };
|
||||
92CC059D21FE3C1700FF79F0 /* GCDWebServerURLEncodedFormRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerURLEncodedFormRequest.h; sourceTree = "<group>"; };
|
||||
92CC059F21FE3C1700FF79F0 /* GCDWebUploader.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = GCDWebUploader.bundle; sourceTree = "<group>"; };
|
||||
92CC05A021FE3C1700FF79F0 /* GCDWebUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebUploader.h; sourceTree = "<group>"; };
|
||||
92CC05A121FE3C1700FF79F0 /* GCDWebUploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebUploader.m; sourceTree = "<group>"; };
|
||||
92CC05C021FE3C6D00FF79F0 /* WebServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebServer.h; sourceTree = "<group>"; };
|
||||
92CC05C121FE3C6D00FF79F0 /* WebServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebServer.m; sourceTree = "<group>"; };
|
||||
92CC05C421FEDC9F00FF79F0 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
|
||||
92CC05C621FEDD0B00FF79F0 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
|
||||
92CC05CD21FF798F00FF79F0 /* modules */ = {isa = PBXFileReference; lastKnownFileType = folder; path = modules; sourceTree = "<group>"; };
|
||||
96366C5416C9AC3300D64A22 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
|
||||
96366C5816C9ACF500D64A22 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
963C3C33186E3DED00A6EB1E /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
|
||||
@ -58,8 +138,6 @@
|
||||
96AFAE2D16C1D4EA009DE44C /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
|
||||
96AFAE2F16C1D4EA009DE44C /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; };
|
||||
96AFAE3116C1D4EA009DE44C /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
|
||||
96AFAF4516C1E00A009DE44C /* bitmap.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = bitmap.bin; sourceTree = "<group>"; };
|
||||
96AFAF4616C1E00A009DE44C /* bitmap.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; path = bitmap.bmp; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -67,6 +145,8 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
92CC05C721FEDD0B00FF79F0 /* MobileCoreServices.framework in Frameworks */,
|
||||
92CC05C521FEDC9F00FF79F0 /* CFNetwork.framework in Frameworks */,
|
||||
9204BE121D319EF300BD49DB /* libz.dylib in Frameworks */,
|
||||
9204BE131D319EF300BD49DB /* QuartzCore.framework in Frameworks */,
|
||||
9204BE141D319EF300BD49DB /* GameController.framework in Frameworks */,
|
||||
@ -85,10 +165,19 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
926C77D421FD1E6500103EDE /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
926C77F121FD26E800103EDE /* GameController.framework in Frameworks */,
|
||||
926C77EF21FD263800103EDE /* AudioToolbox.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
83D632D719ECFCC4009E3161 /* Assets */ = {
|
||||
83D632D719ECFCC4009E3161 /* iOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83EB675F19EEAF050096F441 /* iOS/modules */,
|
||||
@ -96,20 +185,110 @@
|
||||
83D632DE19ECFCC4009E3161 /* iOS/Resources/PauseIndicatorView.xib */,
|
||||
69D31DE31A547EC800EF4C92 /* iOS/Resources/Media.xcassets */,
|
||||
);
|
||||
name = Assets;
|
||||
name = iOS;
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
926C77D821FD1E6500103EDE /* tvOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92CC05CD21FF798F00FF79F0 /* modules */,
|
||||
926C77E221FD1E6700103EDE /* Assets.xcassets */,
|
||||
926C77E421FD1E6700103EDE /* Info.plist */,
|
||||
);
|
||||
path = tvOS;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92CC057E21FE3C1700FF79F0 /* GCDWebServer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92CC057F21FE3C1700FF79F0 /* Core */,
|
||||
92CC058C21FE3C1700FF79F0 /* Responses */,
|
||||
92CC059521FE3C1700FF79F0 /* Requests */,
|
||||
);
|
||||
path = GCDWebServer;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92CC057F21FE3C1700FF79F0 /* Core */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92CC058021FE3C1700FF79F0 /* GCDWebServerFunctions.h */,
|
||||
92CC058121FE3C1700FF79F0 /* GCDWebServerPrivate.h */,
|
||||
92CC058221FE3C1700FF79F0 /* GCDWebServerResponse.m */,
|
||||
92CC058321FE3C1700FF79F0 /* GCDWebServerConnection.h */,
|
||||
92CC058421FE3C1700FF79F0 /* GCDWebServer.h */,
|
||||
92CC058521FE3C1700FF79F0 /* GCDWebServerRequest.m */,
|
||||
92CC058621FE3C1700FF79F0 /* GCDWebServerHTTPStatusCodes.h */,
|
||||
92CC058721FE3C1700FF79F0 /* GCDWebServerResponse.h */,
|
||||
92CC058821FE3C1700FF79F0 /* GCDWebServerFunctions.m */,
|
||||
92CC058921FE3C1700FF79F0 /* GCDWebServer.m */,
|
||||
92CC058A21FE3C1700FF79F0 /* GCDWebServerConnection.m */,
|
||||
92CC058B21FE3C1700FF79F0 /* GCDWebServerRequest.h */,
|
||||
);
|
||||
path = Core;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92CC058C21FE3C1700FF79F0 /* Responses */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92CC058D21FE3C1700FF79F0 /* GCDWebServerFileResponse.h */,
|
||||
92CC058E21FE3C1700FF79F0 /* GCDWebServerStreamedResponse.h */,
|
||||
92CC058F21FE3C1700FF79F0 /* GCDWebServerErrorResponse.m */,
|
||||
92CC059021FE3C1700FF79F0 /* GCDWebServerDataResponse.h */,
|
||||
92CC059121FE3C1700FF79F0 /* GCDWebServerFileResponse.m */,
|
||||
92CC059221FE3C1700FF79F0 /* GCDWebServerDataResponse.m */,
|
||||
92CC059321FE3C1700FF79F0 /* GCDWebServerErrorResponse.h */,
|
||||
92CC059421FE3C1700FF79F0 /* GCDWebServerStreamedResponse.m */,
|
||||
);
|
||||
path = Responses;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92CC059521FE3C1700FF79F0 /* Requests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92CC059621FE3C1700FF79F0 /* GCDWebServerDataRequest.h */,
|
||||
92CC059721FE3C1700FF79F0 /* GCDWebServerMultiPartFormRequest.h */,
|
||||
92CC059821FE3C1700FF79F0 /* GCDWebServerFileRequest.h */,
|
||||
92CC059921FE3C1700FF79F0 /* GCDWebServerURLEncodedFormRequest.m */,
|
||||
92CC059A21FE3C1700FF79F0 /* GCDWebServerMultiPartFormRequest.m */,
|
||||
92CC059B21FE3C1700FF79F0 /* GCDWebServerDataRequest.m */,
|
||||
92CC059C21FE3C1700FF79F0 /* GCDWebServerFileRequest.m */,
|
||||
92CC059D21FE3C1700FF79F0 /* GCDWebServerURLEncodedFormRequest.h */,
|
||||
);
|
||||
path = Requests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92CC059E21FE3C1700FF79F0 /* GCDWebUploader */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92CC059F21FE3C1700FF79F0 /* GCDWebUploader.bundle */,
|
||||
92CC05A021FE3C1700FF79F0 /* GCDWebUploader.h */,
|
||||
92CC05A121FE3C1700FF79F0 /* GCDWebUploader.m */,
|
||||
);
|
||||
path = GCDWebUploader;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92CC05CC21FF782C00FF79F0 /* WebServer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92CC05C021FE3C6D00FF79F0 /* WebServer.h */,
|
||||
92CC05C121FE3C6D00FF79F0 /* WebServer.m */,
|
||||
92CC057E21FE3C1700FF79F0 /* GCDWebServer */,
|
||||
92CC059E21FE3C1700FF79F0 /* GCDWebUploader */,
|
||||
);
|
||||
path = WebServer;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
96AFAE1A16C1D4EA009DE44C = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83D632D719ECFCC4009E3161 /* Assets */,
|
||||
96AFAE9C16C1D976009DE44C /* core */,
|
||||
83D632D719ECFCC4009E3161 /* iOS */,
|
||||
926C77D821FD1E6500103EDE /* tvOS */,
|
||||
92CC05CC21FF782C00FF79F0 /* WebServer */,
|
||||
96AFAE2816C1D4EA009DE44C /* Frameworks */,
|
||||
96AFAE2616C1D4EA009DE44C /* Products */,
|
||||
96AFAE3416C1D4EA009DE44C /* Supporting Files */,
|
||||
503700AE1ACA18E400A51A37 /* Info.plist */,
|
||||
0FDA2A921BE1AFA800F2B5DA /* RetroArch_iOS9-Info.plist */,
|
||||
);
|
||||
indentWidth = 3;
|
||||
sourceTree = "<group>";
|
||||
@ -119,6 +298,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9204BE2B1D319EF300BD49DB /* RetroArchiOS11.app */,
|
||||
926C77D721FD1E6500103EDE /* RetroArchTV.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@ -126,6 +306,11 @@
|
||||
96AFAE2816C1D4EA009DE44C /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92CC05C621FEDD0B00FF79F0 /* MobileCoreServices.framework */,
|
||||
92CC05C421FEDC9F00FF79F0 /* CFNetwork.framework */,
|
||||
926C77F021FD26E800103EDE /* GameController.framework */,
|
||||
926C77EE21FD263800103EDE /* AudioToolbox.framework */,
|
||||
926C77EC21FD261600103EDE /* CoreAudio.framework */,
|
||||
5040F04F1AE47ED4006F6972 /* libz.dylib */,
|
||||
50C3B1AD1AB1107100F478D3 /* QuartzCore.framework */,
|
||||
696012F119F3389A006A1088 /* CoreText.framework */,
|
||||
@ -152,36 +337,16 @@
|
||||
9678945F1788EBD000D6CA69 /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
path = RetroArch;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
96AFAE9C16C1D976009DE44C /* core */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D48581DC16F823E2004BEB17 /* griffin */,
|
||||
96AFAF3116C1E00A009DE44C /* gfx */,
|
||||
);
|
||||
name = core;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
96AFAF3116C1E00A009DE44C /* gfx */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
96AFAF4416C1E00A009DE44C /* fonts */,
|
||||
);
|
||||
name = gfx;
|
||||
path = ../gfx;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
96AFAF4416C1E00A009DE44C /* fonts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
96AFAF4516C1E00A009DE44C /* bitmap.bin */,
|
||||
96AFAF4616C1E00A009DE44C /* bitmap.bmp */,
|
||||
);
|
||||
path = fonts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D48581DC16F823E2004BEB17 /* griffin */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -212,6 +377,24 @@
|
||||
productReference = 9204BE2B1D319EF300BD49DB /* RetroArchiOS11.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
926C77D621FD1E6500103EDE /* RetroArchTV */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 926C77E921FD1E6700103EDE /* Build configuration list for PBXNativeTarget "RetroArchTV" */;
|
||||
buildPhases = (
|
||||
926C77D321FD1E6500103EDE /* Sources */,
|
||||
926C77D421FD1E6500103EDE /* Frameworks */,
|
||||
92CC057521FE2D4900FF79F0 /* ShellScript */,
|
||||
926C77D521FD1E6500103EDE /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = RetroArchTV;
|
||||
productName = RetroArchTV;
|
||||
productReference = 926C77D721FD1E6500103EDE /* RetroArchTV.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@ -222,8 +405,13 @@
|
||||
ORGANIZATIONNAME = RetroArch;
|
||||
TargetAttributes = {
|
||||
9204BE091D319EF300BD49DB = {
|
||||
DevelopmentTeam = UK699V5ZS8;
|
||||
DevelopmentTeamName = "RetroArch";
|
||||
DevelopmentTeam = R72X3BF4KE;
|
||||
DevelopmentTeamName = RetroArch;
|
||||
};
|
||||
926C77D621FD1E6500103EDE = {
|
||||
CreatedOnToolsVersion = 10.1;
|
||||
DevelopmentTeam = R72X3BF4KE;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -233,6 +421,7 @@
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 96AFAE1A16C1D4EA009DE44C;
|
||||
productRefGroup = 96AFAE2616C1D4EA009DE44C /* Products */;
|
||||
@ -240,6 +429,7 @@
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
9204BE091D319EF300BD49DB /* RetroArchiOS11 */,
|
||||
926C77D621FD1E6500103EDE /* RetroArchTV */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@ -251,12 +441,23 @@
|
||||
files = (
|
||||
9204BE221D319EF300BD49DB /* iOS/Resources/ic_pause.png in Resources */,
|
||||
9204BE231D319EF300BD49DB /* InfoPlist.strings in Resources */,
|
||||
9204BE241D319EF300BD49DB /* iOS/Resources/Media.xcassets in Resources */,
|
||||
92CC05BC21FE3C1700FF79F0 /* GCDWebUploader.bundle in Resources */,
|
||||
9204BE251D319EF300BD49DB /* iOS/Resources/PauseIndicatorView.xib in Resources */,
|
||||
9204BE261D319EF300BD49DB /* iOS/modules in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
926C77D521FD1E6500103EDE /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9297844F2200227700989A60 /* iOS/Resources/Media.xcassets in Resources */,
|
||||
92CC05CE21FF798F00FF79F0 /* modules in Resources */,
|
||||
92CC05BD21FE3C1700FF79F0 /* GCDWebUploader.bundle in Resources */,
|
||||
926C77E321FD1E6700103EDE /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
@ -271,7 +472,24 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "./code-sign-cores.sh";
|
||||
shellScript = "./code-sign-cores.sh iOS\n";
|
||||
};
|
||||
92CC057521FE2D4900FF79F0 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "./code-sign-cores.sh tvOS\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
@ -280,8 +498,47 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
92CC05B821FE3C1700FF79F0 /* GCDWebServerDataRequest.m in Sources */,
|
||||
92CC05A421FE3C1700FF79F0 /* GCDWebServerRequest.m in Sources */,
|
||||
92CC05AA21FE3C1700FF79F0 /* GCDWebServerConnection.m in Sources */,
|
||||
92CC05BE21FE3C1700FF79F0 /* GCDWebUploader.m in Sources */,
|
||||
92CC05B621FE3C1700FF79F0 /* GCDWebServerMultiPartFormRequest.m in Sources */,
|
||||
92CC05AC21FE3C1700FF79F0 /* GCDWebServerErrorResponse.m in Sources */,
|
||||
92CC05A621FE3C1700FF79F0 /* GCDWebServerFunctions.m in Sources */,
|
||||
92CC05A821FE3C1700FF79F0 /* GCDWebServer.m in Sources */,
|
||||
9204BE0D1D319EF300BD49DB /* griffin_objc.m in Sources */,
|
||||
9204BE101D319EF300BD49DB /* griffin.c in Sources */,
|
||||
92CC05B421FE3C1700FF79F0 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
|
||||
92CC05B021FE3C1700FF79F0 /* GCDWebServerDataResponse.m in Sources */,
|
||||
92CC05C221FE3C6D00FF79F0 /* WebServer.m in Sources */,
|
||||
92CC05BA21FE3C1700FF79F0 /* GCDWebServerFileRequest.m in Sources */,
|
||||
92CC05AE21FE3C1700FF79F0 /* GCDWebServerFileResponse.m in Sources */,
|
||||
92CC05B221FE3C1700FF79F0 /* GCDWebServerStreamedResponse.m in Sources */,
|
||||
92CC05A221FE3C1700FF79F0 /* GCDWebServerResponse.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
926C77D321FD1E6500103EDE /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
92CC05B921FE3C1700FF79F0 /* GCDWebServerDataRequest.m in Sources */,
|
||||
92CC05A521FE3C1700FF79F0 /* GCDWebServerRequest.m in Sources */,
|
||||
92CC05AB21FE3C1700FF79F0 /* GCDWebServerConnection.m in Sources */,
|
||||
92CC05BF21FE3C1700FF79F0 /* GCDWebUploader.m in Sources */,
|
||||
92CC05B721FE3C1700FF79F0 /* GCDWebServerMultiPartFormRequest.m in Sources */,
|
||||
92CC05AD21FE3C1700FF79F0 /* GCDWebServerErrorResponse.m in Sources */,
|
||||
92CC05A721FE3C1700FF79F0 /* GCDWebServerFunctions.m in Sources */,
|
||||
92CC05A921FE3C1700FF79F0 /* GCDWebServer.m in Sources */,
|
||||
926C77EA21FD20C100103EDE /* griffin_objc.m in Sources */,
|
||||
926C77EB21FD20C400103EDE /* griffin.c in Sources */,
|
||||
92CC05B521FE3C1700FF79F0 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
|
||||
92CC05B121FE3C1700FF79F0 /* GCDWebServerDataResponse.m in Sources */,
|
||||
92CC05C321FE3C6D00FF79F0 /* WebServer.m in Sources */,
|
||||
92CC05BB21FE3C1700FF79F0 /* GCDWebServerFileRequest.m in Sources */,
|
||||
92CC05AF21FE3C1700FF79F0 /* GCDWebServerFileResponse.m in Sources */,
|
||||
92CC05B321FE3C1700FF79F0 /* GCDWebServerStreamedResponse.m in Sources */,
|
||||
92CC05A321FE3C1700FF79F0 /* GCDWebServerResponse.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -309,7 +566,7 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_RESOURCE_RULES_PATH = "$(SDKROOT)/ResourceRules.plist";
|
||||
DEVELOPMENT_TEAM = UK699V5ZS8;
|
||||
DEVELOPMENT_TEAM = R72X3BF4KE;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = NO;
|
||||
GCC_PREFIX_HEADER = "";
|
||||
@ -334,7 +591,7 @@
|
||||
"-DHAVE_NETWORKING",
|
||||
"-DHAVE_NETPLAYDISCOVERY",
|
||||
"-DHAVE_AVFOUNDATION",
|
||||
"-DHAVE_RUNAHEAD",
|
||||
"-DHAVE_RUNAHEAD",
|
||||
"-DHAVE_GRIFFIN",
|
||||
"-DHAVE_STB_VORBIS",
|
||||
"-DHAVE_MINIUPNPC",
|
||||
@ -380,7 +637,7 @@
|
||||
"-DHAVE_AVFOUNDATION",
|
||||
"-DHAVE_KEYMAPPER",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.libretro.RetroArchiOS11;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.libretro.RetroArchiOS11T;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE = "";
|
||||
VALID_ARCHS = "armv7 arm64";
|
||||
@ -399,7 +656,7 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_RESOURCE_RULES_PATH = "$(SDKROOT)/ResourceRules.plist";
|
||||
DEVELOPMENT_TEAM = UK699V5ZS8;
|
||||
DEVELOPMENT_TEAM = R72X3BF4KE;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = NO;
|
||||
GCC_PREFIX_HEADER = "";
|
||||
@ -414,63 +671,6 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_NO_PIE = YES;
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
OTHER_CFLAGS = (
|
||||
"-DNS_BLOCK_ASSERTIONS=1",
|
||||
"-DNDEBUG",
|
||||
"-DDONT_WANT_ARM_OPTIMIZATIONS",
|
||||
"-DHAVE_NETWORKGAMEPAD",
|
||||
"-DHAVE_CORETEXT",
|
||||
"-DHAVE_HID",
|
||||
"-DHAVE_NETWORKING",
|
||||
"-DHAVE_NETPLAYDISCOVERY",
|
||||
"-DHAVE_AVFOUNDATION",
|
||||
"-DHAVE_RUNAHEAD",
|
||||
"-DHAVE_GRIFFIN",
|
||||
"-DHAVE_STB_VORBIS",
|
||||
"-DHAVE_MINIUPNPC",
|
||||
"-DHAVE_BUILTINMINIUPNPC",
|
||||
"-DHAVE_UPDATE_ASSETS",
|
||||
"-DHAVE_LANGEXTRA",
|
||||
"-DHAVE_CHEEVOS",
|
||||
"-DRC_DISABLE_LUA",
|
||||
"-DHAVE_IMAGEVIEWER",
|
||||
"-DHAVE_CORELOCATION",
|
||||
"-DHAVE_RGUI",
|
||||
"-DHAVE_MENU",
|
||||
"-DHAVE_LIBRETRODB",
|
||||
"-DIOS",
|
||||
"-DHAVE_OPENGL",
|
||||
"-DHAVE_OPENGLES",
|
||||
"-DHAVE_OPENGLES2",
|
||||
"-DHAVE_CC_RESAMPLER",
|
||||
"-DHAVE_GLSL",
|
||||
"-DINLINE=inline",
|
||||
"-D__LIBRETRO__",
|
||||
"-DRARCH_MOBILE",
|
||||
"-DHAVE_COREAUDIO",
|
||||
"-DHAVE_DYNAMIC",
|
||||
"-DHAVE_OVERLAY",
|
||||
"-DHAVE_ZLIB",
|
||||
"-DHAVE_RPNG",
|
||||
"-DHAVE_RJPEG",
|
||||
"-DHAVE_RBMP",
|
||||
"-DHAVE_RTGA",
|
||||
"-DHAVE_COCOATOUCH",
|
||||
"-DHAVE_MAIN",
|
||||
"-DRARCH_INTERNAL",
|
||||
"-DHAVE_THREADS",
|
||||
"-DHAVE_FILTERS_BUILTIN",
|
||||
"-DHAVE_7ZIP",
|
||||
"-DHAVE_MATERIALUI",
|
||||
"-DHAVE_XMB",
|
||||
"-DHAVE_OZONE",
|
||||
"-DHAVE_SHADERPIPELINE",
|
||||
"-D_LZMA_UINT32_IS_ULONG",
|
||||
"-DHAVE_MFI",
|
||||
"-DHAVE_BTSTACK",
|
||||
"-DHAVE_AVFOUNDATION",
|
||||
"-DHAVE_KEYMAPPER",
|
||||
);
|
||||
"OTHER_CFLAGS[arch=*]" = (
|
||||
"-DNS_BLOCK_ASSERTIONS=1",
|
||||
"-DNDEBUG",
|
||||
@ -481,7 +681,7 @@
|
||||
"-DHAVE_NETWORKING",
|
||||
"-DHAVE_NETPLAYDISCOVERY",
|
||||
"-DHAVE_AVFOUNDATION",
|
||||
"-DHAVE_RUNAHEAD",
|
||||
"-DHAVE_RUNAHEAD",
|
||||
"-DHAVE_GRIFFIN",
|
||||
"-DHAVE_STB_VORBIS",
|
||||
"-DHAVE_MINIUPNPC",
|
||||
@ -528,7 +728,7 @@
|
||||
"-DHAVE_AVFOUNDATION",
|
||||
"-DHAVE_KEYMAPPER",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.libretro.RetroArchiOS11;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.libretro.RetroArchiOS11T;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE = "";
|
||||
VALID_ARCHS = "armv7 arm64";
|
||||
@ -537,6 +737,235 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
926C77E721FD1E6700103EDE /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../../,
|
||||
"../../libretro-common/include",
|
||||
../../deps/stb,
|
||||
../../deps/rcheevos/include,
|
||||
../../deps/libz,
|
||||
../../deps,
|
||||
);
|
||||
INFOPLIST_FILE = "$(SRCROOT)/tvOS/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = (
|
||||
"-DDONT_WANT_ARM_OPTIMIZATIONS",
|
||||
"-DHAVE_NETWORKGAMEPAD",
|
||||
"-DHAVE_CORETEXT",
|
||||
"-DHAVE_HID",
|
||||
"-DHAVE_NETWORKING",
|
||||
"-DHAVE_NETPLAYDISCOVERY",
|
||||
"-DHAVE_RUNAHEAD",
|
||||
"-DHAVE_GRIFFIN",
|
||||
"-DHAVE_STB_VORBIS",
|
||||
"-DHAVE_MINIUPNPC",
|
||||
"-DHAVE_BUILTINMINIUPNPC",
|
||||
"-DHAVE_UPDATE_ASSETS",
|
||||
"-DHAVE_LANGEXTRA",
|
||||
"-DHAVE_CHEEVOS",
|
||||
"-DRC_DISABLE_LUA",
|
||||
"-DHAVE_IMAGEVIEWER",
|
||||
"-DHAVE_RGUI",
|
||||
"-DHAVE_MENU",
|
||||
"-DHAVE_LIBRETRODB",
|
||||
"-DIOS",
|
||||
"-DHAVE_OPENGL",
|
||||
"-DHAVE_OPENGLES",
|
||||
"-DHAVE_OPENGLES2",
|
||||
"-DHAVE_CC_RESAMPLER",
|
||||
"-DHAVE_GLSL",
|
||||
"-DINLINE=inline",
|
||||
"-D__LIBRETRO__",
|
||||
"-DRARCH_MOBILE",
|
||||
"-DHAVE_COREAUDIO",
|
||||
"-DHAVE_DYNAMIC",
|
||||
"-DHAVE_OVERLAY",
|
||||
"-DHAVE_ZLIB",
|
||||
"-DHAVE_RPNG",
|
||||
"-DHAVE_RJPEG",
|
||||
"-DHAVE_RBMP",
|
||||
"-DHAVE_RTGA",
|
||||
"-DHAVE_COCOATOUCH",
|
||||
"-DHAVE_MAIN",
|
||||
"-DRARCH_INTERNAL",
|
||||
"-DHAVE_THREADS",
|
||||
"-DHAVE_FILTERS_BUILTIN",
|
||||
"-DHAVE_XMB",
|
||||
"-DHAVE_OZONE",
|
||||
"-DHAVE_SHADERPIPELINE",
|
||||
"-D_LZMA_UINT32_IS_ULONG",
|
||||
"-DHAVE_MFI",
|
||||
"-DHAVE_BTSTACK",
|
||||
"-DHAVE_KEYMAPPER",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.libretro.RetroArchTV;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = appletvos;
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 12.1;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
926C77E821FD1E6700103EDE /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../../,
|
||||
"../../libretro-common/include",
|
||||
../../deps/stb,
|
||||
../../deps/libz,
|
||||
../../deps,
|
||||
);
|
||||
INFOPLIST_FILE = "$(SRCROOT)/tvOS/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_CFLAGS = (
|
||||
"-DNS_BLOCK_ASSERTIONS=1",
|
||||
"-DNDEBUG",
|
||||
"-DDONT_WANT_ARM_OPTIMIZATIONS",
|
||||
"-DHAVE_NETWORKGAMEPAD",
|
||||
"-DHAVE_CORETEXT",
|
||||
"-DHAVE_HID",
|
||||
"-DHAVE_NETWORKING",
|
||||
"-DHAVE_NETPLAYDISCOVERY",
|
||||
"-DHAVE_RUNAHEAD",
|
||||
"-DHAVE_GRIFFIN",
|
||||
"-DHAVE_STB_VORBIS",
|
||||
"-DHAVE_MINIUPNPC",
|
||||
"-DHAVE_BUILTINMINIUPNPC",
|
||||
"-DHAVE_UPDATE_ASSETS",
|
||||
"-DHAVE_LANGEXTRA",
|
||||
"-DHAVE_CHEEVOS",
|
||||
"-DRC_DISABLE_LUA",
|
||||
"-DHAVE_IMAGEVIEWER",
|
||||
"-DHAVE_RGUI",
|
||||
"-DHAVE_MENU",
|
||||
"-DHAVE_LIBRETRODB",
|
||||
"-DIOS",
|
||||
"-DHAVE_OPENGL",
|
||||
"-DHAVE_OPENGLES",
|
||||
"-DHAVE_OPENGLES2",
|
||||
"-DHAVE_CC_RESAMPLER",
|
||||
"-DHAVE_GLSL",
|
||||
"-DINLINE=inline",
|
||||
"-D__LIBRETRO__",
|
||||
"-DRARCH_MOBILE",
|
||||
"-DHAVE_COREAUDIO",
|
||||
"-DHAVE_DYNAMIC",
|
||||
"-DHAVE_OVERLAY",
|
||||
"-DHAVE_ZLIB",
|
||||
"-DHAVE_RPNG",
|
||||
"-DHAVE_RJPEG",
|
||||
"-DHAVE_RBMP",
|
||||
"-DHAVE_RTGA",
|
||||
"-DHAVE_COCOATOUCH",
|
||||
"-DHAVE_MAIN",
|
||||
"-DRARCH_INTERNAL",
|
||||
"-DHAVE_THREADS",
|
||||
"-DHAVE_FILTERS_BUILTIN",
|
||||
"-DHAVE_7ZIP",
|
||||
"-DHAVE_XMB",
|
||||
"-DHAVE_OZONE",
|
||||
"-DHAVE_SHADERPIPELINE",
|
||||
"-D_LZMA_UINT32_IS_ULONG",
|
||||
"-DHAVE_MFI",
|
||||
"-DHAVE_BTSTACK",
|
||||
"-DHAVE_KEYMAPPER",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.libretro.RetroArchTV;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = appletvos;
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 12.1;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
96AFAE5216C1D4EA009DE44C /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -572,7 +1001,7 @@
|
||||
"-DHAVE_NETWORKING",
|
||||
"-DHAVE_NETPLAYDISCOVERY",
|
||||
"-DHAVE_AVFOUNDATION",
|
||||
"-DHAVE_RUNAHEAD",
|
||||
"-DHAVE_RUNAHEAD",
|
||||
"-DHAVE_GRIFFIN",
|
||||
"-DHAVE_STB_VORBIS",
|
||||
"-DHAVE_MINIUPNPC",
|
||||
@ -645,7 +1074,7 @@
|
||||
"-DHAVE_NETWORKING",
|
||||
"-DHAVE_NETPLAYDISCOVERY",
|
||||
"-DHAVE_AVFOUNDATION",
|
||||
"-DHAVE_RUNAHEAD",
|
||||
"-DHAVE_RUNAHEAD",
|
||||
"-DHAVE_GRIFFIN",
|
||||
"-DHAVE_STB_VORBIS",
|
||||
"-DHAVE_MINIUPNPC",
|
||||
@ -701,6 +1130,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
926C77E921FD1E6700103EDE /* Build configuration list for PBXNativeTarget "RetroArchTV" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
926C77E721FD1E6700103EDE /* Debug */,
|
||||
926C77E821FD1E6700103EDE /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
96AFAE1F16C1D4EA009DE44C /* Build configuration list for PBXProject "RetroArch_iOS11" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1010"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9204BE091D319EF300BD49DB"
|
||||
BuildableName = "RetroArchiOS11.app"
|
||||
BlueprintName = "RetroArchiOS11"
|
||||
ReferencedContainer = "container:RetroArch_iOS11.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9204BE091D319EF300BD49DB"
|
||||
BuildableName = "RetroArchiOS11.app"
|
||||
BlueprintName = "RetroArchiOS11"
|
||||
ReferencedContainer = "container:RetroArch_iOS11.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9204BE091D319EF300BD49DB"
|
||||
BuildableName = "RetroArchiOS11.app"
|
||||
BlueprintName = "RetroArchiOS11"
|
||||
ReferencedContainer = "container:RetroArch_iOS11.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9204BE091D319EF300BD49DB"
|
||||
BuildableName = "RetroArchiOS11.app"
|
||||
BlueprintName = "RetroArchiOS11"
|
||||
ReferencedContainer = "container:RetroArch_iOS11.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1010"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "926C77D621FD1E6500103EDE"
|
||||
BuildableName = "RetroArchTV.app"
|
||||
BlueprintName = "RetroArchTV"
|
||||
ReferencedContainer = "container:RetroArch_iOS11.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "926C77D621FD1E6500103EDE"
|
||||
BuildableName = "RetroArchTV.app"
|
||||
BlueprintName = "RetroArchTV"
|
||||
ReferencedContainer = "container:RetroArch_iOS11.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "926C77D621FD1E6500103EDE"
|
||||
BuildableName = "RetroArchTV.app"
|
||||
BlueprintName = "RetroArchTV"
|
||||
ReferencedContainer = "container:RetroArch_iOS11.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "926C77D621FD1E6500103EDE"
|
||||
BuildableName = "RetroArchTV.app"
|
||||
BlueprintName = "RetroArchTV"
|
||||
ReferencedContainer = "container:RetroArch_iOS11.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
622
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServer.h
Normal file
622
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServer.h
Normal file
@ -0,0 +1,622 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
|
||||
#import "GCDWebServerRequest.h"
|
||||
#import "GCDWebServerResponse.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerMatchBlock is called for every handler added to the
|
||||
* GCDWebServer whenever a new HTTP request has started (i.e. HTTP headers have
|
||||
* been received). The block is passed the basic info for the request (HTTP method,
|
||||
* URL, headers...) and must decide if it wants to handle it or not.
|
||||
*
|
||||
* If the handler can handle the request, the block must return a new
|
||||
* GCDWebServerRequest instance created with the same basic info.
|
||||
* Otherwise, it simply returns nil.
|
||||
*/
|
||||
typedef GCDWebServerRequest* _Nullable (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary<NSString*, NSString*>* requestHeaders, NSString* urlPath, NSDictionary<NSString*, NSString*>* urlQuery);
|
||||
|
||||
/**
|
||||
* The GCDWebServerProcessBlock is called after the HTTP request has been fully
|
||||
* received (i.e. the entire HTTP body has been read). The block is passed the
|
||||
* GCDWebServerRequest created at the previous step by the GCDWebServerMatchBlock.
|
||||
*
|
||||
* The block must return a GCDWebServerResponse or nil on error, which will
|
||||
* result in a 500 HTTP status code returned to the client. It's however
|
||||
* recommended to return a GCDWebServerErrorResponse on error so more useful
|
||||
* information can be returned to the client.
|
||||
*/
|
||||
typedef GCDWebServerResponse* _Nullable (^GCDWebServerProcessBlock)(__kindof GCDWebServerRequest* request);
|
||||
|
||||
/**
|
||||
* The GCDWebServerAsynchronousProcessBlock works like the GCDWebServerProcessBlock
|
||||
* except the GCDWebServerResponse can be returned to the server at a later time
|
||||
* allowing for asynchronous generation of the response.
|
||||
*
|
||||
* The block must eventually call "completionBlock" passing a GCDWebServerResponse
|
||||
* or nil on error, which will result in a 500 HTTP status code returned to the client.
|
||||
* It's however recommended to return a GCDWebServerErrorResponse on error so more
|
||||
* useful information can be returned to the client.
|
||||
*/
|
||||
typedef void (^GCDWebServerCompletionBlock)(GCDWebServerResponse* _Nullable response);
|
||||
typedef void (^GCDWebServerAsyncProcessBlock)(__kindof GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock);
|
||||
|
||||
/**
|
||||
* The port used by the GCDWebServer (NSNumber / NSUInteger).
|
||||
*
|
||||
* The default value is 0 i.e. let the OS pick a random port.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_Port;
|
||||
|
||||
/**
|
||||
* The Bonjour name used by the GCDWebServer (NSString). If set to an empty string,
|
||||
* the name will automatically take the value of the GCDWebServerOption_ServerName
|
||||
* option. If this option is set to nil, Bonjour will be disabled.
|
||||
*
|
||||
* The default value is nil.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_BonjourName;
|
||||
|
||||
/**
|
||||
* The Bonjour service type used by the GCDWebServer (NSString).
|
||||
*
|
||||
* The default value is "_http._tcp", the service type for HTTP web servers.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_BonjourType;
|
||||
|
||||
/**
|
||||
* Request a port mapping in the NAT gateway (NSNumber / BOOL).
|
||||
*
|
||||
* This uses the DNSService API under the hood which supports IPv4 mappings only.
|
||||
*
|
||||
* The default value is NO.
|
||||
*
|
||||
* @warning The external port set up by the NAT gateway may be different than
|
||||
* the one used by the GCDWebServer.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_RequestNATPortMapping;
|
||||
|
||||
/**
|
||||
* Only accept HTTP requests coming from localhost i.e. not from the outside
|
||||
* network (NSNumber / BOOL).
|
||||
*
|
||||
* The default value is NO.
|
||||
*
|
||||
* @warning Bonjour and NAT port mapping should be disabled if using this option
|
||||
* since the server will not be reachable from the outside network anyway.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_BindToLocalhost;
|
||||
|
||||
/**
|
||||
* The maximum number of incoming HTTP requests that can be queued waiting to
|
||||
* be handled before new ones are dropped (NSNumber / NSUInteger).
|
||||
*
|
||||
* The default value is 16.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_MaxPendingConnections;
|
||||
|
||||
/**
|
||||
* The value for "Server" HTTP header used by the GCDWebServer (NSString).
|
||||
*
|
||||
* The default value is the GCDWebServer class name.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_ServerName;
|
||||
|
||||
/**
|
||||
* The authentication method used by the GCDWebServer
|
||||
* (one of "GCDWebServerAuthenticationMethod_...").
|
||||
*
|
||||
* The default value is nil i.e. authentication is disabled.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_AuthenticationMethod;
|
||||
|
||||
/**
|
||||
* The authentication realm used by the GCDWebServer (NSString).
|
||||
*
|
||||
* The default value is the same as the GCDWebServerOption_ServerName option.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_AuthenticationRealm;
|
||||
|
||||
/**
|
||||
* The authentication accounts used by the GCDWebServer
|
||||
* (NSDictionary of username / password pairs).
|
||||
*
|
||||
* The default value is nil i.e. no accounts.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_AuthenticationAccounts;
|
||||
|
||||
/**
|
||||
* The class used by the GCDWebServer when instantiating GCDWebServerConnection
|
||||
* (subclass of GCDWebServerConnection).
|
||||
*
|
||||
* The default value is the GCDWebServerConnection class.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_ConnectionClass;
|
||||
|
||||
/**
|
||||
* Allow the GCDWebServer to pretend "HEAD" requests are actually "GET" ones
|
||||
* and automatically discard the HTTP body of the response (NSNumber / BOOL).
|
||||
*
|
||||
* The default value is YES.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET;
|
||||
|
||||
/**
|
||||
* The interval expressed in seconds used by the GCDWebServer to decide how to
|
||||
* coalesce calls to -webServerDidConnect: and -webServerDidDisconnect:
|
||||
* (NSNumber / double). Coalescing will be disabled if the interval is <= 0.0.
|
||||
*
|
||||
* The default value is 1.0 second.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval;
|
||||
|
||||
/**
|
||||
* Set the dispatch queue priority on which server connection will be
|
||||
* run (NSNumber / long).
|
||||
*
|
||||
*
|
||||
* The default value is DISPATCH_QUEUE_PRIORITY_DEFAULT.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_DispatchQueuePriority;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
/**
|
||||
* Enables the GCDWebServer to automatically suspend itself (as if -stop was
|
||||
* called) when the iOS app goes into the background and the last
|
||||
* GCDWebServerConnection is closed, then resume itself (as if -start was called)
|
||||
* when the iOS app comes back to the foreground (NSNumber / BOOL).
|
||||
*
|
||||
* See the README.md file for more information about this option.
|
||||
*
|
||||
* The default value is YES.
|
||||
*
|
||||
* @warning The running property will be NO while the GCDWebServer is suspended.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_AutomaticallySuspendInBackground;
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* HTTP Basic Authentication scheme (see https://tools.ietf.org/html/rfc2617).
|
||||
*
|
||||
* @warning Use of this authentication scheme is not recommended as the
|
||||
* passwords are sent in clear.
|
||||
*/
|
||||
extern NSString* const GCDWebServerAuthenticationMethod_Basic;
|
||||
|
||||
/**
|
||||
* HTTP Digest Access Authentication scheme (see https://tools.ietf.org/html/rfc2617).
|
||||
*/
|
||||
extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
|
||||
@class GCDWebServer;
|
||||
|
||||
/**
|
||||
* Delegate methods for GCDWebServer.
|
||||
*
|
||||
* @warning These methods are always called on the main thread in a serialized way.
|
||||
*/
|
||||
@protocol GCDWebServerDelegate <NSObject>
|
||||
@optional
|
||||
|
||||
/**
|
||||
* This method is called after the server has successfully started.
|
||||
*/
|
||||
- (void)webServerDidStart:(GCDWebServer*)server;
|
||||
|
||||
/**
|
||||
* This method is called after the Bonjour registration for the server has
|
||||
* successfully completed.
|
||||
*
|
||||
* Use the "bonjourServerURL" property to retrieve the Bonjour address of the
|
||||
* server.
|
||||
*/
|
||||
- (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server;
|
||||
|
||||
/**
|
||||
* This method is called after the NAT port mapping for the server has been
|
||||
* updated.
|
||||
*
|
||||
* Use the "publicServerURL" property to retrieve the public address of the
|
||||
* server.
|
||||
*/
|
||||
- (void)webServerDidUpdateNATPortMapping:(GCDWebServer*)server;
|
||||
|
||||
/**
|
||||
* This method is called when the first GCDWebServerConnection is opened by the
|
||||
* server to serve a series of HTTP requests.
|
||||
*
|
||||
* A series of HTTP requests is considered ongoing as long as new HTTP requests
|
||||
* keep coming (and new GCDWebServerConnection instances keep being opened),
|
||||
* until before the last HTTP request has been responded to (and the
|
||||
* corresponding last GCDWebServerConnection closed).
|
||||
*/
|
||||
- (void)webServerDidConnect:(GCDWebServer*)server;
|
||||
|
||||
/**
|
||||
* This method is called when the last GCDWebServerConnection is closed after
|
||||
* the server has served a series of HTTP requests.
|
||||
*
|
||||
* The GCDWebServerOption_ConnectedStateCoalescingInterval option can be used
|
||||
* to have the server wait some extra delay before considering that the series
|
||||
* of HTTP requests has ended (in case there some latency between consecutive
|
||||
* requests). This effectively coalesces the calls to -webServerDidConnect:
|
||||
* and -webServerDidDisconnect:.
|
||||
*/
|
||||
- (void)webServerDidDisconnect:(GCDWebServer*)server;
|
||||
|
||||
/**
|
||||
* This method is called after the server has stopped.
|
||||
*/
|
||||
- (void)webServerDidStop:(GCDWebServer*)server;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebServer class listens for incoming HTTP requests on a given port,
|
||||
* then passes each one to a "handler" capable of generating an HTTP response
|
||||
* for it, which is then sent back to the client.
|
||||
*
|
||||
* GCDWebServer instances can be created and used from any thread but it's
|
||||
* recommended to have the main thread's runloop be running so internal callbacks
|
||||
* can be handled e.g. for Bonjour registration.
|
||||
*
|
||||
* See the README.md file for more information about the architecture of GCDWebServer.
|
||||
*/
|
||||
@interface GCDWebServer : NSObject
|
||||
|
||||
/**
|
||||
* Sets the delegate for the server.
|
||||
*/
|
||||
@property(nonatomic, weak, nullable) id<GCDWebServerDelegate> delegate;
|
||||
|
||||
/**
|
||||
* Returns YES if the server is currently running.
|
||||
*/
|
||||
@property(nonatomic, readonly, getter=isRunning) BOOL running;
|
||||
|
||||
/**
|
||||
* Returns the port used by the server.
|
||||
*
|
||||
* @warning This property is only valid if the server is running.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSUInteger port;
|
||||
|
||||
/**
|
||||
* Returns the Bonjour name used by the server.
|
||||
*
|
||||
* @warning This property is only valid if the server is running and Bonjour
|
||||
* registration has successfully completed, which can take up to a few seconds.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString* bonjourName;
|
||||
|
||||
/**
|
||||
* Returns the Bonjour service type used by the server.
|
||||
*
|
||||
* @warning This property is only valid if the server is running and Bonjour
|
||||
* registration has successfully completed, which can take up to a few seconds.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString* bonjourType;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
* Adds to the server a handler that generates responses synchronously when handling incoming HTTP requests.
|
||||
*
|
||||
* Handlers are called in a LIFO queue, so if multiple handlers can potentially
|
||||
* respond to a given request, the latest added one wins.
|
||||
*
|
||||
* @warning Addling handlers while the server is running is not allowed.
|
||||
*/
|
||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
|
||||
|
||||
/**
|
||||
* Adds to the server a handler that generates responses asynchronously when handling incoming HTTP requests.
|
||||
*
|
||||
* Handlers are called in a LIFO queue, so if multiple handlers can potentially
|
||||
* respond to a given request, the latest added one wins.
|
||||
*
|
||||
* @warning Addling handlers while the server is running is not allowed.
|
||||
*/
|
||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock;
|
||||
|
||||
/**
|
||||
* Removes all handlers previously added to the server.
|
||||
*
|
||||
* @warning Removing handlers while the server is running is not allowed.
|
||||
*/
|
||||
- (void)removeAllHandlers;
|
||||
|
||||
/**
|
||||
* Starts the server with explicit options. This method is the designated way
|
||||
* to start the server.
|
||||
*
|
||||
* Returns NO if the server failed to start and sets "error" argument if not NULL.
|
||||
*/
|
||||
- (BOOL)startWithOptions:(nullable NSDictionary<NSString*, id>*)options error:(NSError** _Nullable)error;
|
||||
|
||||
/**
|
||||
* Stops the server and prevents it to accepts new HTTP requests.
|
||||
*
|
||||
* @warning Stopping the server does not abort GCDWebServerConnection instances
|
||||
* currently handling already received HTTP requests. These connections will
|
||||
* continue to execute normally until completion.
|
||||
*/
|
||||
- (void)stop;
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServer (Extensions)
|
||||
|
||||
/**
|
||||
* Returns the server's URL.
|
||||
*
|
||||
* @warning This property is only valid if the server is running.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL* serverURL;
|
||||
|
||||
/**
|
||||
* Returns the server's Bonjour URL.
|
||||
*
|
||||
* @warning This property is only valid if the server is running and Bonjour
|
||||
* registration has successfully completed, which can take up to a few seconds.
|
||||
* Also be aware this property will not automatically update if the Bonjour hostname
|
||||
* has been dynamically changed after the server started running (this should be rare).
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL* bonjourServerURL;
|
||||
|
||||
/**
|
||||
* Returns the server's public URL.
|
||||
*
|
||||
* @warning This property is only valid if the server is running and NAT port
|
||||
* mapping is active.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL* publicServerURL;
|
||||
|
||||
/**
|
||||
* Starts the server on port 8080 (OS X & iOS Simulator) or port 80 (iOS)
|
||||
* using the default Bonjour name.
|
||||
*
|
||||
* Returns NO if the server failed to start.
|
||||
*/
|
||||
- (BOOL)start;
|
||||
|
||||
/**
|
||||
* Starts the server on a given port and with a specific Bonjour name.
|
||||
* Pass a nil Bonjour name to disable Bonjour entirely or an empty string to
|
||||
* use the default name.
|
||||
*
|
||||
* Returns NO if the server failed to start.
|
||||
*/
|
||||
- (BOOL)startWithPort:(NSUInteger)port bonjourName:(nullable NSString*)name;
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
|
||||
/**
|
||||
* Runs the server synchronously using -startWithPort:bonjourName: until a
|
||||
* SIGINT signal is received i.e. Ctrl-C. This method is intended to be used
|
||||
* by command line tools.
|
||||
*
|
||||
* Returns NO if the server failed to start.
|
||||
*
|
||||
* @warning This method must be used from the main thread only.
|
||||
*/
|
||||
- (BOOL)runWithPort:(NSUInteger)port bonjourName:(nullable NSString*)name;
|
||||
|
||||
/**
|
||||
* Runs the server synchronously using -startWithOptions: until a SIGTERM or
|
||||
* SIGINT signal is received i.e. Ctrl-C in Terminal. This method is intended to
|
||||
* be used by command line tools.
|
||||
*
|
||||
* Returns NO if the server failed to start and sets "error" argument if not NULL.
|
||||
*
|
||||
* @warning This method must be used from the main thread only.
|
||||
*/
|
||||
- (BOOL)runWithOptions:(nullable NSDictionary<NSString*, id>*)options error:(NSError** _Nullable)error;
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServer (Handlers)
|
||||
|
||||
/**
|
||||
* Adds a default handler to the server to handle all incoming HTTP requests
|
||||
* with a given HTTP method and generate responses synchronously.
|
||||
*/
|
||||
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block;
|
||||
|
||||
/**
|
||||
* Adds a default handler to the server to handle all incoming HTTP requests
|
||||
* with a given HTTP method and generate responses asynchronously.
|
||||
*/
|
||||
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to handle incoming HTTP requests with a given
|
||||
* HTTP method and a specific case-insensitive path and generate responses
|
||||
* synchronously.
|
||||
*/
|
||||
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to handle incoming HTTP requests with a given
|
||||
* HTTP method and a specific case-insensitive path and generate responses
|
||||
* asynchronously.
|
||||
*/
|
||||
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to handle incoming HTTP requests with a given
|
||||
* HTTP method and a path matching a case-insensitive regular expression and
|
||||
* generate responses synchronously.
|
||||
*/
|
||||
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to handle incoming HTTP requests with a given
|
||||
* HTTP method and a path matching a case-insensitive regular expression and
|
||||
* generate responses asynchronously.
|
||||
*/
|
||||
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block;
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServer (GETHandlers)
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to respond to incoming "GET" HTTP requests
|
||||
* with a specific case-insensitive path with in-memory data.
|
||||
*/
|
||||
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(nullable NSString*)contentType cacheAge:(NSUInteger)cacheAge;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to respond to incoming "GET" HTTP requests
|
||||
* with a specific case-insensitive path with a file.
|
||||
*/
|
||||
- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to respond to incoming "GET" HTTP requests
|
||||
* with a case-insensitive path inside a base path with the corresponding file
|
||||
* inside a local directory. If no local file matches the request path, a 401
|
||||
* HTTP status code is returned to the client.
|
||||
*
|
||||
* The "indexFilename" argument allows to specify an "index" file name to use
|
||||
* when the request path corresponds to a directory.
|
||||
*/
|
||||
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(nullable NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* GCDWebServer provides its own built-in logging facility which is used by
|
||||
* default. It simply sends log messages to stderr assuming it is connected
|
||||
* to a terminal type device.
|
||||
*
|
||||
* GCDWebServer is also compatible with a limited set of third-party logging
|
||||
* facilities. If one of them is available at compile time, GCDWebServer will
|
||||
* automatically use it in place of the built-in one.
|
||||
*
|
||||
* Currently supported third-party logging facilities are:
|
||||
* - XLFacility (by the same author as GCDWebServer): https://github.com/swisspol/XLFacility
|
||||
*
|
||||
* For the built-in logging facility, the default logging level is INFO
|
||||
* (or DEBUG if the preprocessor constant "DEBUG" evaluates to non-zero at
|
||||
* compile time).
|
||||
*
|
||||
* It's possible to have GCDWebServer use a custom logging facility by defining
|
||||
* the "__GCDWEBSERVER_LOGGING_HEADER__" preprocessor constant in Xcode build
|
||||
* settings to the name of a custom header file (escaped like \"MyLogging.h\").
|
||||
* This header file must define the following set of macros:
|
||||
*
|
||||
* GWS_LOG_DEBUG(...)
|
||||
* GWS_LOG_VERBOSE(...)
|
||||
* GWS_LOG_INFO(...)
|
||||
* GWS_LOG_WARNING(...)
|
||||
* GWS_LOG_ERROR(...)
|
||||
*
|
||||
* IMPORTANT: These macros must behave like NSLog(). Furthermore the GWS_LOG_DEBUG()
|
||||
* macro should not do anything unless the preprocessor constant "DEBUG" evaluates
|
||||
* to non-zero.
|
||||
*
|
||||
* The logging methods below send log messages to the same logging facility
|
||||
* used by GCDWebServer. They can be used for consistency wherever you interact
|
||||
* with GCDWebServer in your code (e.g. in the implementation of handlers).
|
||||
*/
|
||||
@interface GCDWebServer (Logging)
|
||||
|
||||
/**
|
||||
* Sets the log level of the logging facility below which log messages are discarded.
|
||||
*
|
||||
* @warning The interpretation of the "level" argument depends on the logging
|
||||
* facility used at compile time.
|
||||
*
|
||||
* If using the built-in logging facility, the log levels are as follow:
|
||||
* DEBUG = 0
|
||||
* VERBOSE = 1
|
||||
* INFO = 2
|
||||
* WARNING = 3
|
||||
* ERROR = 4
|
||||
*/
|
||||
+ (void)setLogLevel:(int)level;
|
||||
|
||||
/**
|
||||
* Logs a message to the logging facility at the VERBOSE level.
|
||||
*/
|
||||
- (void)logVerbose:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
/**
|
||||
* Logs a message to the logging facility at the INFO level.
|
||||
*/
|
||||
- (void)logInfo:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
/**
|
||||
* Logs a message to the logging facility at the WARNING level.
|
||||
*/
|
||||
- (void)logWarning:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
/**
|
||||
* Logs a message to the logging facility at the ERROR level.
|
||||
*/
|
||||
- (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
@end
|
||||
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
|
||||
@interface GCDWebServer (Testing)
|
||||
|
||||
/**
|
||||
* Activates recording of HTTP requests and responses which create files in the
|
||||
* current directory containing the raw data for all requests and responses.
|
||||
*
|
||||
* @warning The current directory must not contain any prior recording files.
|
||||
*/
|
||||
@property(nonatomic, getter=isRecordingEnabled) BOOL recordingEnabled;
|
||||
|
||||
/**
|
||||
* Runs tests by playing back pre-recorded HTTP requests in the given directory
|
||||
* and comparing the generated responses with the pre-recorded ones.
|
||||
*
|
||||
* Returns the number of failed tests or -1 if server failed to start.
|
||||
*/
|
||||
- (NSInteger)runTestsWithOptions:(nullable NSDictionary<NSString*, id>*)options inDirectory:(NSString*)path;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
1292
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServer.m
Normal file
1292
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServer.m
Normal file
File diff suppressed because it is too large
Load Diff
183
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerConnection.h
Normal file
183
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerConnection.h
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServer.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class GCDWebServerHandler;
|
||||
|
||||
/**
|
||||
* The GCDWebServerConnection class is instantiated by GCDWebServer to handle
|
||||
* each new HTTP connection. Each instance stays alive until the connection is
|
||||
* closed.
|
||||
*
|
||||
* You cannot use this class directly, but it is made public so you can
|
||||
* subclass it to override some hooks. Use the GCDWebServerOption_ConnectionClass
|
||||
* option for GCDWebServer to install your custom subclass.
|
||||
*
|
||||
* @warning The GCDWebServerConnection retains the GCDWebServer until the
|
||||
* connection is closed.
|
||||
*/
|
||||
@interface GCDWebServerConnection : NSObject
|
||||
|
||||
/**
|
||||
* Returns the GCDWebServer that owns the connection.
|
||||
*/
|
||||
@property(nonatomic, readonly) GCDWebServer* server;
|
||||
|
||||
/**
|
||||
* Returns YES if the connection is using IPv6.
|
||||
*/
|
||||
@property(nonatomic, readonly, getter=isUsingIPv6) BOOL usingIPv6;
|
||||
|
||||
/**
|
||||
* Returns the address of the local peer (i.e. server) of the connection
|
||||
* as a raw "struct sockaddr".
|
||||
*/
|
||||
@property(nonatomic, readonly) NSData* localAddressData;
|
||||
|
||||
/**
|
||||
* Returns the address of the local peer (i.e. server) of the connection
|
||||
* as a string.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* localAddressString;
|
||||
|
||||
/**
|
||||
* Returns the address of the remote peer (i.e. client) of the connection
|
||||
* as a raw "struct sockaddr".
|
||||
*/
|
||||
@property(nonatomic, readonly) NSData* remoteAddressData;
|
||||
|
||||
/**
|
||||
* Returns the address of the remote peer (i.e. client) of the connection
|
||||
* as a string.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* remoteAddressString;
|
||||
|
||||
/**
|
||||
* Returns the total number of bytes received from the remote peer (i.e. client)
|
||||
* so far.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSUInteger totalBytesRead;
|
||||
|
||||
/**
|
||||
* Returns the total number of bytes sent to the remote peer (i.e. client) so far.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSUInteger totalBytesWritten;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Hooks to customize the behavior of GCDWebServer HTTP connections.
|
||||
*
|
||||
* @warning These methods can be called on any GCD thread.
|
||||
* Be sure to also call "super" when overriding them.
|
||||
*/
|
||||
@interface GCDWebServerConnection (Subclassing)
|
||||
|
||||
/**
|
||||
* This method is called when the connection is opened.
|
||||
*
|
||||
* Return NO to reject the connection e.g. after validating the local
|
||||
* or remote address.
|
||||
*/
|
||||
- (BOOL)open;
|
||||
|
||||
/**
|
||||
* This method is called whenever data has been received
|
||||
* from the remote peer (i.e. client).
|
||||
*
|
||||
* @warning Do not attempt to modify this data.
|
||||
*/
|
||||
- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length;
|
||||
|
||||
/**
|
||||
* This method is called whenever data has been sent
|
||||
* to the remote peer (i.e. client).
|
||||
*
|
||||
* @warning Do not attempt to modify this data.
|
||||
*/
|
||||
- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length;
|
||||
|
||||
/**
|
||||
* This method is called after the HTTP headers have been received to
|
||||
* allow replacing the request URL by another one.
|
||||
*
|
||||
* The default implementation returns the original URL.
|
||||
*/
|
||||
- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary<NSString*, NSString*>*)headers;
|
||||
|
||||
/**
|
||||
* Assuming a valid HTTP request was received, this method is called before
|
||||
* the request is processed.
|
||||
*
|
||||
* Return a non-nil GCDWebServerResponse to bypass the request processing entirely.
|
||||
*
|
||||
* The default implementation checks for HTTP authentication if applicable
|
||||
* and returns a barebone 401 status code response if authentication failed.
|
||||
*/
|
||||
- (nullable GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request;
|
||||
|
||||
/**
|
||||
* Assuming a valid HTTP request was received and -preflightRequest: returned nil,
|
||||
* this method is called to process the request by executing the handler's
|
||||
* process block.
|
||||
*/
|
||||
- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
* Assuming a valid HTTP request was received and either -preflightRequest:
|
||||
* or -processRequest:completion: returned a non-nil GCDWebServerResponse,
|
||||
* this method is called to override the response.
|
||||
*
|
||||
* You can either modify the current response and return it, or return a
|
||||
* completely new one.
|
||||
*
|
||||
* The default implementation replaces any response matching the "ETag" or
|
||||
* "Last-Modified-Date" header of the request by a barebone "Not-Modified" (304)
|
||||
* one.
|
||||
*/
|
||||
- (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request;
|
||||
|
||||
/**
|
||||
* This method is called if any error happens while validing or processing
|
||||
* the request or if no GCDWebServerResponse was generated during processing.
|
||||
*
|
||||
* @warning If the request was invalid (e.g. the HTTP headers were malformed),
|
||||
* the "request" argument will be nil.
|
||||
*/
|
||||
- (void)abortRequest:(nullable GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode;
|
||||
|
||||
/**
|
||||
* Called when the connection is closed.
|
||||
*/
|
||||
- (void)close;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
843
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerConnection.m
Normal file
843
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerConnection.m
Normal file
@ -0,0 +1,843 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#import <netdb.h>
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
#import <libkern/OSAtomic.h>
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kHeadersReadCapacity (1 * 1024)
|
||||
#define kBodyReadCapacity (256 * 1024)
|
||||
|
||||
typedef void (^ReadDataCompletionBlock)(BOOL success);
|
||||
typedef void (^ReadHeadersCompletionBlock)(NSData* extraData);
|
||||
typedef void (^ReadBodyCompletionBlock)(BOOL success);
|
||||
|
||||
typedef void (^WriteDataCompletionBlock)(BOOL success);
|
||||
typedef void (^WriteHeadersCompletionBlock)(BOOL success);
|
||||
typedef void (^WriteBodyCompletionBlock)(BOOL success);
|
||||
|
||||
static NSData* _CRLFData = nil;
|
||||
static NSData* _CRLFCRLFData = nil;
|
||||
static NSData* _continueData = nil;
|
||||
static NSData* _lastChunkData = nil;
|
||||
static NSString* _digestAuthenticationNonce = nil;
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
static int32_t _connectionCounter = 0;
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface GCDWebServerConnection (Read)
|
||||
- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block;
|
||||
- (void)readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block;
|
||||
- (void)readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block;
|
||||
- (void)readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerConnection (Write)
|
||||
- (void)writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block;
|
||||
- (void)writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block;
|
||||
- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@implementation GCDWebServerConnection {
|
||||
CFSocketNativeHandle _socket;
|
||||
BOOL _virtualHEAD;
|
||||
|
||||
CFHTTPMessageRef _requestMessage;
|
||||
GCDWebServerRequest* _request;
|
||||
GCDWebServerHandler* _handler;
|
||||
CFHTTPMessageRef _responseMessage;
|
||||
GCDWebServerResponse* _response;
|
||||
NSInteger _statusCode;
|
||||
|
||||
BOOL _opened;
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
NSUInteger _connectionIndex;
|
||||
NSString* _requestPath;
|
||||
int _requestFD;
|
||||
NSString* _responsePath;
|
||||
int _responseFD;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (void)initialize {
|
||||
if (_CRLFData == nil) {
|
||||
_CRLFData = [[NSData alloc] initWithBytes:"\r\n" length:2];
|
||||
GWS_DCHECK(_CRLFData);
|
||||
}
|
||||
if (_CRLFCRLFData == nil) {
|
||||
_CRLFCRLFData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
|
||||
GWS_DCHECK(_CRLFCRLFData);
|
||||
}
|
||||
if (_continueData == nil) {
|
||||
CFHTTPMessageRef message = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 100, NULL, kCFHTTPVersion1_1);
|
||||
_continueData = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(message));
|
||||
CFRelease(message);
|
||||
GWS_DCHECK(_continueData);
|
||||
}
|
||||
if (_lastChunkData == nil) {
|
||||
_lastChunkData = [[NSData alloc] initWithBytes:"0\r\n\r\n" length:5];
|
||||
}
|
||||
if (_digestAuthenticationNonce == nil) {
|
||||
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
|
||||
_digestAuthenticationNonce = GCDWebServerComputeMD5Digest(@"%@", CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid)));
|
||||
CFRelease(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isUsingIPv6 {
|
||||
const struct sockaddr* localSockAddr = _localAddressData.bytes;
|
||||
return (localSockAddr->sa_family == AF_INET6);
|
||||
}
|
||||
|
||||
- (void)_initializeResponseHeadersWithStatusCode:(NSInteger)statusCode {
|
||||
_statusCode = statusCode;
|
||||
_responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1);
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Connection"), CFSTR("Close"));
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (__bridge CFStringRef)_server.serverName);
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (__bridge CFStringRef)GCDWebServerFormatRFC822([NSDate date]));
|
||||
}
|
||||
|
||||
- (void)_startProcessingRequest {
|
||||
GWS_DCHECK(_responseMessage == NULL);
|
||||
|
||||
GCDWebServerResponse* preflightResponse = [self preflightRequest:_request];
|
||||
if (preflightResponse) {
|
||||
[self _finishProcessingRequest:preflightResponse];
|
||||
} else {
|
||||
[self processRequest:_request
|
||||
completion:^(GCDWebServerResponse* processResponse) {
|
||||
[self _finishProcessingRequest:processResponse];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
- (void)_finishProcessingRequest:(GCDWebServerResponse*)response {
|
||||
GWS_DCHECK(_responseMessage == NULL);
|
||||
BOOL hasBody = NO;
|
||||
|
||||
if (response) {
|
||||
response = [self overrideResponse:response forRequest:_request];
|
||||
}
|
||||
if (response) {
|
||||
if ([response hasBody]) {
|
||||
[response prepareForReading];
|
||||
hasBody = !_virtualHEAD;
|
||||
}
|
||||
NSError* error = nil;
|
||||
if (hasBody && ![response performOpen:&error]) {
|
||||
GWS_LOG_ERROR(@"Failed opening response body for socket %i: %@", _socket, error);
|
||||
} else {
|
||||
_response = response;
|
||||
}
|
||||
}
|
||||
|
||||
if (_response) {
|
||||
[self _initializeResponseHeadersWithStatusCode:_response.statusCode];
|
||||
if (_response.lastModifiedDate) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Last-Modified"), (__bridge CFStringRef)GCDWebServerFormatRFC822((NSDate*)_response.lastModifiedDate));
|
||||
}
|
||||
if (_response.eTag) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("ETag"), (__bridge CFStringRef)_response.eTag);
|
||||
}
|
||||
if ((_response.statusCode >= 200) && (_response.statusCode < 300)) {
|
||||
if (_response.cacheControlMaxAge > 0) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), (__bridge CFStringRef)[NSString stringWithFormat:@"max-age=%i, public", (int)_response.cacheControlMaxAge]);
|
||||
} else {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), CFSTR("no-cache"));
|
||||
}
|
||||
}
|
||||
if (_response.contentType != nil) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Type"), (__bridge CFStringRef)GCDWebServerNormalizeHeaderValue(_response.contentType));
|
||||
}
|
||||
if (_response.contentLength != NSUIntegerMax) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Length"), (__bridge CFStringRef)[NSString stringWithFormat:@"%lu", (unsigned long)_response.contentLength]);
|
||||
}
|
||||
if (_response.usesChunkedTransferEncoding) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Transfer-Encoding"), CFSTR("chunked"));
|
||||
}
|
||||
[_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) {
|
||||
CFHTTPMessageSetHeaderFieldValue(self->_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
|
||||
}];
|
||||
[self writeHeadersWithCompletionBlock:^(BOOL success) {
|
||||
if (success) {
|
||||
if (hasBody) {
|
||||
[self writeBodyWithCompletionBlock:^(BOOL successInner) {
|
||||
[self->_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent
|
||||
}];
|
||||
}
|
||||
} else if (hasBody) {
|
||||
[self->_response performClose];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_readBodyWithLength:(NSUInteger)length initialData:(NSData*)initialData {
|
||||
NSError* error = nil;
|
||||
if (![_request performOpen:&error]) {
|
||||
GWS_LOG_ERROR(@"Failed opening request body for socket %i: %@", _socket, error);
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
return;
|
||||
}
|
||||
|
||||
if (initialData.length) {
|
||||
if (![_request performWriteData:initialData error:&error]) {
|
||||
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
||||
if (![_request performClose:&error]) {
|
||||
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
||||
}
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
return;
|
||||
}
|
||||
length -= initialData.length;
|
||||
}
|
||||
|
||||
if (length) {
|
||||
[self readBodyWithRemainingLength:length
|
||||
completionBlock:^(BOOL success) {
|
||||
NSError* localError = nil;
|
||||
if ([self->_request performClose:&localError]) {
|
||||
[self _startProcessingRequest];
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", self->_socket, error);
|
||||
[self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
if ([_request performClose:&error]) {
|
||||
[self _startProcessingRequest];
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_readChunkedBodyWithInitialData:(NSData*)initialData {
|
||||
NSError* error = nil;
|
||||
if (![_request performOpen:&error]) {
|
||||
GWS_LOG_ERROR(@"Failed opening request body for socket %i: %@", _socket, error);
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData];
|
||||
[self readNextBodyChunk:chunkData
|
||||
completionBlock:^(BOOL success) {
|
||||
NSError* localError = nil;
|
||||
if ([self->_request performClose:&localError]) {
|
||||
[self _startProcessingRequest];
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", self->_socket, error);
|
||||
[self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_readRequestHeaders {
|
||||
_requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
|
||||
NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity];
|
||||
[self readHeaders:headersData
|
||||
withCompletionBlock:^(NSData* extraData) {
|
||||
if (extraData) {
|
||||
NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(self->_requestMessage)); // Method verbs are case-sensitive and uppercase
|
||||
if (self->_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) {
|
||||
requestMethod = @"GET";
|
||||
self->_virtualHEAD = YES;
|
||||
}
|
||||
NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(self->_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones
|
||||
NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(self->_requestMessage));
|
||||
if (requestURL) {
|
||||
requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders];
|
||||
GWS_DCHECK(requestURL);
|
||||
}
|
||||
NSString* urlPath = requestURL ? CFBridgingRelease(CFURLCopyPath((CFURLRef)requestURL)) : nil; // Don't use -[NSURL path] which strips the ending slash
|
||||
if (urlPath == nil) {
|
||||
urlPath = @"/"; // CFURLCopyPath() returns NULL for a relative URL with path "//" contrary to -[NSURL path] which returns "/"
|
||||
}
|
||||
NSString* requestPath = urlPath ? GCDWebServerUnescapeURLString(urlPath) : nil;
|
||||
NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped;
|
||||
NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{};
|
||||
if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) {
|
||||
for (self->_handler in self->_server.handlers) {
|
||||
self->_request = self->_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery);
|
||||
if (self->_request) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (self->_request) {
|
||||
self->_request.localAddressData = self.localAddressData;
|
||||
self->_request.remoteAddressData = self.remoteAddressData;
|
||||
if ([self->_request hasBody]) {
|
||||
[self->_request prepareForWriting];
|
||||
if (self->_request.usesChunkedTransferEncoding || (extraData.length <= self->_request.contentLength)) {
|
||||
NSString* expectHeader = [requestHeaders objectForKey:@"Expect"];
|
||||
if (expectHeader) {
|
||||
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing
|
||||
[self writeData:_continueData
|
||||
withCompletionBlock:^(BOOL success) {
|
||||
if (success) {
|
||||
if (self->_request.usesChunkedTransferEncoding) {
|
||||
[self _readChunkedBodyWithInitialData:extraData];
|
||||
} else {
|
||||
[self _readBodyWithLength:self->_request.contentLength initialData:extraData];
|
||||
}
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", self->_socket);
|
||||
[self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed];
|
||||
}
|
||||
} else {
|
||||
if (self->_request.usesChunkedTransferEncoding) {
|
||||
[self _readChunkedBodyWithInitialData:extraData];
|
||||
} else {
|
||||
[self _readBodyWithLength:self->_request.contentLength initialData:extraData];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", self->_socket);
|
||||
[self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
|
||||
}
|
||||
} else {
|
||||
[self _startProcessingRequest];
|
||||
}
|
||||
} else {
|
||||
self->_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
|
||||
GWS_DCHECK(self->_request);
|
||||
[self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_NotImplemented];
|
||||
}
|
||||
} else {
|
||||
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
} else {
|
||||
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (instancetype)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket {
|
||||
if ((self = [super init])) {
|
||||
_server = server;
|
||||
_localAddressData = localAddress;
|
||||
_remoteAddressData = remoteAddress;
|
||||
_socket = socket;
|
||||
GWS_LOG_DEBUG(@"Did open connection on socket %i", _socket);
|
||||
|
||||
[_server willStartConnection:self];
|
||||
|
||||
if (![self open]) {
|
||||
close(_socket);
|
||||
return nil;
|
||||
}
|
||||
_opened = YES;
|
||||
|
||||
[self _readRequestHeaders];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString*)localAddressString {
|
||||
return GCDWebServerStringFromSockAddr(_localAddressData.bytes, YES);
|
||||
}
|
||||
|
||||
- (NSString*)remoteAddressString {
|
||||
return GCDWebServerStringFromSockAddr(_remoteAddressData.bytes, YES);
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
int result = close(_socket);
|
||||
if (result != 0) {
|
||||
GWS_LOG_ERROR(@"Failed closing socket %i for connection: %s (%i)", _socket, strerror(errno), errno);
|
||||
} else {
|
||||
GWS_LOG_DEBUG(@"Did close connection on socket %i", _socket);
|
||||
}
|
||||
|
||||
if (_opened) {
|
||||
[self close];
|
||||
}
|
||||
|
||||
[_server didEndConnection:self];
|
||||
|
||||
if (_requestMessage) {
|
||||
CFRelease(_requestMessage);
|
||||
}
|
||||
|
||||
if (_responseMessage) {
|
||||
CFRelease(_responseMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerConnection (Read)
|
||||
|
||||
- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block {
|
||||
dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) {
|
||||
@autoreleasepool {
|
||||
if (error == 0) {
|
||||
size_t size = dispatch_data_get_size(buffer);
|
||||
if (size > 0) {
|
||||
NSUInteger originalLength = data.length;
|
||||
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
|
||||
[data appendBytes:chunkBytes length:chunkSize];
|
||||
return true;
|
||||
});
|
||||
[self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)];
|
||||
block(YES);
|
||||
} else {
|
||||
if (self->_totalBytesRead > 0) {
|
||||
GWS_LOG_ERROR(@"No more data available on socket %i", self->_socket);
|
||||
} else {
|
||||
GWS_LOG_WARNING(@"No data received from socket %i", self->_socket);
|
||||
}
|
||||
block(NO);
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Error while reading from socket %i: %s (%i)", self->_socket, strerror(error), error);
|
||||
block(NO);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block {
|
||||
GWS_DCHECK(_requestMessage);
|
||||
[self readData:headersData
|
||||
withLength:NSUIntegerMax
|
||||
completionBlock:^(BOOL success) {
|
||||
if (success) {
|
||||
NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)];
|
||||
if (range.location == NSNotFound) {
|
||||
[self readHeaders:headersData withCompletionBlock:block];
|
||||
} else {
|
||||
NSUInteger length = range.location + range.length;
|
||||
if (CFHTTPMessageAppendBytes(self->_requestMessage, headersData.bytes, length)) {
|
||||
if (CFHTTPMessageIsHeaderComplete(self->_requestMessage)) {
|
||||
block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]);
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", self->_socket);
|
||||
block(nil);
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", self->_socket);
|
||||
block(nil);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
block(nil);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block {
|
||||
GWS_DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]);
|
||||
NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity];
|
||||
[self readData:bodyData
|
||||
withLength:length
|
||||
completionBlock:^(BOOL success) {
|
||||
if (success) {
|
||||
if (bodyData.length <= length) {
|
||||
NSError* error = nil;
|
||||
if ([self->_request performWriteData:bodyData error:&error]) {
|
||||
NSUInteger remainingLength = length - bodyData.length;
|
||||
if (remainingLength) {
|
||||
[self readBodyWithRemainingLength:remainingLength completionBlock:block];
|
||||
} else {
|
||||
block(YES);
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", self->_socket, error);
|
||||
block(NO);
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", self->_socket);
|
||||
block(NO);
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
} else {
|
||||
block(NO);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
char buffer[size + 1];
|
||||
bcopy(bytes, buffer, size);
|
||||
buffer[size] = 0;
|
||||
char* end = NULL;
|
||||
long result = strtol(buffer, &end, 16);
|
||||
return ((end != NULL) && (*end == 0) && (result >= 0) ? result : NSNotFound);
|
||||
}
|
||||
|
||||
- (void)readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block {
|
||||
GWS_DCHECK([_request hasBody] && [_request usesChunkedTransferEncoding]);
|
||||
|
||||
while (1) {
|
||||
NSRange range = [chunkData rangeOfData:_CRLFData options:0 range:NSMakeRange(0, chunkData.length)];
|
||||
if (range.location == NSNotFound) {
|
||||
break;
|
||||
}
|
||||
NSRange extensionRange = [chunkData rangeOfData:[NSData dataWithBytes:";" length:1] options:0 range:NSMakeRange(0, range.location)]; // Ignore chunk extensions
|
||||
NSUInteger length = _ScanHexNumber((char*)chunkData.bytes, extensionRange.location != NSNotFound ? extensionRange.location : range.location);
|
||||
if (length != NSNotFound) {
|
||||
if (length) {
|
||||
if (chunkData.length < range.location + range.length + length + 2) {
|
||||
break;
|
||||
}
|
||||
const char* ptr = (char*)chunkData.bytes + range.location + range.length + length;
|
||||
if ((*ptr == '\r') && (*(ptr + 1) == '\n')) {
|
||||
NSError* error = nil;
|
||||
if ([_request performWriteData:[chunkData subdataWithRange:NSMakeRange(range.location + range.length, length)] error:&error]) {
|
||||
[chunkData replaceBytesInRange:NSMakeRange(0, range.location + range.length + length + 2) withBytes:NULL length:0];
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
||||
block(NO);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Missing terminating CRLF sequence for chunk reading request body on socket %i", _socket);
|
||||
block(NO);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
NSRange trailerRange = [chunkData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(range.location, chunkData.length - range.location)]; // Ignore trailers
|
||||
if (trailerRange.location != NSNotFound) {
|
||||
block(YES);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Invalid chunk length reading request body on socket %i", _socket);
|
||||
block(NO);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[self readData:chunkData
|
||||
withLength:NSUIntegerMax
|
||||
completionBlock:^(BOOL success) {
|
||||
if (success) {
|
||||
[self readNextBodyChunk:chunkData completionBlock:block];
|
||||
} else {
|
||||
block(NO);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerConnection (Write)
|
||||
|
||||
- (void)writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
|
||||
dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^{
|
||||
[data self]; // Keeps ARC from releasing data too early
|
||||
});
|
||||
dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) {
|
||||
@autoreleasepool {
|
||||
if (error == 0) {
|
||||
GWS_DCHECK(remainingData == NULL);
|
||||
[self didWriteBytes:data.bytes length:data.length];
|
||||
block(YES);
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Error while writing to socket %i: %s (%i)", self->_socket, strerror(error), error);
|
||||
block(NO);
|
||||
}
|
||||
}
|
||||
});
|
||||
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||
dispatch_release(buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block {
|
||||
GWS_DCHECK(_responseMessage);
|
||||
CFDataRef data = CFHTTPMessageCopySerializedMessage(_responseMessage);
|
||||
[self writeData:(__bridge NSData*)data withCompletionBlock:block];
|
||||
CFRelease(data);
|
||||
}
|
||||
|
||||
- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
|
||||
GWS_DCHECK([_response hasBody]);
|
||||
[_response performReadDataWithCompletion:^(NSData* data, NSError* error) {
|
||||
if (data) {
|
||||
if (data.length) {
|
||||
if (self->_response.usesChunkedTransferEncoding) {
|
||||
const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String];
|
||||
size_t hexLength = strlen(hexString);
|
||||
NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)];
|
||||
if (chunk == nil) {
|
||||
GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", self->_socket, error);
|
||||
block(NO);
|
||||
return;
|
||||
}
|
||||
char* ptr = (char*)[(NSMutableData*)chunk mutableBytes];
|
||||
bcopy(hexString, ptr, hexLength);
|
||||
ptr += hexLength;
|
||||
*ptr++ = '\r';
|
||||
*ptr++ = '\n';
|
||||
bcopy(data.bytes, ptr, data.length);
|
||||
ptr += data.length;
|
||||
*ptr++ = '\r';
|
||||
*ptr = '\n';
|
||||
data = chunk;
|
||||
}
|
||||
[self writeData:data
|
||||
withCompletionBlock:^(BOOL success) {
|
||||
if (success) {
|
||||
[self writeBodyWithCompletionBlock:block];
|
||||
} else {
|
||||
block(NO);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
if (self->_response.usesChunkedTransferEncoding) {
|
||||
[self writeData:_lastChunkData
|
||||
withCompletionBlock:^(BOOL success) {
|
||||
block(success);
|
||||
}];
|
||||
} else {
|
||||
block(YES);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", self->_socket, error);
|
||||
block(NO);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerConnection (Subclassing)
|
||||
|
||||
- (BOOL)open {
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
if (_server.recordingEnabled) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
_connectionIndex = OSAtomicIncrement32(&_connectionCounter);
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
_requestPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
_requestFD = open([_requestPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
GWS_DCHECK(_requestFD > 0);
|
||||
|
||||
_responsePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
_responseFD = open([_responsePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
GWS_DCHECK(_responseFD > 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length {
|
||||
GWS_LOG_DEBUG(@"Connection received %lu bytes on socket %i", (unsigned long)length, _socket);
|
||||
_totalBytesRead += length;
|
||||
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) {
|
||||
GWS_LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno);
|
||||
close(_requestFD);
|
||||
_requestFD = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length {
|
||||
GWS_LOG_DEBUG(@"Connection sent %lu bytes on socket %i", (unsigned long)length, _socket);
|
||||
_totalBytesWritten += length;
|
||||
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) {
|
||||
GWS_LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno);
|
||||
close(_responseFD);
|
||||
_responseFD = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary<NSString*, NSString*>*)headers {
|
||||
return url;
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc2617
|
||||
- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request {
|
||||
GWS_LOG_DEBUG(@"Connection on socket %i preflighting request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead);
|
||||
GCDWebServerResponse* response = nil;
|
||||
if (_server.authenticationBasicAccounts) {
|
||||
__block BOOL authenticated = NO;
|
||||
NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"];
|
||||
if ([authorizationHeader hasPrefix:@"Basic "]) {
|
||||
NSString* basicAccount = [authorizationHeader substringFromIndex:6];
|
||||
[_server.authenticationBasicAccounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* digest, BOOL* stop) {
|
||||
if ([basicAccount isEqualToString:digest]) {
|
||||
authenticated = YES;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
}
|
||||
if (!authenticated) {
|
||||
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized];
|
||||
[response setValue:[NSString stringWithFormat:@"Basic realm=\"%@\"", _server.authenticationRealm] forAdditionalHeader:@"WWW-Authenticate"];
|
||||
}
|
||||
} else if (_server.authenticationDigestAccounts) {
|
||||
BOOL authenticated = NO;
|
||||
BOOL isStaled = NO;
|
||||
NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"];
|
||||
if ([authorizationHeader hasPrefix:@"Digest "]) {
|
||||
NSString* realm = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"realm");
|
||||
if (realm && [_server.authenticationRealm isEqualToString:realm]) {
|
||||
NSString* nonce = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"nonce");
|
||||
if ([nonce isEqualToString:_digestAuthenticationNonce]) {
|
||||
NSString* username = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"username");
|
||||
NSString* uri = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"uri");
|
||||
NSString* actualResponse = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"response");
|
||||
NSString* ha1 = [_server.authenticationDigestAccounts objectForKey:username];
|
||||
NSString* ha2 = GCDWebServerComputeMD5Digest(@"%@:%@", request.method, uri); // We cannot use "request.path" as the query string is required
|
||||
NSString* expectedResponse = GCDWebServerComputeMD5Digest(@"%@:%@:%@", ha1, _digestAuthenticationNonce, ha2);
|
||||
if ([actualResponse isEqualToString:expectedResponse]) {
|
||||
authenticated = YES;
|
||||
}
|
||||
} else if (nonce.length) {
|
||||
isStaled = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!authenticated) {
|
||||
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized];
|
||||
[response setValue:[NSString stringWithFormat:@"Digest realm=\"%@\", nonce=\"%@\"%@", _server.authenticationRealm, _digestAuthenticationNonce, isStaled ? @", stale=TRUE" : @""] forAdditionalHeader:@"WWW-Authenticate"]; // TODO: Support Quality of Protection ("qop")
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion {
|
||||
GWS_LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead);
|
||||
_handler.asyncProcessBlock(request, [completion copy]);
|
||||
}
|
||||
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
|
||||
static inline BOOL _CompareResources(NSString* responseETag, NSString* requestETag, NSDate* responseLastModified, NSDate* requestLastModified) {
|
||||
if (requestLastModified && responseLastModified) {
|
||||
if ([responseLastModified compare:requestLastModified] != NSOrderedDescending) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
if (requestETag && responseETag) { // Per the specs "If-None-Match" must be checked after "If-Modified-Since"
|
||||
if ([requestETag isEqualToString:@"*"]) {
|
||||
return YES;
|
||||
}
|
||||
if ([responseETag isEqualToString:requestETag]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request {
|
||||
if ((response.statusCode >= 200) && (response.statusCode < 300) && _CompareResources(response.eTag, request.ifNoneMatch, response.lastModifiedDate, request.ifModifiedSince)) {
|
||||
NSInteger code = [request.method isEqualToString:@"HEAD"] || [request.method isEqualToString:@"GET"] ? kGCDWebServerHTTPStatusCode_NotModified : kGCDWebServerHTTPStatusCode_PreconditionFailed;
|
||||
GCDWebServerResponse* newResponse = [GCDWebServerResponse responseWithStatusCode:code];
|
||||
newResponse.cacheControlMaxAge = response.cacheControlMaxAge;
|
||||
newResponse.lastModifiedDate = response.lastModifiedDate;
|
||||
newResponse.eTag = response.eTag;
|
||||
GWS_DCHECK(newResponse);
|
||||
return newResponse;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
- (void)abortRequest:(GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode {
|
||||
GWS_DCHECK(_responseMessage == NULL);
|
||||
GWS_DCHECK((statusCode >= 400) && (statusCode < 600));
|
||||
[self _initializeResponseHeadersWithStatusCode:statusCode];
|
||||
[self writeHeadersWithCompletionBlock:^(BOOL success) {
|
||||
; // Nothing more to do
|
||||
}];
|
||||
GWS_LOG_DEBUG(@"Connection aborted with status code %i on socket %i", (int)statusCode, _socket);
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
if (_requestPath) {
|
||||
BOOL success = NO;
|
||||
NSError* error = nil;
|
||||
if (_requestFD > 0) {
|
||||
close(_requestFD);
|
||||
NSString* name = [NSString stringWithFormat:@"%03lu-%@.request", (unsigned long)_connectionIndex, _virtualHEAD ? @"HEAD" : _request.method];
|
||||
success = [[NSFileManager defaultManager] moveItemAtPath:_requestPath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error];
|
||||
}
|
||||
if (!success) {
|
||||
GWS_LOG_ERROR(@"Failed saving recorded request: %@", error);
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
unlink([_requestPath fileSystemRepresentation]);
|
||||
}
|
||||
|
||||
if (_responsePath) {
|
||||
BOOL success = NO;
|
||||
NSError* error = nil;
|
||||
if (_responseFD > 0) {
|
||||
close(_responseFD);
|
||||
NSString* name = [NSString stringWithFormat:@"%03lu-%i.response", (unsigned long)_connectionIndex, (int)_statusCode];
|
||||
success = [[NSFileManager defaultManager] moveItemAtPath:_responsePath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error];
|
||||
}
|
||||
if (!success) {
|
||||
GWS_LOG_ERROR(@"Failed saving recorded response: %@", error);
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
unlink([_responsePath fileSystemRepresentation]);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_request) {
|
||||
GWS_LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead, (unsigned long)_totalBytesWritten);
|
||||
} else {
|
||||
GWS_LOG_VERBOSE(@"[%@] %@ %i \"(invalid request)\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, (unsigned long)_totalBytesRead, (unsigned long)_totalBytesWritten);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
114
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerFunctions.h
Normal file
114
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerFunctions.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Converts a file extension to the corresponding MIME type.
|
||||
* If there is no match, "application/octet-stream" is returned.
|
||||
*
|
||||
* Overrides allow to customize the built-in mapping from extensions to MIME
|
||||
* types. Keys of the dictionary must be lowercased file extensions without
|
||||
* the period, and the values must be the corresponding MIME types.
|
||||
*/
|
||||
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary<NSString*, NSString*>* _Nullable overrides);
|
||||
|
||||
/**
|
||||
* Add percent-escapes to a string so it can be used in a URL.
|
||||
* The legal characters ":@/?&=+" are also escaped to ensure compatibility
|
||||
* with URL encoded forms and URL queries.
|
||||
*/
|
||||
NSString* _Nullable GCDWebServerEscapeURLString(NSString* string);
|
||||
|
||||
/**
|
||||
* Unescapes a URL percent-encoded string.
|
||||
*/
|
||||
NSString* _Nullable GCDWebServerUnescapeURLString(NSString* string);
|
||||
|
||||
/**
|
||||
* Extracts the unescaped names and values from an
|
||||
* "application/x-www-form-urlencoded" form.
|
||||
* http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
|
||||
*/
|
||||
NSDictionary<NSString*, NSString*>* GCDWebServerParseURLEncodedForm(NSString* form);
|
||||
|
||||
/**
|
||||
* On OS X, returns the IPv4 or IPv6 address as a string of the primary
|
||||
* connected service or nil if not available.
|
||||
*
|
||||
* On iOS, returns the IPv4 or IPv6 address as a string of the WiFi
|
||||
* interface if connected or nil otherwise.
|
||||
*/
|
||||
NSString* _Nullable GCDWebServerGetPrimaryIPAddress(BOOL useIPv6);
|
||||
|
||||
/**
|
||||
* Converts a date into a string using RFC822 formatting.
|
||||
* https://tools.ietf.org/html/rfc822#section-5
|
||||
* https://tools.ietf.org/html/rfc1123#section-5.2.14
|
||||
*/
|
||||
NSString* GCDWebServerFormatRFC822(NSDate* date);
|
||||
|
||||
/**
|
||||
* Converts a RFC822 formatted string into a date.
|
||||
* https://tools.ietf.org/html/rfc822#section-5
|
||||
* https://tools.ietf.org/html/rfc1123#section-5.2.14
|
||||
*
|
||||
* @warning Timezones other than GMT are not supported by this function.
|
||||
*/
|
||||
NSDate* _Nullable GCDWebServerParseRFC822(NSString* string);
|
||||
|
||||
/**
|
||||
* Converts a date into a string using IOS 8601 formatting.
|
||||
* http://tools.ietf.org/html/rfc3339#section-5.6
|
||||
*/
|
||||
NSString* GCDWebServerFormatISO8601(NSDate* date);
|
||||
|
||||
/**
|
||||
* Converts a ISO 8601 formatted string into a date.
|
||||
* http://tools.ietf.org/html/rfc3339#section-5.6
|
||||
*
|
||||
* @warning Only "calendar" variant is supported at this time and timezones
|
||||
* other than GMT are not supported either.
|
||||
*/
|
||||
NSDate* _Nullable GCDWebServerParseISO8601(NSString* string);
|
||||
|
||||
/**
|
||||
* Removes "//", "/./" and "/../" components from path as well as any trailing slash.
|
||||
*/
|
||||
NSString* GCDWebServerNormalizePath(NSString* path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
331
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerFunctions.m
Normal file
331
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerFunctions.m
Normal file
@ -0,0 +1,331 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
#else
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#endif
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
#import <ifaddrs.h>
|
||||
#import <net/if.h>
|
||||
#import <netdb.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
static NSDateFormatter* _dateFormatterRFC822 = nil;
|
||||
static NSDateFormatter* _dateFormatterISO8601 = nil;
|
||||
static dispatch_queue_t _dateFormatterQueue = NULL;
|
||||
|
||||
// TODO: Handle RFC 850 and ANSI C's asctime() format
|
||||
void GCDWebServerInitializeFunctions() {
|
||||
GWS_DCHECK([NSThread isMainThread]); // NSDateFormatter should be initialized on main thread
|
||||
if (_dateFormatterRFC822 == nil) {
|
||||
_dateFormatterRFC822 = [[NSDateFormatter alloc] init];
|
||||
_dateFormatterRFC822.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
|
||||
_dateFormatterRFC822.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
|
||||
_dateFormatterRFC822.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
|
||||
GWS_DCHECK(_dateFormatterRFC822);
|
||||
}
|
||||
if (_dateFormatterISO8601 == nil) {
|
||||
_dateFormatterISO8601 = [[NSDateFormatter alloc] init];
|
||||
_dateFormatterISO8601.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
|
||||
_dateFormatterISO8601.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'+00:00'";
|
||||
_dateFormatterISO8601.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
|
||||
GWS_DCHECK(_dateFormatterISO8601);
|
||||
}
|
||||
if (_dateFormatterQueue == NULL) {
|
||||
_dateFormatterQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
|
||||
GWS_DCHECK(_dateFormatterQueue);
|
||||
}
|
||||
}
|
||||
|
||||
NSString* GCDWebServerNormalizeHeaderValue(NSString* value) {
|
||||
if (value) {
|
||||
NSRange range = [value rangeOfString:@";"]; // Assume part before ";" separator is case-insensitive
|
||||
if (range.location != NSNotFound) {
|
||||
value = [[[value substringToIndex:range.location] lowercaseString] stringByAppendingString:[value substringFromIndex:range.location]];
|
||||
} else {
|
||||
value = [value lowercaseString];
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerTruncateHeaderValue(NSString* value) {
|
||||
if (value) {
|
||||
NSRange range = [value rangeOfString:@";"];
|
||||
if (range.location != NSNotFound) {
|
||||
return [value substringToIndex:range.location];
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerExtractHeaderValueParameter(NSString* value, NSString* name) {
|
||||
NSString* parameter = nil;
|
||||
if (value) {
|
||||
NSScanner* scanner = [[NSScanner alloc] initWithString:value];
|
||||
[scanner setCaseSensitive:NO]; // Assume parameter names are case-insensitive
|
||||
NSString* string = [NSString stringWithFormat:@"%@=", name];
|
||||
if ([scanner scanUpToString:string intoString:NULL]) {
|
||||
[scanner scanString:string intoString:NULL];
|
||||
if ([scanner scanString:@"\"" intoString:NULL]) {
|
||||
[scanner scanUpToString:@"\"" intoString:¶meter];
|
||||
} else {
|
||||
[scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:¶meter];
|
||||
}
|
||||
}
|
||||
}
|
||||
return parameter;
|
||||
}
|
||||
|
||||
// http://www.w3schools.com/tags/ref_charactersets.asp
|
||||
NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset) {
|
||||
NSStringEncoding encoding = kCFStringEncodingInvalidId;
|
||||
if (charset) {
|
||||
encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)charset));
|
||||
}
|
||||
return (encoding != kCFStringEncodingInvalidId ? encoding : NSUTF8StringEncoding);
|
||||
}
|
||||
|
||||
NSString* GCDWebServerFormatRFC822(NSDate* date) {
|
||||
__block NSString* string;
|
||||
dispatch_sync(_dateFormatterQueue, ^{
|
||||
string = [_dateFormatterRFC822 stringFromDate:date];
|
||||
});
|
||||
return string;
|
||||
}
|
||||
|
||||
NSDate* GCDWebServerParseRFC822(NSString* string) {
|
||||
__block NSDate* date;
|
||||
dispatch_sync(_dateFormatterQueue, ^{
|
||||
date = [_dateFormatterRFC822 dateFromString:string];
|
||||
});
|
||||
return date;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerFormatISO8601(NSDate* date) {
|
||||
__block NSString* string;
|
||||
dispatch_sync(_dateFormatterQueue, ^{
|
||||
string = [_dateFormatterISO8601 stringFromDate:date];
|
||||
});
|
||||
return string;
|
||||
}
|
||||
|
||||
NSDate* GCDWebServerParseISO8601(NSString* string) {
|
||||
__block NSDate* date;
|
||||
dispatch_sync(_dateFormatterQueue, ^{
|
||||
date = [_dateFormatterISO8601 dateFromString:string];
|
||||
});
|
||||
return date;
|
||||
}
|
||||
|
||||
BOOL GCDWebServerIsTextContentType(NSString* type) {
|
||||
return ([type hasPrefix:@"text/"] || [type hasPrefix:@"application/json"] || [type hasPrefix:@"application/xml"]);
|
||||
}
|
||||
|
||||
NSString* GCDWebServerDescribeData(NSData* data, NSString* type) {
|
||||
if (GCDWebServerIsTextContentType(type)) {
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(type, @"charset");
|
||||
NSString* string = [[NSString alloc] initWithData:data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
if (string) {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
return [NSString stringWithFormat:@"<%lu bytes>", (unsigned long)data.length];
|
||||
}
|
||||
|
||||
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary<NSString*, NSString*>* overrides) {
|
||||
NSDictionary* builtInOverrides = @{@"css" : @"text/css"};
|
||||
NSString* mimeType = nil;
|
||||
extension = [extension lowercaseString];
|
||||
if (extension.length) {
|
||||
mimeType = [overrides objectForKey:extension];
|
||||
if (mimeType == nil) {
|
||||
mimeType = [builtInOverrides objectForKey:extension];
|
||||
}
|
||||
if (mimeType == nil) {
|
||||
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
|
||||
if (uti) {
|
||||
mimeType = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType));
|
||||
CFRelease(uti);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mimeType ? mimeType : kGCDWebServerDefaultMimeType;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerEscapeURLString(NSString* string) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":@/?&=+"), kCFStringEncodingUTF8));
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
NSString* GCDWebServerUnescapeURLString(NSString* string) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8));
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
NSDictionary<NSString*, NSString*>* GCDWebServerParseURLEncodedForm(NSString* form) {
|
||||
NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
|
||||
NSScanner* scanner = [[NSScanner alloc] initWithString:form];
|
||||
[scanner setCharactersToBeSkipped:nil];
|
||||
while (1) {
|
||||
NSString* key = nil;
|
||||
if (![scanner scanUpToString:@"=" intoString:&key] || [scanner isAtEnd]) {
|
||||
break;
|
||||
}
|
||||
[scanner setScanLocation:([scanner scanLocation] + 1)];
|
||||
|
||||
NSString* value = nil;
|
||||
[scanner scanUpToString:@"&" intoString:&value];
|
||||
if (value == nil) {
|
||||
value = @"";
|
||||
}
|
||||
|
||||
key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||
NSString* unescapedKey = key ? GCDWebServerUnescapeURLString(key) : nil;
|
||||
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||
NSString* unescapedValue = value ? GCDWebServerUnescapeURLString(value) : nil;
|
||||
if (unescapedKey && unescapedValue) {
|
||||
[parameters setObject:unescapedValue forKey:unescapedKey];
|
||||
} else {
|
||||
GWS_LOG_WARNING(@"Failed parsing URL encoded form for key \"%@\" and value \"%@\"", key, value);
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
|
||||
if ([scanner isAtEnd]) {
|
||||
break;
|
||||
}
|
||||
[scanner setScanLocation:([scanner scanLocation] + 1)];
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService) {
|
||||
char hostBuffer[NI_MAXHOST];
|
||||
char serviceBuffer[NI_MAXSERV];
|
||||
if (getnameinfo(addr, addr->sa_len, hostBuffer, sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN) != 0) {
|
||||
#if DEBUG
|
||||
GWS_DNOT_REACHED();
|
||||
#else
|
||||
return @"";
|
||||
#endif
|
||||
}
|
||||
return includeService ? [NSString stringWithFormat:@"%s:%s", hostBuffer, serviceBuffer] : (NSString*)[NSString stringWithUTF8String:hostBuffer];
|
||||
}
|
||||
|
||||
NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) {
|
||||
NSString* address = nil;
|
||||
#if TARGET_OS_IPHONE
|
||||
#if !TARGET_IPHONE_SIMULATOR && !TARGET_OS_TV
|
||||
const char* primaryInterface = "en0"; // WiFi interface on iOS
|
||||
#endif
|
||||
#else
|
||||
const char* primaryInterface = NULL;
|
||||
SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("GCDWebServer"), NULL, NULL);
|
||||
if (store) {
|
||||
CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4")); // There is no equivalent for IPv6 but the primary interface should be the same
|
||||
if (info) {
|
||||
NSString* interface = [(__bridge NSDictionary*)info objectForKey:@"PrimaryInterface"];
|
||||
if (interface) {
|
||||
primaryInterface = [[NSString stringWithString:interface] UTF8String]; // Copy string to auto-release pool
|
||||
}
|
||||
CFRelease(info);
|
||||
}
|
||||
CFRelease(store);
|
||||
}
|
||||
if (primaryInterface == NULL) {
|
||||
primaryInterface = "lo0";
|
||||
}
|
||||
#endif
|
||||
struct ifaddrs* list;
|
||||
if (getifaddrs(&list) >= 0) {
|
||||
for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) {
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_TV
|
||||
// Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator
|
||||
// Assumption holds for Apple TV running tvOS
|
||||
if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1"))
|
||||
#else
|
||||
if (strcmp(ifap->ifa_name, primaryInterface))
|
||||
#endif
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ((ifap->ifa_flags & IFF_UP) && ((!useIPv6 && (ifap->ifa_addr->sa_family == AF_INET)) || (useIPv6 && (ifap->ifa_addr->sa_family == AF_INET6)))) {
|
||||
address = GCDWebServerStringFromSockAddr(ifap->ifa_addr, NO);
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeifaddrs(list);
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
const char* string = [[[NSString alloc] initWithFormat:format arguments:arguments] UTF8String];
|
||||
va_end(arguments);
|
||||
unsigned char md5[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5(string, (CC_LONG)strlen(string), md5);
|
||||
char buffer[2 * CC_MD5_DIGEST_LENGTH + 1];
|
||||
for (int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) {
|
||||
unsigned char byte = md5[i];
|
||||
unsigned char byteHi = (byte & 0xF0) >> 4;
|
||||
buffer[2 * i + 0] = byteHi >= 10 ? 'a' + byteHi - 10 : '0' + byteHi;
|
||||
unsigned char byteLo = byte & 0x0F;
|
||||
buffer[2 * i + 1] = byteLo >= 10 ? 'a' + byteLo - 10 : '0' + byteLo;
|
||||
}
|
||||
buffer[2 * CC_MD5_DIGEST_LENGTH] = 0;
|
||||
return (NSString*)[NSString stringWithUTF8String:buffer];
|
||||
}
|
||||
|
||||
NSString* GCDWebServerNormalizePath(NSString* path) {
|
||||
NSMutableArray* components = [[NSMutableArray alloc] init];
|
||||
for (NSString* component in [path componentsSeparatedByString:@"/"]) {
|
||||
if ([component isEqualToString:@".."]) {
|
||||
[components removeLastObject];
|
||||
} else if (component.length && ![component isEqualToString:@"."]) {
|
||||
[components addObject:component];
|
||||
}
|
||||
}
|
||||
if (path.length && ([path characterAtIndex:0] == '/')) {
|
||||
return [@"/" stringByAppendingString:[components componentsJoinedByString:@"/"]]; // Preserve initial slash
|
||||
}
|
||||
return [components componentsJoinedByString:@"/"];
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
// http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* Convenience constants for "informational" HTTP status codes.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, GCDWebServerInformationalHTTPStatusCode) {
|
||||
kGCDWebServerHTTPStatusCode_Continue = 100,
|
||||
kGCDWebServerHTTPStatusCode_SwitchingProtocols = 101,
|
||||
kGCDWebServerHTTPStatusCode_Processing = 102
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience constants for "successful" HTTP status codes.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, GCDWebServerSuccessfulHTTPStatusCode) {
|
||||
kGCDWebServerHTTPStatusCode_OK = 200,
|
||||
kGCDWebServerHTTPStatusCode_Created = 201,
|
||||
kGCDWebServerHTTPStatusCode_Accepted = 202,
|
||||
kGCDWebServerHTTPStatusCode_NonAuthoritativeInformation = 203,
|
||||
kGCDWebServerHTTPStatusCode_NoContent = 204,
|
||||
kGCDWebServerHTTPStatusCode_ResetContent = 205,
|
||||
kGCDWebServerHTTPStatusCode_PartialContent = 206,
|
||||
kGCDWebServerHTTPStatusCode_MultiStatus = 207,
|
||||
kGCDWebServerHTTPStatusCode_AlreadyReported = 208
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience constants for "redirection" HTTP status codes.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, GCDWebServerRedirectionHTTPStatusCode) {
|
||||
kGCDWebServerHTTPStatusCode_MultipleChoices = 300,
|
||||
kGCDWebServerHTTPStatusCode_MovedPermanently = 301,
|
||||
kGCDWebServerHTTPStatusCode_Found = 302,
|
||||
kGCDWebServerHTTPStatusCode_SeeOther = 303,
|
||||
kGCDWebServerHTTPStatusCode_NotModified = 304,
|
||||
kGCDWebServerHTTPStatusCode_UseProxy = 305,
|
||||
kGCDWebServerHTTPStatusCode_TemporaryRedirect = 307,
|
||||
kGCDWebServerHTTPStatusCode_PermanentRedirect = 308
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience constants for "client error" HTTP status codes.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, GCDWebServerClientErrorHTTPStatusCode) {
|
||||
kGCDWebServerHTTPStatusCode_BadRequest = 400,
|
||||
kGCDWebServerHTTPStatusCode_Unauthorized = 401,
|
||||
kGCDWebServerHTTPStatusCode_PaymentRequired = 402,
|
||||
kGCDWebServerHTTPStatusCode_Forbidden = 403,
|
||||
kGCDWebServerHTTPStatusCode_NotFound = 404,
|
||||
kGCDWebServerHTTPStatusCode_MethodNotAllowed = 405,
|
||||
kGCDWebServerHTTPStatusCode_NotAcceptable = 406,
|
||||
kGCDWebServerHTTPStatusCode_ProxyAuthenticationRequired = 407,
|
||||
kGCDWebServerHTTPStatusCode_RequestTimeout = 408,
|
||||
kGCDWebServerHTTPStatusCode_Conflict = 409,
|
||||
kGCDWebServerHTTPStatusCode_Gone = 410,
|
||||
kGCDWebServerHTTPStatusCode_LengthRequired = 411,
|
||||
kGCDWebServerHTTPStatusCode_PreconditionFailed = 412,
|
||||
kGCDWebServerHTTPStatusCode_RequestEntityTooLarge = 413,
|
||||
kGCDWebServerHTTPStatusCode_RequestURITooLong = 414,
|
||||
kGCDWebServerHTTPStatusCode_UnsupportedMediaType = 415,
|
||||
kGCDWebServerHTTPStatusCode_RequestedRangeNotSatisfiable = 416,
|
||||
kGCDWebServerHTTPStatusCode_ExpectationFailed = 417,
|
||||
kGCDWebServerHTTPStatusCode_UnprocessableEntity = 422,
|
||||
kGCDWebServerHTTPStatusCode_Locked = 423,
|
||||
kGCDWebServerHTTPStatusCode_FailedDependency = 424,
|
||||
kGCDWebServerHTTPStatusCode_UpgradeRequired = 426,
|
||||
kGCDWebServerHTTPStatusCode_PreconditionRequired = 428,
|
||||
kGCDWebServerHTTPStatusCode_TooManyRequests = 429,
|
||||
kGCDWebServerHTTPStatusCode_RequestHeaderFieldsTooLarge = 431
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience constants for "server error" HTTP status codes.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, GCDWebServerServerErrorHTTPStatusCode) {
|
||||
kGCDWebServerHTTPStatusCode_InternalServerError = 500,
|
||||
kGCDWebServerHTTPStatusCode_NotImplemented = 501,
|
||||
kGCDWebServerHTTPStatusCode_BadGateway = 502,
|
||||
kGCDWebServerHTTPStatusCode_ServiceUnavailable = 503,
|
||||
kGCDWebServerHTTPStatusCode_GatewayTimeout = 504,
|
||||
kGCDWebServerHTTPStatusCode_HTTPVersionNotSupported = 505,
|
||||
kGCDWebServerHTTPStatusCode_InsufficientStorage = 507,
|
||||
kGCDWebServerHTTPStatusCode_LoopDetected = 508,
|
||||
kGCDWebServerHTTPStatusCode_NotExtended = 510,
|
||||
kGCDWebServerHTTPStatusCode_NetworkAuthenticationRequired = 511
|
||||
};
|
224
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerPrivate.h
Normal file
224
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerPrivate.h
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <os/object.h>
|
||||
#import <sys/socket.h>
|
||||
|
||||
/**
|
||||
* All GCDWebServer headers.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerHTTPStatusCodes.h"
|
||||
#import "GCDWebServerFunctions.h"
|
||||
|
||||
#import "GCDWebServer.h"
|
||||
#import "GCDWebServerConnection.h"
|
||||
|
||||
#import "GCDWebServerDataRequest.h"
|
||||
#import "GCDWebServerFileRequest.h"
|
||||
#import "GCDWebServerMultiPartFormRequest.h"
|
||||
#import "GCDWebServerURLEncodedFormRequest.h"
|
||||
|
||||
#import "GCDWebServerDataResponse.h"
|
||||
#import "GCDWebServerErrorResponse.h"
|
||||
#import "GCDWebServerFileResponse.h"
|
||||
#import "GCDWebServerStreamedResponse.h"
|
||||
|
||||
/**
|
||||
* Check if a custom logging facility should be used instead.
|
||||
*/
|
||||
|
||||
#if defined(__GCDWEBSERVER_LOGGING_HEADER__)
|
||||
|
||||
#define __GCDWEBSERVER_LOGGING_FACILITY_CUSTOM__
|
||||
|
||||
#import __GCDWEBSERVER_LOGGING_HEADER__
|
||||
|
||||
/**
|
||||
* Automatically detect if XLFacility is available and if so use it as a
|
||||
* logging facility.
|
||||
*/
|
||||
|
||||
#elif defined(__has_include) && __has_include("XLFacilityMacros.h")
|
||||
|
||||
#define __GCDWEBSERVER_LOGGING_FACILITY_XLFACILITY__
|
||||
|
||||
#undef XLOG_TAG
|
||||
#define XLOG_TAG @"gcdwebserver.internal"
|
||||
|
||||
#import "XLFacilityMacros.h"
|
||||
|
||||
#define GWS_LOG_DEBUG(...) XLOG_DEBUG(__VA_ARGS__)
|
||||
#define GWS_LOG_VERBOSE(...) XLOG_VERBOSE(__VA_ARGS__)
|
||||
#define GWS_LOG_INFO(...) XLOG_INFO(__VA_ARGS__)
|
||||
#define GWS_LOG_WARNING(...) XLOG_WARNING(__VA_ARGS__)
|
||||
#define GWS_LOG_ERROR(...) XLOG_ERROR(__VA_ARGS__)
|
||||
|
||||
#define GWS_DCHECK(__CONDITION__) XLOG_DEBUG_CHECK(__CONDITION__)
|
||||
#define GWS_DNOT_REACHED() XLOG_DEBUG_UNREACHABLE()
|
||||
|
||||
/**
|
||||
* If all of the above fail, then use GCDWebServer built-in
|
||||
* logging facility.
|
||||
*/
|
||||
|
||||
#else
|
||||
|
||||
#define __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__
|
||||
|
||||
typedef NS_ENUM(int, GCDWebServerLoggingLevel) {
|
||||
kGCDWebServerLoggingLevel_Debug = 0,
|
||||
kGCDWebServerLoggingLevel_Verbose,
|
||||
kGCDWebServerLoggingLevel_Info,
|
||||
kGCDWebServerLoggingLevel_Warning,
|
||||
kGCDWebServerLoggingLevel_Error
|
||||
};
|
||||
|
||||
extern GCDWebServerLoggingLevel GCDWebServerLogLevel;
|
||||
extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* _Nonnull format, ...) NS_FORMAT_FUNCTION(2, 3);
|
||||
|
||||
#if DEBUG
|
||||
#define GWS_LOG_DEBUG(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Debug) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Debug, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define GWS_LOG_DEBUG(...)
|
||||
#endif
|
||||
#define GWS_LOG_VERBOSE(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Verbose) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Verbose, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define GWS_LOG_INFO(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Info) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Info, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define GWS_LOG_WARNING(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Warning) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Warning, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define GWS_LOG_ERROR(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Error) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Error, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Consistency check macros used when building Debug only.
|
||||
*/
|
||||
|
||||
#if !defined(GWS_DCHECK) || !defined(GWS_DNOT_REACHED)
|
||||
|
||||
#if DEBUG
|
||||
|
||||
#define GWS_DCHECK(__CONDITION__) \
|
||||
do { \
|
||||
if (!(__CONDITION__)) { \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
#define GWS_DNOT_REACHED() abort()
|
||||
|
||||
#else
|
||||
|
||||
#define GWS_DCHECK(__CONDITION__)
|
||||
#define GWS_DNOT_REACHED()
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* GCDWebServer internal constants and APIs.
|
||||
*/
|
||||
|
||||
#define kGCDWebServerDefaultMimeType @"application/octet-stream"
|
||||
#define kGCDWebServerErrorDomain @"GCDWebServerErrorDomain"
|
||||
|
||||
static inline BOOL GCDWebServerIsValidByteRange(NSRange range) {
|
||||
return ((range.location != NSUIntegerMax) || (range.length > 0));
|
||||
}
|
||||
|
||||
static inline NSError* GCDWebServerMakePosixError(int code) {
|
||||
return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : (NSString*)[NSString stringWithUTF8String:strerror(code)]}];
|
||||
}
|
||||
|
||||
extern void GCDWebServerInitializeFunctions(void);
|
||||
extern NSString* _Nullable GCDWebServerNormalizeHeaderValue(NSString* _Nullable value);
|
||||
extern NSString* _Nullable GCDWebServerTruncateHeaderValue(NSString* _Nullable value);
|
||||
extern NSString* _Nullable GCDWebServerExtractHeaderValueParameter(NSString* _Nullable value, NSString* attribute);
|
||||
extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset);
|
||||
extern BOOL GCDWebServerIsTextContentType(NSString* type);
|
||||
extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
|
||||
extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2);
|
||||
extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService);
|
||||
|
||||
@interface GCDWebServerConnection ()
|
||||
- (instancetype)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket;
|
||||
@end
|
||||
|
||||
@interface GCDWebServer ()
|
||||
@property(nonatomic, readonly) NSMutableArray<GCDWebServerHandler*>* handlers;
|
||||
@property(nonatomic, readonly, nullable) NSString* serverName;
|
||||
@property(nonatomic, readonly, nullable) NSString* authenticationRealm;
|
||||
@property(nonatomic, readonly, nullable) NSMutableDictionary<NSString*, NSString*>* authenticationBasicAccounts;
|
||||
@property(nonatomic, readonly, nullable) NSMutableDictionary<NSString*, NSString*>* authenticationDigestAccounts;
|
||||
@property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET;
|
||||
@property(nonatomic, readonly) dispatch_queue_priority_t dispatchQueuePriority;
|
||||
- (void)willStartConnection:(GCDWebServerConnection*)connection;
|
||||
- (void)didEndConnection:(GCDWebServerConnection*)connection;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerHandler : NSObject
|
||||
@property(nonatomic, readonly) GCDWebServerMatchBlock matchBlock;
|
||||
@property(nonatomic, readonly) GCDWebServerAsyncProcessBlock asyncProcessBlock;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerRequest ()
|
||||
@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
|
||||
@property(nonatomic) NSData* localAddressData;
|
||||
@property(nonatomic) NSData* remoteAddressData;
|
||||
- (void)prepareForWriting;
|
||||
- (BOOL)performOpen:(NSError**)error;
|
||||
- (BOOL)performWriteData:(NSData*)data error:(NSError**)error;
|
||||
- (BOOL)performClose:(NSError**)error;
|
||||
- (void)setAttribute:(nullable id)attribute forKey:(NSString*)key;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerResponse ()
|
||||
@property(nonatomic, readonly) NSDictionary<NSString*, NSString*>* additionalHeaders;
|
||||
@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
|
||||
- (void)prepareForReading;
|
||||
- (BOOL)performOpen:(NSError**)error;
|
||||
- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block;
|
||||
- (void)performClose;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
210
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerRequest.h
Normal file
210
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerRequest.h
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* Attribute key to retrieve an NSArray containing NSStrings from a GCDWebServerRequest
|
||||
* with the contents of any regular expression captures done on the request path.
|
||||
*
|
||||
* @warning This attribute will only be set on the request if adding a handler using
|
||||
* -addHandlerForMethod:pathRegex:requestClass:processBlock:.
|
||||
*/
|
||||
extern NSString* const GCDWebServerRequestAttribute_RegexCaptures;
|
||||
|
||||
/**
|
||||
* This protocol is used by the GCDWebServerConnection to communicate with
|
||||
* the GCDWebServerRequest and write the received HTTP body data.
|
||||
*
|
||||
* Note that multiple GCDWebServerBodyWriter objects can be chained together
|
||||
* internally e.g. to automatically decode gzip encoded content before
|
||||
* passing it on to the GCDWebServerRequest.
|
||||
*
|
||||
* @warning These methods can be called on any GCD thread.
|
||||
*/
|
||||
@protocol GCDWebServerBodyWriter <NSObject>
|
||||
|
||||
/**
|
||||
* This method is called before any body data is received.
|
||||
*
|
||||
* It should return YES on success or NO on failure and set the "error" argument
|
||||
* which is guaranteed to be non-NULL.
|
||||
*/
|
||||
- (BOOL)open:(NSError**)error;
|
||||
|
||||
/**
|
||||
* This method is called whenever body data has been received.
|
||||
*
|
||||
* It should return YES on success or NO on failure and set the "error" argument
|
||||
* which is guaranteed to be non-NULL.
|
||||
*/
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error;
|
||||
|
||||
/**
|
||||
* This method is called after all body data has been received.
|
||||
*
|
||||
* It should return YES on success or NO on failure and set the "error" argument
|
||||
* which is guaranteed to be non-NULL.
|
||||
*/
|
||||
- (BOOL)close:(NSError**)error;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebServerRequest class is instantiated by the GCDWebServerConnection
|
||||
* after the HTTP headers have been received. Each instance wraps a single HTTP
|
||||
* request. If a body is present, the methods from the GCDWebServerBodyWriter
|
||||
* protocol will be called by the GCDWebServerConnection to receive it.
|
||||
*
|
||||
* The default implementation of the GCDWebServerBodyWriter protocol on the class
|
||||
* simply ignores the body data.
|
||||
*
|
||||
* @warning GCDWebServerRequest instances can be created and used on any GCD thread.
|
||||
*/
|
||||
@interface GCDWebServerRequest : NSObject <GCDWebServerBodyWriter>
|
||||
|
||||
/**
|
||||
* Returns the HTTP method for the request.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* method;
|
||||
|
||||
/**
|
||||
* Returns the URL for the request.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSURL* URL;
|
||||
|
||||
/**
|
||||
* Returns the HTTP headers for the request.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSDictionary<NSString*, NSString*>* headers;
|
||||
|
||||
/**
|
||||
* Returns the path component of the URL for the request.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* path;
|
||||
|
||||
/**
|
||||
* Returns the parsed and unescaped query component of the URL for the request.
|
||||
*
|
||||
* @warning This property will be nil if there is no query in the URL.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDictionary<NSString*, NSString*>* query;
|
||||
|
||||
/**
|
||||
* Returns the content type for the body of the request parsed from the
|
||||
* "Content-Type" header.
|
||||
*
|
||||
* This property will be nil if the request has no body or set to
|
||||
* "application/octet-stream" if a body is present but there was no
|
||||
* "Content-Type" header.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString* contentType;
|
||||
|
||||
/**
|
||||
* Returns the content length for the body of the request parsed from the
|
||||
* "Content-Length" header.
|
||||
*
|
||||
* This property will be set to "NSUIntegerMax" if the request has no body or
|
||||
* if there is a body but no "Content-Length" header, typically because
|
||||
* chunked transfer encoding is used.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSUInteger contentLength;
|
||||
|
||||
/**
|
||||
* Returns the parsed "If-Modified-Since" header or nil if absent or malformed.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDate* ifModifiedSince;
|
||||
|
||||
/**
|
||||
* Returns the parsed "If-None-Match" header or nil if absent or malformed.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString* ifNoneMatch;
|
||||
|
||||
/**
|
||||
* Returns the parsed "Range" header or (NSUIntegerMax, 0) if absent or malformed.
|
||||
* The range will be set to (offset, length) if expressed from the beginning
|
||||
* of the entity body, or (NSUIntegerMax, length) if expressed from its end.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSRange byteRange;
|
||||
|
||||
/**
|
||||
* Returns YES if the client supports gzip content encoding according to the
|
||||
* "Accept-Encoding" header.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL acceptsGzipContentEncoding;
|
||||
|
||||
/**
|
||||
* Returns the address of the local peer (i.e. server) for the request
|
||||
* as a raw "struct sockaddr".
|
||||
*/
|
||||
@property(nonatomic, readonly) NSData* localAddressData;
|
||||
|
||||
/**
|
||||
* Returns the address of the local peer (i.e. server) for the request
|
||||
* as a string.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* localAddressString;
|
||||
|
||||
/**
|
||||
* Returns the address of the remote peer (i.e. client) for the request
|
||||
* as a raw "struct sockaddr".
|
||||
*/
|
||||
@property(nonatomic, readonly) NSData* remoteAddressData;
|
||||
|
||||
/**
|
||||
* Returns the address of the remote peer (i.e. client) for the request
|
||||
* as a string.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* remoteAddressString;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*/
|
||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary<NSString*, NSString*>*)headers path:(NSString*)path query:(nullable NSDictionary<NSString*, NSString*>*)query;
|
||||
|
||||
/**
|
||||
* Convenience method that checks if the contentType property is defined.
|
||||
*/
|
||||
- (BOOL)hasBody;
|
||||
|
||||
/**
|
||||
* Convenience method that checks if the byteRange property is defined.
|
||||
*/
|
||||
- (BOOL)hasByteRange;
|
||||
|
||||
/**
|
||||
* Retrieves an attribute associated with this request using the given key.
|
||||
*
|
||||
* @return The attribute value for the key.
|
||||
*/
|
||||
- (nullable id)attributeForKey:(NSString*)key;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
303
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerRequest.m
Normal file
303
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerRequest.m
Normal file
@ -0,0 +1,303 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <zlib.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerRequestAttribute_RegexCaptures";
|
||||
|
||||
#define kZlibErrorDomain @"ZlibErrorDomain"
|
||||
#define kGZipInitialBufferSize (256 * 1024)
|
||||
|
||||
@interface GCDWebServerBodyDecoder : NSObject <GCDWebServerBodyWriter>
|
||||
@end
|
||||
|
||||
@interface GCDWebServerGZipDecoder : GCDWebServerBodyDecoder
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerBodyDecoder {
|
||||
GCDWebServerRequest* __unsafe_unretained _request;
|
||||
id<GCDWebServerBodyWriter> __unsafe_unretained _writer;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRequest:(GCDWebServerRequest* _Nonnull)request writer:(id<GCDWebServerBodyWriter> _Nonnull)writer {
|
||||
if ((self = [super init])) {
|
||||
_request = request;
|
||||
_writer = writer;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
return [_writer open:error];
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
return [_writer writeData:data error:error];
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
return [_writer close:error];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerGZipDecoder {
|
||||
z_stream _stream;
|
||||
BOOL _finished;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
int result = inflateInit2(&_stream, 15 + 16);
|
||||
if (result != Z_OK) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
if (![super open:error]) {
|
||||
inflateEnd(&_stream);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
GWS_DCHECK(!_finished);
|
||||
_stream.next_in = (Bytef*)data.bytes;
|
||||
_stream.avail_in = (uInt)data.length;
|
||||
NSMutableData* decodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize];
|
||||
if (decodedData == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
return NO;
|
||||
}
|
||||
NSUInteger length = 0;
|
||||
while (1) {
|
||||
NSUInteger maxLength = decodedData.length - length;
|
||||
_stream.next_out = (Bytef*)((char*)decodedData.mutableBytes + length);
|
||||
_stream.avail_out = (uInt)maxLength;
|
||||
int result = inflate(&_stream, Z_NO_FLUSH);
|
||||
if ((result != Z_OK) && (result != Z_STREAM_END)) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
length += maxLength - _stream.avail_out;
|
||||
if (_stream.avail_out > 0) {
|
||||
if (result == Z_STREAM_END) {
|
||||
_finished = YES;
|
||||
}
|
||||
break;
|
||||
}
|
||||
decodedData.length = 2 * decodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available
|
||||
}
|
||||
decodedData.length = length;
|
||||
BOOL success = length ? [super writeData:decodedData error:error] : YES; // No need to call writer if we have no data yet
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
GWS_DCHECK(_finished);
|
||||
inflateEnd(&_stream);
|
||||
return [super close:error];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerRequest {
|
||||
BOOL _opened;
|
||||
NSMutableArray<GCDWebServerBodyDecoder*>* _decoders;
|
||||
id<GCDWebServerBodyWriter> __unsafe_unretained _writer;
|
||||
NSMutableDictionary<NSString*, id>* _attributes;
|
||||
}
|
||||
|
||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary<NSString*, NSString*>*)headers path:(NSString*)path query:(NSDictionary<NSString*, NSString*>*)query {
|
||||
if ((self = [super init])) {
|
||||
_method = [method copy];
|
||||
_URL = url;
|
||||
_headers = headers;
|
||||
_path = [path copy];
|
||||
_query = query;
|
||||
|
||||
_contentType = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]);
|
||||
_usesChunkedTransferEncoding = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"];
|
||||
NSString* lengthHeader = [_headers objectForKey:@"Content-Length"];
|
||||
if (lengthHeader) {
|
||||
NSInteger length = [lengthHeader integerValue];
|
||||
if (_usesChunkedTransferEncoding || (length < 0)) {
|
||||
GWS_LOG_WARNING(@"Invalid 'Content-Length' header '%@' for '%@' request on \"%@\"", lengthHeader, _method, _URL);
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
_contentLength = length;
|
||||
if (_contentType == nil) {
|
||||
_contentType = kGCDWebServerDefaultMimeType;
|
||||
}
|
||||
} else if (_usesChunkedTransferEncoding) {
|
||||
if (_contentType == nil) {
|
||||
_contentType = kGCDWebServerDefaultMimeType;
|
||||
}
|
||||
_contentLength = NSUIntegerMax;
|
||||
} else {
|
||||
if (_contentType) {
|
||||
GWS_LOG_WARNING(@"Ignoring 'Content-Type' header for '%@' request on \"%@\"", _method, _URL);
|
||||
_contentType = nil; // Content-Type without Content-Length or chunked-encoding doesn't make sense
|
||||
}
|
||||
_contentLength = NSUIntegerMax;
|
||||
}
|
||||
|
||||
NSString* modifiedHeader = [_headers objectForKey:@"If-Modified-Since"];
|
||||
if (modifiedHeader) {
|
||||
_ifModifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy];
|
||||
}
|
||||
_ifNoneMatch = [_headers objectForKey:@"If-None-Match"];
|
||||
|
||||
_byteRange = NSMakeRange(NSUIntegerMax, 0);
|
||||
NSString* rangeHeader = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Range"]);
|
||||
if (rangeHeader) {
|
||||
if ([rangeHeader hasPrefix:@"bytes="]) {
|
||||
NSArray* components = [[rangeHeader substringFromIndex:6] componentsSeparatedByString:@","];
|
||||
if (components.count == 1) {
|
||||
components = [(NSString*)[components firstObject] componentsSeparatedByString:@"-"];
|
||||
if (components.count == 2) {
|
||||
NSString* startString = [components objectAtIndex:0];
|
||||
NSInteger startValue = [startString integerValue];
|
||||
NSString* endString = [components objectAtIndex:1];
|
||||
NSInteger endValue = [endString integerValue];
|
||||
if (startString.length && (startValue >= 0) && endString.length && (endValue >= startValue)) { // The second 500 bytes: "500-999"
|
||||
_byteRange.location = startValue;
|
||||
_byteRange.length = endValue - startValue + 1;
|
||||
} else if (startString.length && (startValue >= 0)) { // The bytes after 9500 bytes: "9500-"
|
||||
_byteRange.location = startValue;
|
||||
_byteRange.length = NSUIntegerMax;
|
||||
} else if (endString.length && (endValue > 0)) { // The final 500 bytes: "-500"
|
||||
_byteRange.location = NSUIntegerMax;
|
||||
_byteRange.length = endValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((_byteRange.location == NSUIntegerMax) && (_byteRange.length == 0)) { // Ignore "Range" header if syntactically invalid
|
||||
GWS_LOG_WARNING(@"Failed to parse 'Range' header \"%@\" for url: %@", rangeHeader, url);
|
||||
}
|
||||
}
|
||||
|
||||
if ([[_headers objectForKey:@"Accept-Encoding"] rangeOfString:@"gzip"].location != NSNotFound) {
|
||||
_acceptsGzipContentEncoding = YES;
|
||||
}
|
||||
|
||||
_decoders = [[NSMutableArray alloc] init];
|
||||
_attributes = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)hasBody {
|
||||
return _contentType ? YES : NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasByteRange {
|
||||
return GCDWebServerIsValidByteRange(_byteRange);
|
||||
}
|
||||
|
||||
- (id)attributeForKey:(NSString*)key {
|
||||
return [_attributes objectForKey:key];
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)prepareForWriting {
|
||||
_writer = self;
|
||||
if ([GCDWebServerNormalizeHeaderValue([self.headers objectForKey:@"Content-Encoding"]) isEqualToString:@"gzip"]) {
|
||||
GCDWebServerGZipDecoder* decoder = [[GCDWebServerGZipDecoder alloc] initWithRequest:self writer:_writer];
|
||||
[_decoders addObject:decoder];
|
||||
_writer = decoder;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)performOpen:(NSError**)error {
|
||||
GWS_DCHECK(_contentType);
|
||||
GWS_DCHECK(_writer);
|
||||
if (_opened) {
|
||||
GWS_DNOT_REACHED();
|
||||
return NO;
|
||||
}
|
||||
_opened = YES;
|
||||
return [_writer open:error];
|
||||
}
|
||||
|
||||
- (BOOL)performWriteData:(NSData*)data error:(NSError**)error {
|
||||
GWS_DCHECK(_opened);
|
||||
return [_writer writeData:data error:error];
|
||||
}
|
||||
|
||||
- (BOOL)performClose:(NSError**)error {
|
||||
GWS_DCHECK(_opened);
|
||||
return [_writer close:error];
|
||||
}
|
||||
|
||||
- (void)setAttribute:(id)attribute forKey:(NSString*)key {
|
||||
[_attributes setValue:attribute forKey:key];
|
||||
}
|
||||
|
||||
- (NSString*)localAddressString {
|
||||
return GCDWebServerStringFromSockAddr(_localAddressData.bytes, YES);
|
||||
}
|
||||
|
||||
- (NSString*)remoteAddressString {
|
||||
return GCDWebServerStringFromSockAddr(_remoteAddressData.bytes, YES);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithFormat:@"%@ %@", _method, _path];
|
||||
for (NSString* argument in [[_query allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
||||
[description appendFormat:@"\n %@ = %@", argument, [_query objectForKey:argument]];
|
||||
}
|
||||
[description appendString:@"\n"];
|
||||
for (NSString* header in [[_headers allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
||||
[description appendFormat:@"\n%@: %@", header, [_headers objectForKey:header]];
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
212
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerResponse.h
Normal file
212
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerResponse.h
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerBodyReaderCompletionBlock is passed by GCDWebServer to the
|
||||
* GCDWebServerBodyReader object when reading data from it asynchronously.
|
||||
*/
|
||||
typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* _Nullable error);
|
||||
|
||||
/**
|
||||
* This protocol is used by the GCDWebServerConnection to communicate with
|
||||
* the GCDWebServerResponse and read the HTTP body data to send.
|
||||
*
|
||||
* Note that multiple GCDWebServerBodyReader objects can be chained together
|
||||
* internally e.g. to automatically apply gzip encoding to the content before
|
||||
* passing it on to the GCDWebServerResponse.
|
||||
*
|
||||
* @warning These methods can be called on any GCD thread.
|
||||
*/
|
||||
@protocol GCDWebServerBodyReader <NSObject>
|
||||
|
||||
@required
|
||||
|
||||
/**
|
||||
* This method is called before any body data is sent.
|
||||
*
|
||||
* It should return YES on success or NO on failure and set the "error" argument
|
||||
* which is guaranteed to be non-NULL.
|
||||
*/
|
||||
- (BOOL)open:(NSError**)error;
|
||||
|
||||
/**
|
||||
* This method is called whenever body data is sent.
|
||||
*
|
||||
* It should return a non-empty NSData if there is body data available,
|
||||
* or an empty NSData there is no more body data, or nil on error and set
|
||||
* the "error" argument which is guaranteed to be non-NULL.
|
||||
*/
|
||||
- (nullable NSData*)readData:(NSError**)error;
|
||||
|
||||
/**
|
||||
* This method is called after all body data has been sent.
|
||||
*/
|
||||
- (void)close;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* If this method is implemented, it will be preferred over -readData:.
|
||||
*
|
||||
* It must call the passed block when data is available, passing a non-empty
|
||||
* NSData if there is body data available, or an empty NSData there is no more
|
||||
* body data, or nil on error and pass an NSError along.
|
||||
*/
|
||||
- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebServerResponse class is used to wrap a single HTTP response.
|
||||
* It is instantiated by the handler of the GCDWebServer that handled the request.
|
||||
* If a body is present, the methods from the GCDWebServerBodyReader protocol
|
||||
* will be called by the GCDWebServerConnection to send it.
|
||||
*
|
||||
* The default implementation of the GCDWebServerBodyReader protocol
|
||||
* on the class simply returns an empty body.
|
||||
*
|
||||
* @warning GCDWebServerResponse instances can be created and used on any GCD thread.
|
||||
*/
|
||||
@interface GCDWebServerResponse : NSObject <GCDWebServerBodyReader>
|
||||
|
||||
/**
|
||||
* Sets the content type for the body of the response.
|
||||
*
|
||||
* The default value is nil i.e. the response has no body.
|
||||
*
|
||||
* @warning This property must be set if a body is present.
|
||||
*/
|
||||
@property(nonatomic, copy, nullable) NSString* contentType;
|
||||
|
||||
/**
|
||||
* Sets the content length for the body of the response. If a body is present
|
||||
* but this property is set to "NSUIntegerMax", this means the length of the body
|
||||
* cannot be known ahead of time. Chunked transfer encoding will be
|
||||
* automatically enabled by the GCDWebServerConnection to comply with HTTP/1.1
|
||||
* specifications.
|
||||
*
|
||||
* The default value is "NSUIntegerMax" i.e. the response has no body or its length
|
||||
* is undefined.
|
||||
*/
|
||||
@property(nonatomic) NSUInteger contentLength;
|
||||
|
||||
/**
|
||||
* Sets the HTTP status code for the response.
|
||||
*
|
||||
* The default value is 200 i.e. "OK".
|
||||
*/
|
||||
@property(nonatomic) NSInteger statusCode;
|
||||
|
||||
/**
|
||||
* Sets the caching hint for the response using the "Cache-Control" header.
|
||||
* This value is expressed in seconds.
|
||||
*
|
||||
* The default value is 0 i.e. "no-cache".
|
||||
*/
|
||||
@property(nonatomic) NSUInteger cacheControlMaxAge;
|
||||
|
||||
/**
|
||||
* Sets the last modified date for the response using the "Last-Modified" header.
|
||||
*
|
||||
* The default value is nil.
|
||||
*/
|
||||
@property(nonatomic, nullable) NSDate* lastModifiedDate;
|
||||
|
||||
/**
|
||||
* Sets the ETag for the response using the "ETag" header.
|
||||
*
|
||||
* The default value is nil.
|
||||
*/
|
||||
@property(nonatomic, copy, nullable) NSString* eTag;
|
||||
|
||||
/**
|
||||
* Enables gzip encoding for the response body.
|
||||
*
|
||||
* The default value is NO.
|
||||
*
|
||||
* @warning Enabling gzip encoding will remove any "Content-Length" header
|
||||
* since the length of the body is not known anymore. The client will still
|
||||
* be able to determine the body length when connection is closed per
|
||||
* HTTP/1.1 specifications.
|
||||
*/
|
||||
@property(nonatomic, getter=isGZipContentEncodingEnabled) BOOL gzipContentEncodingEnabled;
|
||||
|
||||
/**
|
||||
* Creates an empty response.
|
||||
*/
|
||||
+ (instancetype)response;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
* Sets an additional HTTP header on the response.
|
||||
* Pass a nil value to remove an additional header.
|
||||
*
|
||||
* @warning Do not attempt to override the primary headers used
|
||||
* by GCDWebServerResponse like "Content-Type", "ETag", etc...
|
||||
*/
|
||||
- (void)setValue:(nullable NSString*)value forAdditionalHeader:(NSString*)header;
|
||||
|
||||
/**
|
||||
* Convenience method that checks if the contentType property is defined.
|
||||
*/
|
||||
- (BOOL)hasBody;
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerResponse (Extensions)
|
||||
|
||||
/**
|
||||
* Creates a empty response with a specific HTTP status code.
|
||||
*/
|
||||
+ (instancetype)responseWithStatusCode:(NSInteger)statusCode;
|
||||
|
||||
/**
|
||||
* Creates an HTTP redirect response to a new URL.
|
||||
*/
|
||||
+ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
|
||||
|
||||
/**
|
||||
* Initializes an empty response with a specific HTTP status code.
|
||||
*/
|
||||
- (instancetype)initWithStatusCode:(NSInteger)statusCode;
|
||||
|
||||
/**
|
||||
* Initializes an HTTP redirect response to a new URL.
|
||||
*/
|
||||
- (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
284
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerResponse.m
Normal file
284
pkg/apple/WebServer/GCDWebServer/Core/GCDWebServerResponse.m
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <zlib.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kZlibErrorDomain @"ZlibErrorDomain"
|
||||
#define kGZipInitialBufferSize (256 * 1024)
|
||||
|
||||
@interface GCDWebServerBodyEncoder : NSObject <GCDWebServerBodyReader>
|
||||
@end
|
||||
|
||||
@interface GCDWebServerGZipEncoder : GCDWebServerBodyEncoder
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerBodyEncoder {
|
||||
GCDWebServerResponse* __unsafe_unretained _response;
|
||||
id<GCDWebServerBodyReader> __unsafe_unretained _reader;
|
||||
}
|
||||
|
||||
- (instancetype)initWithResponse:(GCDWebServerResponse* _Nonnull)response reader:(id<GCDWebServerBodyReader> _Nonnull)reader {
|
||||
if ((self = [super init])) {
|
||||
_response = response;
|
||||
_reader = reader;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
return [_reader open:error];
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
return [_reader readData:error];
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
[_reader close];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerGZipEncoder {
|
||||
z_stream _stream;
|
||||
BOOL _finished;
|
||||
}
|
||||
|
||||
- (instancetype)initWithResponse:(GCDWebServerResponse* _Nonnull)response reader:(id<GCDWebServerBodyReader> _Nonnull)reader {
|
||||
if ((self = [super initWithResponse:response reader:reader])) {
|
||||
response.contentLength = NSUIntegerMax; // Make sure "Content-Length" header is not set since we don't know it
|
||||
[response setValue:@"gzip" forAdditionalHeader:@"Content-Encoding"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
int result = deflateInit2(&_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
||||
if (result != Z_OK) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
if (![super open:error]) {
|
||||
deflateEnd(&_stream);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
NSMutableData* encodedData;
|
||||
if (_finished) {
|
||||
encodedData = [[NSMutableData alloc] init];
|
||||
} else {
|
||||
encodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize];
|
||||
if (encodedData == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
NSUInteger length = 0;
|
||||
do {
|
||||
NSData* data = [super readData:error];
|
||||
if (data == nil) {
|
||||
return nil;
|
||||
}
|
||||
_stream.next_in = (Bytef*)data.bytes;
|
||||
_stream.avail_in = (uInt)data.length;
|
||||
while (1) {
|
||||
NSUInteger maxLength = encodedData.length - length;
|
||||
_stream.next_out = (Bytef*)((char*)encodedData.mutableBytes + length);
|
||||
_stream.avail_out = (uInt)maxLength;
|
||||
int result = deflate(&_stream, data.length ? Z_NO_FLUSH : Z_FINISH);
|
||||
if (result == Z_STREAM_END) {
|
||||
_finished = YES;
|
||||
} else if (result != Z_OK) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
length += maxLength - _stream.avail_out;
|
||||
if (_stream.avail_out > 0) {
|
||||
break;
|
||||
}
|
||||
encodedData.length = 2 * encodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available
|
||||
}
|
||||
GWS_DCHECK(_stream.avail_in == 0);
|
||||
} while (length == 0); // Make sure we don't return an empty NSData if not in finished state
|
||||
encodedData.length = length;
|
||||
}
|
||||
return encodedData;
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
deflateEnd(&_stream);
|
||||
[super close];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerResponse {
|
||||
BOOL _opened;
|
||||
NSMutableArray<GCDWebServerBodyEncoder*>* _encoders;
|
||||
id<GCDWebServerBodyReader> __unsafe_unretained _reader;
|
||||
}
|
||||
|
||||
+ (instancetype)response {
|
||||
return [(GCDWebServerResponse*)[[self class] alloc] init];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if ((self = [super init])) {
|
||||
_contentType = nil;
|
||||
_contentLength = NSUIntegerMax;
|
||||
_statusCode = kGCDWebServerHTTPStatusCode_OK;
|
||||
_cacheControlMaxAge = 0;
|
||||
_additionalHeaders = [[NSMutableDictionary alloc] init];
|
||||
_encoders = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header {
|
||||
[_additionalHeaders setValue:value forKey:header];
|
||||
}
|
||||
|
||||
- (BOOL)hasBody {
|
||||
return _contentType ? YES : NO;
|
||||
}
|
||||
|
||||
- (BOOL)usesChunkedTransferEncoding {
|
||||
return (_contentType != nil) && (_contentLength == NSUIntegerMax);
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
return [NSData data];
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
;
|
||||
}
|
||||
|
||||
- (void)prepareForReading {
|
||||
_reader = self;
|
||||
if (_gzipContentEncodingEnabled) {
|
||||
GCDWebServerGZipEncoder* encoder = [[GCDWebServerGZipEncoder alloc] initWithResponse:self reader:_reader];
|
||||
[_encoders addObject:encoder];
|
||||
_reader = encoder;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)performOpen:(NSError**)error {
|
||||
GWS_DCHECK(_contentType);
|
||||
GWS_DCHECK(_reader);
|
||||
if (_opened) {
|
||||
GWS_DNOT_REACHED();
|
||||
return NO;
|
||||
}
|
||||
_opened = YES;
|
||||
return [_reader open:error];
|
||||
}
|
||||
|
||||
- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block {
|
||||
GWS_DCHECK(_opened);
|
||||
if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) {
|
||||
[_reader asyncReadDataWithCompletion:[block copy]];
|
||||
} else {
|
||||
NSError* error = nil;
|
||||
NSData* data = [_reader readData:&error];
|
||||
block(data, error);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)performClose {
|
||||
GWS_DCHECK(_opened);
|
||||
[_reader close];
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithFormat:@"Status Code = %i", (int)_statusCode];
|
||||
if (_contentType) {
|
||||
[description appendFormat:@"\nContent Type = %@", _contentType];
|
||||
}
|
||||
if (_contentLength != NSUIntegerMax) {
|
||||
[description appendFormat:@"\nContent Length = %lu", (unsigned long)_contentLength];
|
||||
}
|
||||
[description appendFormat:@"\nCache Control Max Age = %lu", (unsigned long)_cacheControlMaxAge];
|
||||
if (_lastModifiedDate) {
|
||||
[description appendFormat:@"\nLast Modified Date = %@", _lastModifiedDate];
|
||||
}
|
||||
if (_eTag) {
|
||||
[description appendFormat:@"\nETag = %@", _eTag];
|
||||
}
|
||||
if (_additionalHeaders.count) {
|
||||
[description appendString:@"\n"];
|
||||
for (NSString* header in [[_additionalHeaders allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
||||
[description appendFormat:@"\n%@: %@", header, [_additionalHeaders objectForKey:header]];
|
||||
}
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerResponse (Extensions)
|
||||
|
||||
+ (instancetype)responseWithStatusCode:(NSInteger)statusCode {
|
||||
return [(GCDWebServerResponse*)[self alloc] initWithStatusCode:statusCode];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent {
|
||||
return [(GCDWebServerResponse*)[self alloc] initWithRedirect:location permanent:permanent];
|
||||
}
|
||||
|
||||
- (instancetype)initWithStatusCode:(NSInteger)statusCode {
|
||||
if ((self = [self init])) {
|
||||
self.statusCode = statusCode;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent {
|
||||
if ((self = [self init])) {
|
||||
self.statusCode = permanent ? kGCDWebServerHTTPStatusCode_MovedPermanently : kGCDWebServerHTTPStatusCode_TemporaryRedirect;
|
||||
[self setValue:[location absoluteString] forAdditionalHeader:@"Location"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerDataRequest subclass of GCDWebServerRequest stores the body
|
||||
* of the HTTP request in memory.
|
||||
*/
|
||||
@interface GCDWebServerDataRequest : GCDWebServerRequest
|
||||
|
||||
/**
|
||||
* Returns the data for the request body.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSData* data;
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerDataRequest (Extensions)
|
||||
|
||||
/**
|
||||
* Returns the data for the request body interpreted as text. If the content
|
||||
* type of the body is not a text one, or if an error occurs, nil is returned.
|
||||
*
|
||||
* The text encoding used to interpret the data is extracted from the
|
||||
* "Content-Type" header or defaults to UTF-8.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString* text;
|
||||
|
||||
/**
|
||||
* Returns the data for the request body interpreted as a JSON object. If the
|
||||
* content type of the body is not JSON, or if an error occurs, nil is returned.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) id jsonObject;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerDataRequest ()
|
||||
@property(nonatomic) NSMutableData* data;
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerDataRequest {
|
||||
NSString* _text;
|
||||
id _jsonObject;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
if (self.contentLength != NSUIntegerMax) {
|
||||
_data = [[NSMutableData alloc] initWithCapacity:self.contentLength];
|
||||
} else {
|
||||
_data = [[NSMutableData alloc] init];
|
||||
}
|
||||
if (_data == nil) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Failed allocating memory"}];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
[_data appendData:data];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
if (_data) {
|
||||
[description appendString:@"\n\n"];
|
||||
[description appendString:GCDWebServerDescribeData(_data, (NSString*)self.contentType)];
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerDataRequest (Extensions)
|
||||
|
||||
- (NSString*)text {
|
||||
if (_text == nil) {
|
||||
if ([self.contentType hasPrefix:@"text/"]) {
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset");
|
||||
_text = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
}
|
||||
return _text;
|
||||
}
|
||||
|
||||
- (id)jsonObject {
|
||||
if (_jsonObject == nil) {
|
||||
NSString* mimeType = GCDWebServerTruncateHeaderValue(self.contentType);
|
||||
if ([mimeType isEqualToString:@"application/json"] || [mimeType isEqualToString:@"text/json"] || [mimeType isEqualToString:@"text/javascript"]) {
|
||||
_jsonObject = [NSJSONSerialization JSONObjectWithData:_data options:0 error:NULL];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
}
|
||||
return _jsonObject;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerFileRequest subclass of GCDWebServerRequest stores the body
|
||||
* of the HTTP request to a file on disk.
|
||||
*/
|
||||
@interface GCDWebServerFileRequest : GCDWebServerRequest
|
||||
|
||||
/**
|
||||
* Returns the path to the temporary file containing the request body.
|
||||
*
|
||||
* @warning This temporary file will be automatically deleted when the
|
||||
* GCDWebServerFileRequest is deallocated. If you want to preserve this file,
|
||||
* you must move it to a different location beforehand.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* temporaryPath;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@implementation GCDWebServerFileRequest {
|
||||
int _file;
|
||||
}
|
||||
|
||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary<NSString*, NSString*>*)headers path:(NSString*)path query:(NSDictionary<NSString*, NSString*>*)query {
|
||||
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
|
||||
_temporaryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
unlink([_temporaryPath fileSystemRepresentation]);
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
_file = open([_temporaryPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (_file <= 0) {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
if (write(_file, data.bytes, data.length) != (ssize_t)data.length) {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
if (close(_file) < 0) {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
NSString* creationDateHeader = [self.headers objectForKey:@"X-GCDWebServer-CreationDate"];
|
||||
if (creationDateHeader) {
|
||||
NSDate* date = GCDWebServerParseISO8601(creationDateHeader);
|
||||
if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileCreationDate : date} ofItemAtPath:_temporaryPath error:error]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
NSString* modifiedDateHeader = [self.headers objectForKey:@"X-GCDWebServer-ModifiedDate"];
|
||||
if (modifiedDateHeader) {
|
||||
NSDate* date = GCDWebServerParseRFC822(modifiedDateHeader);
|
||||
if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate : date} ofItemAtPath:_temporaryPath error:error]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
[description appendFormat:@"\n\n{%@}", _temporaryPath];
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerMultiPart class is an abstract class that wraps the content
|
||||
* of a part.
|
||||
*/
|
||||
@interface GCDWebServerMultiPart : NSObject
|
||||
|
||||
/**
|
||||
* Returns the control name retrieved from the part headers.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* controlName;
|
||||
|
||||
/**
|
||||
* Returns the content type retrieved from the part headers or "text/plain"
|
||||
* if not available (per HTTP specifications).
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* contentType;
|
||||
|
||||
/**
|
||||
* Returns the MIME type component of the content type for the part.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* mimeType;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebServerMultiPartArgument subclass of GCDWebServerMultiPart wraps
|
||||
* the content of a part as data in memory.
|
||||
*/
|
||||
@interface GCDWebServerMultiPartArgument : GCDWebServerMultiPart
|
||||
|
||||
/**
|
||||
* Returns the data for the part.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSData* data;
|
||||
|
||||
/**
|
||||
* Returns the data for the part interpreted as text. If the content
|
||||
* type of the part is not a text one, or if an error occurs, nil is returned.
|
||||
*
|
||||
* The text encoding used to interpret the data is extracted from the
|
||||
* "Content-Type" header or defaults to UTF-8.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString* string;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebServerMultiPartFile subclass of GCDWebServerMultiPart wraps
|
||||
* the content of a part as a file on disk.
|
||||
*/
|
||||
@interface GCDWebServerMultiPartFile : GCDWebServerMultiPart
|
||||
|
||||
/**
|
||||
* Returns the file name retrieved from the part headers.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* fileName;
|
||||
|
||||
/**
|
||||
* Returns the path to the temporary file containing the part data.
|
||||
*
|
||||
* @warning This temporary file will be automatically deleted when the
|
||||
* GCDWebServerMultiPartFile is deallocated. If you want to preserve this file,
|
||||
* you must move it to a different location beforehand.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* temporaryPath;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebServerMultiPartFormRequest subclass of GCDWebServerRequest
|
||||
* parses the body of the HTTP request as a multipart encoded form.
|
||||
*/
|
||||
@interface GCDWebServerMultiPartFormRequest : GCDWebServerRequest
|
||||
|
||||
/**
|
||||
* Returns the argument parts from the multipart encoded form as
|
||||
* name / GCDWebServerMultiPartArgument pairs.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray<GCDWebServerMultiPartArgument*>* arguments;
|
||||
|
||||
/**
|
||||
* Returns the files parts from the multipart encoded form as
|
||||
* name / GCDWebServerMultiPartFile pairs.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray<GCDWebServerMultiPartFile*>* files;
|
||||
|
||||
/**
|
||||
* Returns the MIME type for multipart encoded forms
|
||||
* i.e. "multipart/form-data".
|
||||
*/
|
||||
+ (NSString*)mimeType;
|
||||
|
||||
/**
|
||||
* Returns the first argument for a given control name or nil if not found.
|
||||
*/
|
||||
- (nullable GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name;
|
||||
|
||||
/**
|
||||
* Returns the first file for a given control name or nil if not found.
|
||||
*/
|
||||
- (nullable GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,405 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kMultiPartBufferSize (256 * 1024)
|
||||
|
||||
typedef enum {
|
||||
kParserState_Undefined = 0,
|
||||
kParserState_Start,
|
||||
kParserState_Headers,
|
||||
kParserState_Content,
|
||||
kParserState_End
|
||||
} ParserState;
|
||||
|
||||
@interface GCDWebServerMIMEStreamParser : NSObject
|
||||
@end
|
||||
|
||||
static NSData* _newlineData = nil;
|
||||
static NSData* _newlinesData = nil;
|
||||
static NSData* _dashNewlineData = nil;
|
||||
|
||||
@implementation GCDWebServerMultiPart
|
||||
|
||||
- (instancetype)initWithControlName:(NSString* _Nonnull)name contentType:(NSString* _Nonnull)type {
|
||||
if ((self = [super init])) {
|
||||
_controlName = [name copy];
|
||||
_contentType = [type copy];
|
||||
_mimeType = (NSString*)GCDWebServerTruncateHeaderValue(_contentType);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPartArgument
|
||||
|
||||
- (instancetype)initWithControlName:(NSString* _Nonnull)name contentType:(NSString* _Nonnull)type data:(NSData* _Nonnull)data {
|
||||
if ((self = [super initWithControlName:name contentType:type])) {
|
||||
_data = data;
|
||||
|
||||
if ([self.contentType hasPrefix:@"text/"]) {
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset");
|
||||
_string = [[NSString alloc] initWithData:_data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
return [NSString stringWithFormat:@"<%@ | '%@' | %lu bytes>", [self class], self.mimeType, (unsigned long)_data.length];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPartFile
|
||||
|
||||
- (instancetype)initWithControlName:(NSString* _Nonnull)name contentType:(NSString* _Nonnull)type fileName:(NSString* _Nonnull)fileName temporaryPath:(NSString* _Nonnull)temporaryPath {
|
||||
if ((self = [super initWithControlName:name contentType:type])) {
|
||||
_fileName = [fileName copy];
|
||||
_temporaryPath = [temporaryPath copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
unlink([_temporaryPath fileSystemRepresentation]);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
return [NSString stringWithFormat:@"<%@ | '%@' | '%@>'", [self class], self.mimeType, _fileName];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMIMEStreamParser {
|
||||
NSData* _boundary;
|
||||
NSString* _defaultcontrolName;
|
||||
ParserState _state;
|
||||
NSMutableData* _data;
|
||||
NSMutableArray<GCDWebServerMultiPartArgument*>* _arguments;
|
||||
NSMutableArray<GCDWebServerMultiPartFile*>* _files;
|
||||
|
||||
NSString* _controlName;
|
||||
NSString* _fileName;
|
||||
NSString* _contentType;
|
||||
NSString* _tmpPath;
|
||||
int _tmpFile;
|
||||
GCDWebServerMIMEStreamParser* _subParser;
|
||||
}
|
||||
|
||||
+ (void)initialize {
|
||||
if (_newlineData == nil) {
|
||||
_newlineData = [[NSData alloc] initWithBytes:"\r\n" length:2];
|
||||
GWS_DCHECK(_newlineData);
|
||||
}
|
||||
if (_newlinesData == nil) {
|
||||
_newlinesData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
|
||||
GWS_DCHECK(_newlinesData);
|
||||
}
|
||||
if (_dashNewlineData == nil) {
|
||||
_dashNewlineData = [[NSData alloc] initWithBytes:"--\r\n" length:4];
|
||||
GWS_DCHECK(_dashNewlineData);
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)initWithBoundary:(NSString* _Nonnull)boundary defaultControlName:(NSString* _Nullable)name arguments:(NSMutableArray<GCDWebServerMultiPartArgument*>* _Nonnull)arguments files:(NSMutableArray<GCDWebServerMultiPartFile*>* _Nonnull)files {
|
||||
NSData* data = boundary.length ? [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding] : nil;
|
||||
if (data == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
if ((self = [super init])) {
|
||||
_boundary = data;
|
||||
_defaultcontrolName = name;
|
||||
_arguments = arguments;
|
||||
_files = files;
|
||||
_data = [[NSMutableData alloc] initWithCapacity:kMultiPartBufferSize];
|
||||
_state = kParserState_Start;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_tmpFile > 0) {
|
||||
close(_tmpFile);
|
||||
unlink([_tmpPath fileSystemRepresentation]);
|
||||
}
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
|
||||
- (BOOL)_parseData {
|
||||
BOOL success = YES;
|
||||
|
||||
if (_state == kParserState_Headers) {
|
||||
NSRange range = [_data rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _data.length)];
|
||||
if (range.location != NSNotFound) {
|
||||
_controlName = nil;
|
||||
_fileName = nil;
|
||||
_contentType = nil;
|
||||
_tmpPath = nil;
|
||||
_subParser = nil;
|
||||
NSString* headers = [[NSString alloc] initWithData:[_data subdataWithRange:NSMakeRange(0, range.location)] encoding:NSUTF8StringEncoding];
|
||||
if (headers) {
|
||||
for (NSString* header in [headers componentsSeparatedByString:@"\r\n"]) {
|
||||
NSRange subRange = [header rangeOfString:@":"];
|
||||
if (subRange.location != NSNotFound) {
|
||||
NSString* name = [header substringToIndex:subRange.location];
|
||||
NSString* value = [[header substringFromIndex:(subRange.location + subRange.length)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
if ([name caseInsensitiveCompare:@"Content-Type"] == NSOrderedSame) {
|
||||
_contentType = GCDWebServerNormalizeHeaderValue(value);
|
||||
} else if ([name caseInsensitiveCompare:@"Content-Disposition"] == NSOrderedSame) {
|
||||
NSString* contentDisposition = GCDWebServerNormalizeHeaderValue(value);
|
||||
if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"form-data"]) {
|
||||
_controlName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"name");
|
||||
_fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename");
|
||||
} else if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"file"]) {
|
||||
_controlName = _defaultcontrolName;
|
||||
_fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
}
|
||||
if (_contentType == nil) {
|
||||
_contentType = @"text/plain";
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed decoding headers in part of 'multipart/form-data'");
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
if (_controlName) {
|
||||
if ([GCDWebServerTruncateHeaderValue(_contentType) isEqualToString:@"multipart/mixed"]) {
|
||||
NSString* boundary = GCDWebServerExtractHeaderValueParameter(_contentType, @"boundary");
|
||||
_subParser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:_controlName arguments:_arguments files:_files];
|
||||
if (_subParser == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
} else if (_fileName) {
|
||||
NSString* path = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
_tmpFile = open([path fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (_tmpFile > 0) {
|
||||
_tmpPath = [path copy];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
|
||||
[_data replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0];
|
||||
_state = kParserState_Content;
|
||||
}
|
||||
}
|
||||
|
||||
if ((_state == kParserState_Start) || (_state == kParserState_Content)) {
|
||||
NSRange range = [_data rangeOfData:_boundary options:0 range:NSMakeRange(0, _data.length)];
|
||||
if (range.location != NSNotFound) {
|
||||
NSRange subRange = NSMakeRange(range.location + range.length, _data.length - range.location - range.length);
|
||||
NSRange subRange1 = [_data rangeOfData:_newlineData options:NSDataSearchAnchored range:subRange];
|
||||
NSRange subRange2 = [_data rangeOfData:_dashNewlineData options:NSDataSearchAnchored range:subRange];
|
||||
if ((subRange1.location != NSNotFound) || (subRange2.location != NSNotFound)) {
|
||||
if (_state == kParserState_Content) {
|
||||
const void* dataBytes = _data.bytes;
|
||||
NSUInteger dataLength = range.location - 2;
|
||||
if (_subParser) {
|
||||
if (![_subParser appendBytes:dataBytes length:(dataLength + 2)] || ![_subParser isAtEnd]) {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
_subParser = nil;
|
||||
} else if (_tmpPath) {
|
||||
ssize_t result = write(_tmpFile, dataBytes, dataLength);
|
||||
if (result == (ssize_t)dataLength) {
|
||||
if (close(_tmpFile) == 0) {
|
||||
_tmpFile = 0;
|
||||
GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithControlName:_controlName contentType:_contentType fileName:_fileName temporaryPath:_tmpPath];
|
||||
[_files addObject:file];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
_tmpPath = nil;
|
||||
} else {
|
||||
NSData* data = [[NSData alloc] initWithBytes:(void*)dataBytes length:dataLength];
|
||||
GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithControlName:_controlName contentType:_contentType data:data];
|
||||
[_arguments addObject:argument];
|
||||
}
|
||||
}
|
||||
|
||||
if (subRange1.location != NSNotFound) {
|
||||
[_data replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0];
|
||||
_state = kParserState_Headers;
|
||||
success = [self _parseData];
|
||||
} else {
|
||||
_state = kParserState_End;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NSUInteger margin = 2 * _boundary.length;
|
||||
if (_data.length > margin) {
|
||||
NSUInteger length = _data.length - margin;
|
||||
if (_subParser) {
|
||||
if ([_subParser appendBytes:_data.bytes length:length]) {
|
||||
[_data replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
} else if (_tmpPath) {
|
||||
ssize_t result = write(_tmpFile, _data.bytes, length);
|
||||
if (result == (ssize_t)length) {
|
||||
[_data replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)appendBytes:(const void*)bytes length:(NSUInteger)length {
|
||||
[_data appendBytes:bytes length:length];
|
||||
return [self _parseData];
|
||||
}
|
||||
|
||||
- (BOOL)isAtEnd {
|
||||
return (_state == kParserState_End);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerMultiPartFormRequest ()
|
||||
@property(nonatomic) NSMutableArray<GCDWebServerMultiPartArgument*>* arguments;
|
||||
@property(nonatomic) NSMutableArray<GCDWebServerMultiPartFile*>* files;
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPartFormRequest {
|
||||
GCDWebServerMIMEStreamParser* _parser;
|
||||
}
|
||||
|
||||
+ (NSString*)mimeType {
|
||||
return @"multipart/form-data";
|
||||
}
|
||||
|
||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary<NSString*, NSString*>*)headers path:(NSString*)path query:(NSDictionary<NSString*, NSString*>*)query {
|
||||
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
|
||||
_arguments = [[NSMutableArray alloc] init];
|
||||
_files = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
NSString* boundary = GCDWebServerExtractHeaderValueParameter(self.contentType, @"boundary");
|
||||
_parser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:nil arguments:_arguments files:_files];
|
||||
if (_parser == nil) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Failed starting to parse multipart form data"}];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
if (![_parser appendBytes:data.bytes length:data.length]) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Failed continuing to parse multipart form data"}];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
BOOL atEnd = [_parser isAtEnd];
|
||||
_parser = nil;
|
||||
if (!atEnd) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Failed finishing to parse multipart form data"}];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name {
|
||||
for (GCDWebServerMultiPartArgument* argument in _arguments) {
|
||||
if ([argument.controlName isEqualToString:name]) {
|
||||
return argument;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name {
|
||||
for (GCDWebServerMultiPartFile* file in _files) {
|
||||
if ([file.controlName isEqualToString:name]) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
if (_arguments.count) {
|
||||
[description appendString:@"\n"];
|
||||
for (GCDWebServerMultiPartArgument* argument in _arguments) {
|
||||
[description appendFormat:@"\n%@ (%@)\n", argument.controlName, argument.contentType];
|
||||
[description appendString:GCDWebServerDescribeData(argument.data, argument.contentType)];
|
||||
}
|
||||
}
|
||||
if (_files.count) {
|
||||
[description appendString:@"\n"];
|
||||
for (GCDWebServerMultiPartFile* file in _files) {
|
||||
[description appendFormat:@"\n%@ (%@): %@\n{%@}", file.controlName, file.contentType, file.fileName, file.temporaryPath];
|
||||
}
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerDataRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerURLEncodedFormRequest subclass of GCDWebServerRequest
|
||||
* parses the body of the HTTP request as a URL encoded form using
|
||||
* GCDWebServerParseURLEncodedForm().
|
||||
*/
|
||||
@interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest
|
||||
|
||||
/**
|
||||
* Returns the unescaped control names and values for the URL encoded form.
|
||||
*
|
||||
* The text encoding used to interpret the data is extracted from the
|
||||
* "Content-Type" header or defaults to UTF-8.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSDictionary<NSString*, NSString*>* arguments;
|
||||
|
||||
/**
|
||||
* Returns the MIME type for URL encoded forms
|
||||
* i.e. "application/x-www-form-urlencoded".
|
||||
*/
|
||||
+ (NSString*)mimeType;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@implementation GCDWebServerURLEncodedFormRequest
|
||||
|
||||
+ (NSString*)mimeType {
|
||||
return @"application/x-www-form-urlencoded";
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
if (![super close:error]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset");
|
||||
NSString* string = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
_arguments = GCDWebServerParseURLEncodedForm(string);
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
[description appendString:@"\n"];
|
||||
for (NSString* argument in [[_arguments allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
||||
[description appendFormat:@"\n%@ = %@", argument, [_arguments objectForKey:argument]];
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerResponse.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerDataResponse subclass of GCDWebServerResponse reads the body
|
||||
* of the HTTP response from memory.
|
||||
*/
|
||||
@interface GCDWebServerDataResponse : GCDWebServerResponse
|
||||
@property(nonatomic, copy) NSString* contentType; // Redeclare as non-null
|
||||
|
||||
/**
|
||||
* Creates a response with data in memory and a given content type.
|
||||
*/
|
||||
+ (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*/
|
||||
- (instancetype)initWithData:(NSData*)data contentType:(NSString*)type;
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerDataResponse (Extensions)
|
||||
|
||||
/**
|
||||
* Creates a data response from text encoded using UTF-8.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithText:(NSString*)text;
|
||||
|
||||
/**
|
||||
* Creates a data response from HTML encoded using UTF-8.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithHTML:(NSString*)html;
|
||||
|
||||
/**
|
||||
* Creates a data response from an HTML template encoded using UTF-8.
|
||||
* See -initWithHTMLTemplate:variables: for details.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary<NSString*, NSString*>*)variables;
|
||||
|
||||
/**
|
||||
* Creates a data response from a serialized JSON object and the default
|
||||
* "application/json" content type.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithJSONObject:(id)object;
|
||||
|
||||
/**
|
||||
* Creates a data response from a serialized JSON object and a custom
|
||||
* content type.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type;
|
||||
|
||||
/**
|
||||
* Initializes a data response from text encoded using UTF-8.
|
||||
*/
|
||||
- (nullable instancetype)initWithText:(NSString*)text;
|
||||
|
||||
/**
|
||||
* Initializes a data response from HTML encoded using UTF-8.
|
||||
*/
|
||||
- (nullable instancetype)initWithHTML:(NSString*)html;
|
||||
|
||||
/**
|
||||
* Initializes a data response from an HTML template encoded using UTF-8.
|
||||
*
|
||||
* All occurences of "%variable%" within the HTML template are replaced with
|
||||
* their corresponding values.
|
||||
*/
|
||||
- (nullable instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary<NSString*, NSString*>*)variables;
|
||||
|
||||
/**
|
||||
* Initializes a data response from a serialized JSON object and the default
|
||||
* "application/json" content type.
|
||||
*/
|
||||
- (nullable instancetype)initWithJSONObject:(id)object;
|
||||
|
||||
/**
|
||||
* Initializes a data response from a serialized JSON object and a custom
|
||||
* content type.
|
||||
*/
|
||||
- (nullable instancetype)initWithJSONObject:(id)object contentType:(NSString*)type;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@implementation GCDWebServerDataResponse {
|
||||
NSData* _data;
|
||||
BOOL _done;
|
||||
}
|
||||
|
||||
@dynamic contentType;
|
||||
|
||||
+ (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type {
|
||||
return [(GCDWebServerDataResponse*)[[self class] alloc] initWithData:data contentType:type];
|
||||
}
|
||||
|
||||
- (instancetype)initWithData:(NSData*)data contentType:(NSString*)type {
|
||||
if ((self = [super init])) {
|
||||
_data = data;
|
||||
|
||||
self.contentType = type;
|
||||
self.contentLength = data.length;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
NSData* data;
|
||||
if (_done) {
|
||||
data = [NSData data];
|
||||
} else {
|
||||
data = _data;
|
||||
_done = YES;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
[description appendString:@"\n\n"];
|
||||
[description appendString:GCDWebServerDescribeData(_data, self.contentType)];
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerDataResponse (Extensions)
|
||||
|
||||
+ (instancetype)responseWithText:(NSString*)text {
|
||||
return [(GCDWebServerDataResponse*)[self alloc] initWithText:text];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithHTML:(NSString*)html {
|
||||
return [(GCDWebServerDataResponse*)[self alloc] initWithHTML:html];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary<NSString*, NSString*>*)variables {
|
||||
return [(GCDWebServerDataResponse*)[self alloc] initWithHTMLTemplate:path variables:variables];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithJSONObject:(id)object {
|
||||
return [(GCDWebServerDataResponse*)[self alloc] initWithJSONObject:object];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type {
|
||||
return [(GCDWebServerDataResponse*)[self alloc] initWithJSONObject:object contentType:type];
|
||||
}
|
||||
|
||||
- (instancetype)initWithText:(NSString*)text {
|
||||
NSData* data = [text dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (data == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:@"text/plain; charset=utf-8"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithHTML:(NSString*)html {
|
||||
NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (data == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:@"text/html; charset=utf-8"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary<NSString*, NSString*>*)variables {
|
||||
NSMutableString* html = [[NSMutableString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
|
||||
[variables enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) {
|
||||
[html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)];
|
||||
}];
|
||||
return [self initWithHTML:html];
|
||||
}
|
||||
|
||||
- (instancetype)initWithJSONObject:(id)object {
|
||||
return [self initWithJSONObject:object contentType:@"application/json"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithJSONObject:(id)object contentType:(NSString*)type {
|
||||
NSData* data = [NSJSONSerialization dataWithJSONObject:object options:0 error:NULL];
|
||||
if (data == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:type];
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerDataResponse.h"
|
||||
#import "GCDWebServerHTTPStatusCodes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerDataResponse subclass of GCDWebServerDataResponse generates
|
||||
* an HTML body from an HTTP status code and an error message.
|
||||
*/
|
||||
@interface GCDWebServerErrorResponse : GCDWebServerDataResponse
|
||||
|
||||
/**
|
||||
* Creates a client error response with the corresponding HTTP status code.
|
||||
*/
|
||||
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||
|
||||
/**
|
||||
* Creates a server error response with the corresponding HTTP status code.
|
||||
*/
|
||||
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||
|
||||
/**
|
||||
* Creates a client error response with the corresponding HTTP status code
|
||||
* and an underlying NSError.
|
||||
*/
|
||||
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4);
|
||||
|
||||
/**
|
||||
* Creates a server error response with the corresponding HTTP status code
|
||||
* and an underlying NSError.
|
||||
*/
|
||||
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4);
|
||||
|
||||
/**
|
||||
* Initializes a client error response with the corresponding HTTP status code.
|
||||
*/
|
||||
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||
|
||||
/**
|
||||
* Initializes a server error response with the corresponding HTTP status code.
|
||||
*/
|
||||
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||
|
||||
/**
|
||||
* Initializes a client error response with the corresponding HTTP status code
|
||||
* and an underlying NSError.
|
||||
*/
|
||||
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4);
|
||||
|
||||
/**
|
||||
* Initializes a server error response with the corresponding HTTP status code
|
||||
* and an underlying NSError.
|
||||
*/
|
||||
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4);
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@implementation GCDWebServerErrorResponse
|
||||
|
||||
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GCDWebServerErrorResponse* response = [(GCDWebServerErrorResponse*)[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return response;
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GCDWebServerErrorResponse* response = [(GCDWebServerErrorResponse*)[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return response;
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GCDWebServerErrorResponse* response = [(GCDWebServerErrorResponse*)[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return response;
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GCDWebServerErrorResponse* response = [(GCDWebServerErrorResponse*)[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return response;
|
||||
}
|
||||
|
||||
static inline NSString* _EscapeHTMLString(NSString* string) {
|
||||
return [string stringByReplacingOccurrencesOfString:@"\"" withString:@"""];
|
||||
}
|
||||
|
||||
- (instancetype)initWithStatusCode:(NSInteger)statusCode underlyingError:(NSError*)underlyingError messageFormat:(NSString*)format arguments:(va_list)arguments {
|
||||
NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments];
|
||||
NSString* title = [NSString stringWithFormat:@"HTTP Error %i", (int)statusCode];
|
||||
NSString* error = underlyingError ? [NSString stringWithFormat:@"[%@] %@ (%li)", underlyingError.domain, _EscapeHTMLString(underlyingError.localizedDescription), (long)underlyingError.code] : @"";
|
||||
NSString* html = [NSString stringWithFormat:@"<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>%@</title></head><body><h1>%@: %@</h1><h3>%@</h3></body></html>",
|
||||
title, title, _EscapeHTMLString(message), error];
|
||||
if ((self = [self initWithHTML:html])) {
|
||||
self.statusCode = statusCode;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerResponse.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerFileResponse subclass of GCDWebServerResponse reads the body
|
||||
* of the HTTP response from a file on disk.
|
||||
*
|
||||
* It will automatically set the contentType, lastModifiedDate and eTag
|
||||
* properties of the GCDWebServerResponse according to the file extension and
|
||||
* metadata.
|
||||
*/
|
||||
@interface GCDWebServerFileResponse : GCDWebServerResponse
|
||||
@property(nonatomic, copy) NSString* contentType; // Redeclare as non-null
|
||||
@property(nonatomic) NSDate* lastModifiedDate; // Redeclare as non-null
|
||||
@property(nonatomic, copy) NSString* eTag; // Redeclare as non-null
|
||||
|
||||
/**
|
||||
* Creates a response with the contents of a file.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithFile:(NSString*)path;
|
||||
|
||||
/**
|
||||
* Creates a response like +responseWithFile: and sets the "Content-Disposition"
|
||||
* HTTP header for a download if the "attachment" argument is YES.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment;
|
||||
|
||||
/**
|
||||
* Creates a response like +responseWithFile: but restricts the file contents
|
||||
* to a specific byte range.
|
||||
*
|
||||
* See -initWithFile:byteRange: for details.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range;
|
||||
|
||||
/**
|
||||
* Creates a response like +responseWithFile:byteRange: and sets the
|
||||
* "Content-Disposition" HTTP header for a download if the "attachment"
|
||||
* argument is YES.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment;
|
||||
|
||||
/**
|
||||
* Initializes a response with the contents of a file.
|
||||
*/
|
||||
- (nullable instancetype)initWithFile:(NSString*)path;
|
||||
|
||||
/**
|
||||
* Initializes a response like +responseWithFile: and sets the
|
||||
* "Content-Disposition" HTTP header for a download if the "attachment"
|
||||
* argument is YES.
|
||||
*/
|
||||
- (nullable instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment;
|
||||
|
||||
/**
|
||||
* Initializes a response like -initWithFile: but restricts the file contents
|
||||
* to a specific byte range. This range should be set to (NSUIntegerMax, 0) for
|
||||
* the full file, (offset, length) if expressed from the beginning of the file,
|
||||
* or (NSUIntegerMax, length) if expressed from the end of the file. The "offset"
|
||||
* and "length" values will be automatically adjusted to be compatible with the
|
||||
* actual size of the file.
|
||||
*
|
||||
* This argument would typically be set to the value of the byteRange property
|
||||
* of the current GCDWebServerRequest.
|
||||
*/
|
||||
- (nullable instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*
|
||||
* If MIME type overrides are specified, they allow to customize the built-in
|
||||
* mapping from extensions to MIME types. Keys of the dictionary must be lowercased
|
||||
* file extensions without the period, and the values must be the corresponding
|
||||
* MIME types.
|
||||
*/
|
||||
- (nullable instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(nullable NSDictionary<NSString*, NSString*>*)overrides;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,185 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <sys/stat.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kFileReadBufferSize (32 * 1024)
|
||||
|
||||
@implementation GCDWebServerFileResponse {
|
||||
NSString* _path;
|
||||
NSUInteger _offset;
|
||||
NSUInteger _size;
|
||||
int _file;
|
||||
}
|
||||
|
||||
@dynamic contentType, lastModifiedDate, eTag;
|
||||
|
||||
+ (instancetype)responseWithFile:(NSString*)path {
|
||||
return [(GCDWebServerFileResponse*)[[self class] alloc] initWithFile:path];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment {
|
||||
return [(GCDWebServerFileResponse*)[[self class] alloc] initWithFile:path isAttachment:attachment];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range {
|
||||
return [(GCDWebServerFileResponse*)[[self class] alloc] initWithFile:path byteRange:range];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment {
|
||||
return [(GCDWebServerFileResponse*)[[self class] alloc] initWithFile:path byteRange:range isAttachment:attachment mimeTypeOverrides:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFile:(NSString*)path {
|
||||
return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:NO mimeTypeOverrides:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment {
|
||||
return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:attachment mimeTypeOverrides:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range {
|
||||
return [self initWithFile:path byteRange:range isAttachment:NO mimeTypeOverrides:nil];
|
||||
}
|
||||
|
||||
static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
||||
return [NSDate dateWithTimeIntervalSince1970:((NSTimeInterval)t->tv_sec + (NSTimeInterval)t->tv_nsec / 1000000000.0)];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(NSDictionary<NSString*, NSString*>*)overrides {
|
||||
struct stat info;
|
||||
if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) {
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
#ifndef __LP64__
|
||||
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)
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
#endif
|
||||
NSUInteger fileSize = (NSUInteger)info.st_size;
|
||||
|
||||
BOOL hasByteRange = GCDWebServerIsValidByteRange(range);
|
||||
if (hasByteRange) {
|
||||
if (range.location != NSUIntegerMax) {
|
||||
range.location = MIN(range.location, fileSize);
|
||||
range.length = MIN(range.length, fileSize - range.location);
|
||||
} else {
|
||||
range.length = MIN(range.length, fileSize);
|
||||
range.location = fileSize - range.length;
|
||||
}
|
||||
if (range.length == 0) {
|
||||
return nil; // TODO: Return 416 status code and "Content-Range: bytes */{file length}" header
|
||||
}
|
||||
} else {
|
||||
range.location = 0;
|
||||
range.length = fileSize;
|
||||
}
|
||||
|
||||
if ((self = [super init])) {
|
||||
_path = [path copy];
|
||||
_offset = range.location;
|
||||
_size = range.length;
|
||||
if (hasByteRange) {
|
||||
[self setStatusCode:kGCDWebServerHTTPStatusCode_PartialContent];
|
||||
[self setValue:[NSString stringWithFormat:@"bytes %lu-%lu/%lu", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), (unsigned long)fileSize] forAdditionalHeader:@"Content-Range"];
|
||||
GWS_LOG_DEBUG(@"Using content bytes range [%lu-%lu] for file \"%@\"", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), path);
|
||||
}
|
||||
|
||||
if (attachment) {
|
||||
NSString* fileName = [path lastPathComponent];
|
||||
NSData* data = [[fileName stringByReplacingOccurrencesOfString:@"\"" withString:@""] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES];
|
||||
NSString* lossyFileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil;
|
||||
if (lossyFileName) {
|
||||
NSString* value = [NSString stringWithFormat:@"attachment; filename=\"%@\"; filename*=UTF-8''%@", lossyFileName, GCDWebServerEscapeURLString(fileName)];
|
||||
[self setValue:value forAdditionalHeader:@"Content-Disposition"];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
self.contentType = GCDWebServerGetMimeTypeForExtension([_path pathExtension], overrides);
|
||||
self.contentLength = _size;
|
||||
self.lastModifiedDate = _NSDateFromTimeSpec(&info.st_mtimespec);
|
||||
self.eTag = [NSString stringWithFormat:@"%llu/%li/%li", info.st_ino, info.st_mtimespec.tv_sec, info.st_mtimespec.tv_nsec];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
_file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY);
|
||||
if (_file <= 0) {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
close(_file);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
size_t length = MIN((NSUInteger)kFileReadBufferSize, _size);
|
||||
NSMutableData* data = [[NSMutableData alloc] initWithLength:length];
|
||||
ssize_t result = read(_file, data.mutableBytes, length);
|
||||
if (result < 0) {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
if (result > 0) {
|
||||
[data setLength:result];
|
||||
_size -= result;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
close(_file);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
[description appendFormat:@"\n\n{%@}", _path];
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerResponse.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerStreamBlock is called to stream the data for the HTTP body.
|
||||
* The block must return either a chunk of data, an empty NSData when done, or
|
||||
* nil on error and set the "error" argument which is guaranteed to be non-NULL.
|
||||
*/
|
||||
typedef NSData* _Nullable (^GCDWebServerStreamBlock)(NSError** error);
|
||||
|
||||
/**
|
||||
* The GCDWebServerAsyncStreamBlock works like the GCDWebServerStreamBlock
|
||||
* except the streamed data can be returned at a later time allowing for
|
||||
* truly asynchronous generation of the data.
|
||||
*
|
||||
* The block must call "completionBlock" passing the new chunk of data when ready,
|
||||
* an empty NSData when done, or nil on error and pass a NSError.
|
||||
*
|
||||
* The block cannot call "completionBlock" more than once per invocation.
|
||||
*/
|
||||
typedef void (^GCDWebServerAsyncStreamBlock)(GCDWebServerBodyReaderCompletionBlock completionBlock);
|
||||
|
||||
/**
|
||||
* The GCDWebServerStreamedResponse subclass of GCDWebServerResponse streams
|
||||
* the body of the HTTP response using a GCD block.
|
||||
*/
|
||||
@interface GCDWebServerStreamedResponse : GCDWebServerResponse
|
||||
@property(nonatomic, copy) NSString* contentType; // Redeclare as non-null
|
||||
|
||||
/**
|
||||
* Creates a response with streamed data and a given content type.
|
||||
*/
|
||||
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block;
|
||||
|
||||
/**
|
||||
* Creates a response with async streamed data and a given content type.
|
||||
*/
|
||||
+ (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block;
|
||||
|
||||
/**
|
||||
* Initializes a response with streamed data and a given content type.
|
||||
*/
|
||||
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*/
|
||||
- (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@implementation GCDWebServerStreamedResponse {
|
||||
GCDWebServerAsyncStreamBlock _block;
|
||||
}
|
||||
|
||||
@dynamic contentType;
|
||||
|
||||
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||
return [(GCDWebServerStreamedResponse*)[[self class] alloc] initWithContentType:type streamBlock:block];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block {
|
||||
return [(GCDWebServerStreamedResponse*)[[self class] alloc] initWithContentType:type asyncStreamBlock:block];
|
||||
}
|
||||
|
||||
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||
return [self initWithContentType:type
|
||||
asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) {
|
||||
NSError* error = nil;
|
||||
NSData* data = block(&error);
|
||||
completionBlock(data, error);
|
||||
}];
|
||||
}
|
||||
|
||||
- (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block {
|
||||
if ((self = [super init])) {
|
||||
_block = [block copy];
|
||||
|
||||
self.contentType = type;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block {
|
||||
_block(block);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
[description appendString:@"\n\n<STREAM>"];
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
Binary file not shown.
347
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/css/bootstrap-theme.css
vendored
Normal file
347
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/css/bootstrap-theme.css
vendored
Normal file
@ -0,0 +1,347 @@
|
||||
/*!
|
||||
* Bootstrap v3.1.1 (http://getbootstrap.com)
|
||||
* Copyright 2011-2014 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
.btn-default,
|
||||
.btn-primary,
|
||||
.btn-success,
|
||||
.btn-info,
|
||||
.btn-warning,
|
||||
.btn-danger {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-primary:active,
|
||||
.btn-success:active,
|
||||
.btn-info:active,
|
||||
.btn-warning:active,
|
||||
.btn-danger:active,
|
||||
.btn-default.active,
|
||||
.btn-primary.active,
|
||||
.btn-success.active,
|
||||
.btn-info.active,
|
||||
.btn-warning.active,
|
||||
.btn-danger.active {
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
}
|
||||
.btn:active,
|
||||
.btn.active {
|
||||
background-image: none;
|
||||
}
|
||||
.btn-default {
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dbdbdb;
|
||||
border-color: #ccc;
|
||||
}
|
||||
.btn-default:hover,
|
||||
.btn-default:focus {
|
||||
background-color: #e0e0e0;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
border-color: #dbdbdb;
|
||||
}
|
||||
.btn-primary {
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #2b669a;
|
||||
}
|
||||
.btn-primary:hover,
|
||||
.btn-primary:focus {
|
||||
background-color: #2d6ca2;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-primary:active,
|
||||
.btn-primary.active {
|
||||
background-color: #2d6ca2;
|
||||
border-color: #2b669a;
|
||||
}
|
||||
.btn-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success:hover,
|
||||
.btn-success:focus {
|
||||
background-color: #419641;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-success:active,
|
||||
.btn-success.active {
|
||||
background-color: #419641;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info:hover,
|
||||
.btn-info:focus {
|
||||
background-color: #2aabd2;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-info:active,
|
||||
.btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning:hover,
|
||||
.btn-warning:focus {
|
||||
background-color: #eb9316;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-warning:active,
|
||||
.btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger:hover,
|
||||
.btn-danger:focus {
|
||||
background-color: #c12e2a;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-danger:active,
|
||||
.btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.thumbnail,
|
||||
.img-thumbnail {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus {
|
||||
background-color: #e8e8e8;
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.dropdown-menu > .active > a,
|
||||
.dropdown-menu > .active > a:hover,
|
||||
.dropdown-menu > .active > a:focus {
|
||||
background-color: #357ebd;
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.navbar-default {
|
||||
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.navbar-default .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.navbar-brand,
|
||||
.navbar-nav > li > a {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
|
||||
}
|
||||
.navbar-inverse {
|
||||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
|
||||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.navbar-inverse .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%);
|
||||
background-image: linear-gradient(to bottom, #222 0%, #282828 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
}
|
||||
.navbar-inverse .navbar-brand,
|
||||
.navbar-inverse .navbar-nav > li > a {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
|
||||
}
|
||||
.navbar-static-top,
|
||||
.navbar-fixed-top,
|
||||
.navbar-fixed-bottom {
|
||||
border-radius: 0;
|
||||
}
|
||||
.alert {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
}
|
||||
.alert-success {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b2dba1;
|
||||
}
|
||||
.alert-info {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #9acfea;
|
||||
}
|
||||
.alert-warning {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #f5e79e;
|
||||
}
|
||||
.alert-danger {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dca7a7;
|
||||
}
|
||||
.progress {
|
||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar {
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.list-group {
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.list-group-item.active,
|
||||
.list-group-item.active:hover,
|
||||
.list-group-item.active:focus {
|
||||
text-shadow: 0 -1px 0 #3071a9;
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #3278b3;
|
||||
}
|
||||
.panel {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
}
|
||||
.panel-default > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-primary > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-success > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-info > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-warning > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-danger > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.well {
|
||||
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dcdcdc;
|
||||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-theme.css.map */
|
5785
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/css/bootstrap.css
vendored
Normal file
5785
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
.row-file {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.column-icon {
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.column-name {
|
||||
}
|
||||
|
||||
.column-size {
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.column-move {
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.column-delete {
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.column-path {
|
||||
}
|
||||
|
||||
.column-progress {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
color: #999;
|
||||
text-align: center;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
#reload {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#create-input {
|
||||
width: 50%;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
#move-input {
|
||||
width: 80%;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* Bootstrap overrides */
|
||||
|
||||
.btn:focus {
|
||||
outline: none; /* FIXME: Work around for Chrome only but still draws focus ring while button pressed */
|
||||
}
|
||||
|
||||
.btn-toolbar {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.table .progress {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.panel-default > .panel-heading {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
background-color: transparent;
|
||||
border-radius: 0px;
|
||||
margin-bottom: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.breadcrumb > .active {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.breadcrumb > li + li:before {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.table > tbody > tr > td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table > tbody > tr > td > p {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* Initial state */
|
||||
|
||||
.uploading {
|
||||
display: none;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
@charset "UTF-8";
|
||||
/*
|
||||
* jQuery File Upload Plugin CSS 1.3.0
|
||||
* https://github.com/blueimp/jQuery-File-Upload
|
||||
*
|
||||
* Copyright 2013, Sebastian Tschan
|
||||
* https://blueimp.net
|
||||
*
|
||||
* Licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
.fileinput-button {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.fileinput-button input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
opacity: 0;
|
||||
-ms-filter: 'alpha(opacity=0)';
|
||||
font-size: 200px;
|
||||
direction: ltr;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Fixes for IE < 8 */
|
||||
@media screen\9 {
|
||||
.fileinput-button input {
|
||||
filter: alpha(opacity=0);
|
||||
font-size: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
"PROLOGUE" = "<p>Drag & drop files on this window or use the \"Upload Files…\" button to upload new files.</p>";
|
||||
"EPILOGUE" = "";
|
||||
"FOOTER_FORMAT" = "%@ %@";
|
Binary file not shown.
@ -0,0 +1,229 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata></metadata>
|
||||
<defs>
|
||||
<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
|
||||
<font-face units-per-em="1200" ascent="960" descent="-240" />
|
||||
<missing-glyph horiz-adv-x="500" />
|
||||
<glyph />
|
||||
<glyph />
|
||||
<glyph unicode="
" />
|
||||
<glyph unicode=" " />
|
||||
<glyph unicode="*" d="M100 500v200h259l-183 183l141 141l183 -183v259h200v-259l183 183l141 -141l-183 -183h259v-200h-259l183 -183l-141 -141l-183 183v-259h-200v259l-183 -183l-141 141l183 183h-259z" />
|
||||
<glyph unicode="+" d="M0 400v300h400v400h300v-400h400v-300h-400v-400h-300v400h-400z" />
|
||||
<glyph unicode=" " />
|
||||
<glyph unicode=" " horiz-adv-x="652" />
|
||||
<glyph unicode=" " horiz-adv-x="1304" />
|
||||
<glyph unicode=" " horiz-adv-x="652" />
|
||||
<glyph unicode=" " horiz-adv-x="1304" />
|
||||
<glyph unicode=" " horiz-adv-x="434" />
|
||||
<glyph unicode=" " horiz-adv-x="326" />
|
||||
<glyph unicode=" " horiz-adv-x="217" />
|
||||
<glyph unicode=" " horiz-adv-x="217" />
|
||||
<glyph unicode=" " horiz-adv-x="163" />
|
||||
<glyph unicode=" " horiz-adv-x="260" />
|
||||
<glyph unicode=" " horiz-adv-x="72" />
|
||||
<glyph unicode=" " horiz-adv-x="260" />
|
||||
<glyph unicode=" " horiz-adv-x="326" />
|
||||
<glyph unicode="€" d="M100 500l100 100h113q0 47 5 100h-218l100 100h135q37 167 112 257q117 141 297 141q242 0 354 -189q60 -103 66 -209h-181q0 55 -25.5 99t-63.5 68t-75 36.5t-67 12.5q-24 0 -52.5 -10t-62.5 -32t-65.5 -67t-50.5 -107h379l-100 -100h-300q-6 -46 -6 -100h406l-100 -100 h-300q9 -74 33 -132t52.5 -91t62 -54.5t59 -29t46.5 -7.5q29 0 66 13t75 37t63.5 67.5t25.5 96.5h174q-31 -172 -128 -278q-107 -117 -274 -117q-205 0 -324 158q-36 46 -69 131.5t-45 205.5h-217z" />
|
||||
<glyph unicode="−" d="M200 400h900v300h-900v-300z" />
|
||||
<glyph unicode="◼" horiz-adv-x="500" d="M0 0z" />
|
||||
<glyph unicode="☁" d="M-14 494q0 -80 56.5 -137t135.5 -57h750q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5z" />
|
||||
<glyph unicode="✉" d="M0 100l400 400l200 -200l200 200l400 -400h-1200zM0 300v600l300 -300zM0 1100l600 -603l600 603h-1200zM900 600l300 300v-600z" />
|
||||
<glyph unicode="✏" d="M-13 -13l333 112l-223 223zM187 403l214 -214l614 614l-214 214zM887 1103l214 -214l99 92q13 13 13 32.5t-13 33.5l-153 153q-15 13 -33 13t-33 -13z" />
|
||||
<glyph unicode="" d="M0 1200h1200l-500 -550v-550h300v-100h-800v100h300v550z" />
|
||||
<glyph unicode="" d="M14 84q18 -55 86 -75.5t147 5.5q65 21 109 69t44 90v606l600 155v-521q-64 16 -138 -7q-79 -26 -122.5 -83t-25.5 -111q18 -55 86 -75.5t147 4.5q70 23 111.5 63.5t41.5 95.5v881q0 10 -7 15.5t-17 2.5l-752 -193q-10 -3 -17 -12.5t-7 -19.5v-689q-64 17 -138 -7 q-79 -25 -122.5 -82t-25.5 -112z" />
|
||||
<glyph unicode="" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233z" />
|
||||
<glyph unicode="" d="M100 784q0 64 28 123t73 100.5t104.5 64t119 20.5t120 -38.5t104.5 -104.5q48 69 109.5 105t121.5 38t118.5 -20.5t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-149.5 152.5t-126.5 127.5 t-94 124.5t-33.5 117.5z" />
|
||||
<glyph unicode="" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1z" />
|
||||
<glyph unicode="" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1zM237 700l196 -142l-73 -226l192 140l195 -141l-74 229l193 140h-235l-77 211l-78 -211h-239z" />
|
||||
<glyph unicode="" d="M0 0v143l400 257v100q-37 0 -68.5 74.5t-31.5 125.5v200q0 124 88 212t212 88t212 -88t88 -212v-200q0 -51 -31.5 -125.5t-68.5 -74.5v-100l400 -257v-143h-1200z" />
|
||||
<glyph unicode="" d="M0 0v1100h1200v-1100h-1200zM100 100h100v100h-100v-100zM100 300h100v100h-100v-100zM100 500h100v100h-100v-100zM100 700h100v100h-100v-100zM100 900h100v100h-100v-100zM300 100h600v400h-600v-400zM300 600h600v400h-600v-400zM1000 100h100v100h-100v-100z M1000 300h100v100h-100v-100zM1000 500h100v100h-100v-100zM1000 700h100v100h-100v-100zM1000 900h100v100h-100v-100z" />
|
||||
<glyph unicode="" d="M0 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM0 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5zM600 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM600 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 450v200q0 21 14.5 35.5t35.5 14.5h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v200q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5 t-14.5 -35.5v-200zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M29 454l419 -420l818 820l-212 212l-607 -607l-206 207z" />
|
||||
<glyph unicode="" d="M106 318l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282l-212 -212l-282 282l-282 -282z" />
|
||||
<glyph unicode="" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233zM300 600v200h100v100h200v-100h100v-200h-100v-100h-200v100h-100z" />
|
||||
<glyph unicode="" d="M23 694q0 200 142 342t342 142t342 -142t142 -342q0 -141 -78 -262l300 -299q7 -7 7 -18t-7 -18l-109 -109q-8 -8 -18 -8t-18 8l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 694q0 -136 97 -233t234 -97t233.5 97t96.5 233t-96.5 233t-233.5 97t-234 -97 t-97 -233zM300 601h400v200h-400v-200z" />
|
||||
<glyph unicode="" d="M23 600q0 183 105 331t272 210v-166q-103 -55 -165 -155t-62 -220q0 -177 125 -302t302 -125t302 125t125 302q0 120 -62 220t-165 155v166q167 -62 272 -210t105 -331q0 -118 -45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5 zM500 750q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v400q0 21 -14.5 35.5t-35.5 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-400z" />
|
||||
<glyph unicode="" d="M100 1h200v300h-200v-300zM400 1v500h200v-500h-200zM700 1v800h200v-800h-200zM1000 1v1200h200v-1200h-200z" />
|
||||
<glyph unicode="" d="M26 601q0 -33 6 -74l151 -38l2 -6q14 -49 38 -93l3 -5l-80 -134q45 -59 105 -105l133 81l5 -3q45 -26 94 -39l5 -2l38 -151q40 -5 74 -5q27 0 74 5l38 151l6 2q46 13 93 39l5 3l134 -81q56 44 104 105l-80 134l3 5q24 44 39 93l1 6l152 38q5 40 5 74q0 28 -5 73l-152 38 l-1 6q-16 51 -39 93l-3 5l80 134q-44 58 -104 105l-134 -81l-5 3q-45 25 -93 39l-6 1l-38 152q-40 5 -74 5q-27 0 -74 -5l-38 -152l-5 -1q-50 -14 -94 -39l-5 -3l-133 81q-59 -47 -105 -105l80 -134l-3 -5q-25 -47 -38 -93l-2 -6l-151 -38q-6 -48 -6 -73zM385 601 q0 88 63 151t152 63t152 -63t63 -151q0 -89 -63 -152t-152 -63t-152 63t-63 152z" />
|
||||
<glyph unicode="" d="M100 1025v50q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-50q0 -11 -7 -18t-18 -7h-1050q-11 0 -18 7t-7 18zM200 100v800h900v-800q0 -41 -29.5 -71t-70.5 -30h-700q-41 0 -70.5 30 t-29.5 71zM300 100h100v700h-100v-700zM500 100h100v700h-100v-700zM500 1100h300v100h-300v-100zM700 100h100v700h-100v-700zM900 100h100v700h-100v-700z" />
|
||||
<glyph unicode="" d="M1 601l656 644l644 -644h-200v-600h-300v400h-300v-400h-300v600h-200z" />
|
||||
<glyph unicode="" d="M100 25v1150q0 11 7 18t18 7h475v-500h400v-675q0 -11 -7 -18t-18 -7h-850q-11 0 -18 7t-7 18zM700 800v300l300 -300h-300z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 500v400h100 v-300h200v-100h-300z" />
|
||||
<glyph unicode="" d="M-100 0l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538l-41 400h-242l-40 -400h-539zM488 500h224l-27 300h-170z" />
|
||||
<glyph unicode="" d="M0 0v400h490l-290 300h200v500h300v-500h200l-290 -300h490v-400h-1100zM813 200h175v100h-175v-100z" />
|
||||
<glyph unicode="" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM188 600q0 -170 121 -291t291 -121t291 121t121 291t-121 291t-291 121 t-291 -121t-121 -291zM350 600h150v300h200v-300h150l-250 -300z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM350 600l250 300 l250 -300h-150v-300h-200v300h-150z" />
|
||||
<glyph unicode="" d="M0 25v475l200 700h800l199 -700l1 -475q0 -11 -7 -18t-18 -7h-1150q-11 0 -18 7t-7 18zM200 500h200l50 -200h300l50 200h200l-97 500h-606z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 397v401 l297 -200z" />
|
||||
<glyph unicode="" d="M23 600q0 -118 45.5 -224.5t123 -184t184 -123t224.5 -45.5t224.5 45.5t184 123t123 184t45.5 224.5h-150q0 -177 -125 -302t-302 -125t-302 125t-125 302t125 302t302 125q136 0 246 -81l-146 -146h400v400l-145 -145q-157 122 -355 122q-118 0 -224.5 -45.5t-184 -123 t-123 -184t-45.5 -224.5z" />
|
||||
<glyph unicode="" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5q198 0 355 -122l145 145v-400h-400l147 147q-112 80 -247 80q-177 0 -302 -125t-125 -302h-150zM100 0v400h400l-147 -147q112 -80 247 -80q177 0 302 125t125 302h150q0 -118 -45.5 -224.5t-123 -184t-184 -123 t-224.5 -45.5q-198 0 -355 122z" />
|
||||
<glyph unicode="" d="M100 0h1100v1200h-1100v-1200zM200 100v900h900v-900h-900zM300 200v100h100v-100h-100zM300 400v100h100v-100h-100zM300 600v100h100v-100h-100zM300 800v100h100v-100h-100zM500 200h500v100h-500v-100zM500 400v100h500v-100h-500zM500 600v100h500v-100h-500z M500 800v100h500v-100h-500z" />
|
||||
<glyph unicode="" d="M0 100v600q0 41 29.5 70.5t70.5 29.5h100v200q0 82 59 141t141 59h300q82 0 141 -59t59 -141v-200h100q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-900q-41 0 -70.5 29.5t-29.5 70.5zM400 800h300v150q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-150z" />
|
||||
<glyph unicode="" d="M100 0v1100h100v-1100h-100zM300 400q60 60 127.5 84t127.5 17.5t122 -23t119 -30t110 -11t103 42t91 120.5v500q-40 -81 -101.5 -115.5t-127.5 -29.5t-138 25t-139.5 40t-125.5 25t-103 -29.5t-65 -115.5v-500z" />
|
||||
<glyph unicode="" d="M0 275q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 127 70.5 231.5t184.5 161.5t245 57t245 -57t184.5 -161.5t70.5 -231.5v-300q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 116 -49.5 227t-131 192.5t-192.5 131t-227 49.5t-227 -49.5t-192.5 -131t-131 -192.5 t-49.5 -227v-300zM200 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14zM800 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14z" />
|
||||
<glyph unicode="" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM688 459l141 141l-141 141l71 71l141 -141l141 141l71 -71l-141 -141l141 -141l-71 -71l-141 141l-141 -141z" />
|
||||
<glyph unicode="" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM700 857l69 53q111 -135 111 -310q0 -169 -106 -302l-67 54q86 110 86 248q0 146 -93 257z" />
|
||||
<glyph unicode="" d="M0 401v400h300l300 200v-800l-300 200h-300zM702 858l69 53q111 -135 111 -310q0 -170 -106 -303l-67 55q86 110 86 248q0 145 -93 257zM889 951l7 -8q123 -151 123 -344q0 -189 -119 -339l-7 -8l81 -66l6 8q142 178 142 405q0 230 -144 408l-6 8z" />
|
||||
<glyph unicode="" d="M0 0h500v500h-200v100h-100v-100h-200v-500zM0 600h100v100h400v100h100v100h-100v300h-500v-600zM100 100v300h300v-300h-300zM100 800v300h300v-300h-300zM200 200v100h100v-100h-100zM200 900h100v100h-100v-100zM500 500v100h300v-300h200v-100h-100v-100h-200v100 h-100v100h100v200h-200zM600 0v100h100v-100h-100zM600 1000h100v-300h200v-300h300v200h-200v100h200v500h-600v-200zM800 800v300h300v-300h-300zM900 0v100h300v-100h-300zM900 900v100h100v-100h-100zM1100 200v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M0 200h100v1000h-100v-1000zM100 0v100h300v-100h-300zM200 200v1000h100v-1000h-100zM500 0v91h100v-91h-100zM500 200v1000h200v-1000h-200zM700 0v91h100v-91h-100zM800 200v1000h100v-1000h-100zM900 0v91h200v-91h-200zM1000 200v1000h200v-1000h-200z" />
|
||||
<glyph unicode="" d="M0 700l1 475q0 10 7.5 17.5t17.5 7.5h474l700 -700l-500 -500zM148 953q0 -42 29 -71q30 -30 71.5 -30t71.5 30q29 29 29 71t-29 71q-30 30 -71.5 30t-71.5 -30q-29 -29 -29 -71z" />
|
||||
<glyph unicode="" d="M1 700l1 475q0 11 7 18t18 7h474l700 -700l-500 -500zM148 953q0 -42 30 -71q29 -30 71 -30t71 30q30 29 30 71t-30 71q-29 30 -71 30t-71 -30q-30 -29 -30 -71zM701 1200h100l700 -700l-500 -500l-50 50l450 450z" />
|
||||
<glyph unicode="" d="M100 0v1025l175 175h925v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900z" />
|
||||
<glyph unicode="" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" />
|
||||
<glyph unicode="" d="M0 100v700h200l100 -200h600l100 200h200v-700h-200v200h-800v-200h-200zM253 829l40 -124h592l62 124l-94 346q-2 11 -10 18t-18 7h-450q-10 0 -18 -7t-10 -18zM281 24l38 152q2 10 11.5 17t19.5 7h500q10 0 19.5 -7t11.5 -17l38 -152q2 -10 -3.5 -17t-15.5 -7h-600 q-10 0 -15.5 7t-3.5 17z" />
|
||||
<glyph unicode="" d="M0 200q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-150q-4 8 -11.5 21.5t-33 48t-53 61t-69 48t-83.5 21.5h-200q-41 0 -82 -20.5t-70 -50t-52 -59t-34 -50.5l-12 -20h-150q-41 0 -70.5 -29.5t-29.5 -70.5v-600z M356 500q0 100 72 172t172 72t172 -72t72 -172t-72 -172t-172 -72t-172 72t-72 172zM494 500q0 -44 31 -75t75 -31t75 31t31 75t-31 75t-75 31t-75 -31t-31 -75zM900 700v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M53 0h365v66q-41 0 -72 11t-49 38t1 71l92 234h391l82 -222q16 -45 -5.5 -88.5t-74.5 -43.5v-66h417v66q-34 1 -74 43q-18 19 -33 42t-21 37l-6 13l-385 998h-93l-399 -1006q-24 -48 -52 -75q-12 -12 -33 -25t-36 -20l-15 -7v-66zM416 521l178 457l46 -140l116 -317h-340 z" />
|
||||
<glyph unicode="" d="M100 0v89q41 7 70.5 32.5t29.5 65.5v827q0 28 -1 39.5t-5.5 26t-15.5 21t-29 14t-49 14.5v71l471 -1q120 0 213 -88t93 -228q0 -55 -11.5 -101.5t-28 -74t-33.5 -47.5t-28 -28l-12 -7q8 -3 21.5 -9t48 -31.5t60.5 -58t47.5 -91.5t21.5 -129q0 -84 -59 -156.5t-142 -111 t-162 -38.5h-500zM400 200h161q89 0 153 48.5t64 132.5q0 90 -62.5 154.5t-156.5 64.5h-159v-400zM400 700h139q76 0 130 61.5t54 138.5q0 82 -84 130.5t-239 48.5v-379z" />
|
||||
<glyph unicode="" d="M200 0v57q77 7 134.5 40.5t65.5 80.5l173 849q10 56 -10 74t-91 37q-6 1 -10.5 2.5t-9.5 2.5v57h425l2 -57q-33 -8 -62 -25.5t-46 -37t-29.5 -38t-17.5 -30.5l-5 -12l-128 -825q-10 -52 14 -82t95 -36v-57h-500z" />
|
||||
<glyph unicode="" d="M-75 200h75v800h-75l125 167l125 -167h-75v-800h75l-125 -167zM300 900v300h150h700h150v-300h-50q0 29 -8 48.5t-18.5 30t-33.5 15t-39.5 5.5t-50.5 1h-200v-850l100 -50v-100h-400v100l100 50v850h-200q-34 0 -50.5 -1t-40 -5.5t-33.5 -15t-18.5 -30t-8.5 -48.5h-49z " />
|
||||
<glyph unicode="" d="M33 51l167 125v-75h800v75l167 -125l-167 -125v75h-800v-75zM100 901v300h150h700h150v-300h-50q0 29 -8 48.5t-18 30t-33.5 15t-40 5.5t-50.5 1h-200v-650l100 -50v-100h-400v100l100 50v650h-200q-34 0 -50.5 -1t-39.5 -5.5t-33.5 -15t-18.5 -30t-8 -48.5h-50z" />
|
||||
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 350q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM0 650q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1000q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 950q0 -20 14.5 -35t35.5 -15h600q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-600q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
|
||||
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 650q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM200 350q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM200 950q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
|
||||
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1000q-21 0 -35.5 15 t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-600 q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100 q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM300 50v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800 q-21 0 -35.5 15t-14.5 35zM300 650v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 950v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15 h-800q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M-101 500v100h201v75l166 -125l-166 -125v75h-201zM300 0h100v1100h-100v-1100zM500 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35 v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 650q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100 q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100z" />
|
||||
<glyph unicode="" d="M1 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 650 q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM801 0v1100h100v-1100 h-100zM934 550l167 -125v75h200v100h-200v75z" />
|
||||
<glyph unicode="" d="M0 275v650q0 31 22 53t53 22h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53zM900 600l300 300v-600z" />
|
||||
<glyph unicode="" d="M0 44v1012q0 18 13 31t31 13h1112q19 0 31.5 -13t12.5 -31v-1012q0 -18 -12.5 -31t-31.5 -13h-1112q-18 0 -31 13t-13 31zM100 263l247 182l298 -131l-74 156l293 318l236 -288v500h-1000v-737zM208 750q0 56 39 95t95 39t95 -39t39 -95t-39 -95t-95 -39t-95 39t-39 95z " />
|
||||
<glyph unicode="" d="M148 745q0 124 60.5 231.5t165 172t226.5 64.5q123 0 227 -63t164.5 -169.5t60.5 -229.5t-73 -272q-73 -114 -166.5 -237t-150.5 -189l-57 -66q-10 9 -27 26t-66.5 70.5t-96 109t-104 135.5t-100.5 155q-63 139 -63 262zM342 772q0 -107 75.5 -182.5t181.5 -75.5 q107 0 182.5 75.5t75.5 182.5t-75.5 182t-182.5 75t-182 -75.5t-75 -181.5z" />
|
||||
<glyph unicode="" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM173 600q0 -177 125.5 -302t301.5 -125v854q-176 0 -301.5 -125 t-125.5 -302z" />
|
||||
<glyph unicode="" d="M117 406q0 94 34 186t88.5 172.5t112 159t115 177t87.5 194.5q21 -71 57.5 -142.5t76 -130.5t83 -118.5t82 -117t70 -116t50 -125.5t18.5 -136q0 -89 -39 -165.5t-102 -126.5t-140 -79.5t-156 -33.5q-114 6 -211.5 53t-161.5 139t-64 210zM243 414q14 -82 59.5 -136 t136.5 -80l16 98q-7 6 -18 17t-34 48t-33 77q-15 73 -14 143.5t10 122.5l9 51q-92 -110 -119.5 -185t-12.5 -156z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5q366 -6 397 -14l-186 -186h-311q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v125l200 200v-225q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM436 341l161 50l412 412l-114 113l-405 -405zM995 1015l113 -113l113 113l-21 85l-92 28z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h261l2 -80q-133 -32 -218 -120h-145q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-53q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5 zM423 524q30 38 81.5 64t103 35.5t99 14t77.5 3.5l29 -1v-209l360 324l-359 318v-216q-7 0 -19 -1t-48 -8t-69.5 -18.5t-76.5 -37t-76.5 -59t-62 -88t-39.5 -121.5z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q61 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-169q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM342 632l283 -284l567 567l-137 137l-430 -431l-146 147z" />
|
||||
<glyph unicode="" d="M0 603l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296l-300 -300v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198z" />
|
||||
<glyph unicode="" d="M200 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-1100l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M0 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-487l500 487v-1100l-500 488v-488l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M136 550l564 550v-487l500 487v-1100l-500 488v-488z" />
|
||||
<glyph unicode="" d="M200 0l900 550l-900 550v-1100z" />
|
||||
<glyph unicode="" d="M200 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800zM600 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
|
||||
<glyph unicode="" d="M200 150q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v800q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
|
||||
<glyph unicode="" d="M0 0v1100l500 -487v487l564 -550l-564 -550v488z" />
|
||||
<glyph unicode="" d="M0 0v1100l500 -487v487l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v488z" />
|
||||
<glyph unicode="" d="M300 0v1100l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438z" />
|
||||
<glyph unicode="" d="M100 250v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5zM100 500h1100l-550 564z" />
|
||||
<glyph unicode="" d="M185 599l592 -592l240 240l-353 353l353 353l-240 240z" />
|
||||
<glyph unicode="" d="M272 194l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1l-592 -591z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h200v-200h200v200h200v200h-200v200h-200v-200h-200v-200z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h600v200h-600v-200z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM246 459l213 -213l141 142l141 -142l213 213l-142 141l142 141l-213 212l-141 -141l-141 142l-212 -213l141 -141 z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM270 551l276 -277l411 411l-175 174l-236 -236l-102 102z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM364 700h143q4 0 11.5 -1t11 -1t6.5 3t3 9t1 11t3.5 8.5t3.5 6t5.5 4t6.5 2.5t9 1.5t9 0.5h11.5h12.5 q19 0 30 -10t11 -26q0 -22 -4 -28t-27 -22q-5 -1 -12.5 -3t-27 -13.5t-34 -27t-26.5 -46t-11 -68.5h200q5 3 14 8t31.5 25.5t39.5 45.5t31 69t14 94q0 51 -17.5 89t-42 58t-58.5 32t-58.5 15t-51.5 3q-50 0 -90.5 -12t-75 -38.5t-53.5 -74.5t-19 -114zM500 300h200v100h-200 v-100z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM400 300h400v100h-100v300h-300v-100h100v-200h-100v-100zM500 800h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M0 500v200h195q31 125 98.5 199.5t206.5 100.5v200h200v-200q54 -20 113 -60t112.5 -105.5t71.5 -134.5h203v-200h-203q-25 -102 -116.5 -186t-180.5 -117v-197h-200v197q-140 27 -208 102.5t-98 200.5h-194zM290 500q24 -73 79.5 -127.5t130.5 -78.5v206h200v-206 q149 48 201 206h-201v200h200q-25 74 -75.5 127t-124.5 77v-204h-200v203q-75 -23 -130 -77t-79 -126h209v-200h-210z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM356 465l135 135 l-135 135l109 109l135 -135l135 135l109 -109l-135 -135l135 -135l-109 -109l-135 135l-135 -135z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM322 537l141 141 l87 -87l204 205l142 -142l-346 -345z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -115 62 -215l568 567q-100 62 -216 62q-171 0 -292.5 -121.5t-121.5 -292.5zM391 245q97 -59 209 -59q171 0 292.5 121.5t121.5 292.5 q0 112 -59 209z" />
|
||||
<glyph unicode="" d="M0 547l600 453v-300h600v-300h-600v-301z" />
|
||||
<glyph unicode="" d="M0 400v300h600v300l600 -453l-600 -448v301h-600z" />
|
||||
<glyph unicode="" d="M204 600l450 600l444 -600h-298v-600h-300v600h-296z" />
|
||||
<glyph unicode="" d="M104 600h296v600h300v-600h298l-449 -600z" />
|
||||
<glyph unicode="" d="M0 200q6 132 41 238.5t103.5 193t184 138t271.5 59.5v271l600 -453l-600 -448v301q-95 -2 -183 -20t-170 -52t-147 -92.5t-100 -135.5z" />
|
||||
<glyph unicode="" d="M0 0v400l129 -129l294 294l142 -142l-294 -294l129 -129h-400zM635 777l142 -142l294 294l129 -129v400h-400l129 -129z" />
|
||||
<glyph unicode="" d="M34 176l295 295l-129 129h400v-400l-129 130l-295 -295zM600 600v400l129 -129l295 295l142 -141l-295 -295l129 -130h-400z" />
|
||||
<glyph unicode="" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5t224.5 -45.5t184 -123t123 -184t45.5 -224.5t-45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5zM456 851l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5 t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5h-207q-21 0 -33 -14.5t-8 -34.5zM500 300h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M0 800h100v-200h400v300h200v-300h400v200h100v100h-111q1 1 1 6.5t-1.5 15t-3.5 17.5l-34 172q-11 39 -41.5 63t-69.5 24q-32 0 -61 -17l-239 -144q-22 -13 -40 -35q-19 24 -40 36l-238 144q-33 18 -62 18q-39 0 -69.5 -23t-40.5 -61l-35 -177q-2 -8 -3 -18t-1 -15v-6 h-111v-100zM100 0h400v400h-400v-400zM200 900q-3 0 14 48t36 96l18 47l213 -191h-281zM700 0v400h400v-400h-400zM731 900l202 197q5 -12 12 -32.5t23 -64t25 -72t7 -28.5h-269z" />
|
||||
<glyph unicode="" d="M0 -22v143l216 193q-9 53 -13 83t-5.5 94t9 113t38.5 114t74 124q47 60 99.5 102.5t103 68t127.5 48t145.5 37.5t184.5 43.5t220 58.5q0 -189 -22 -343t-59 -258t-89 -181.5t-108.5 -120t-122 -68t-125.5 -30t-121.5 -1.5t-107.5 12.5t-87.5 17t-56.5 7.5l-99 -55z M238.5 300.5q19.5 -6.5 86.5 76.5q55 66 367 234q70 38 118.5 69.5t102 79t99 111.5t86.5 148q22 50 24 60t-6 19q-7 5 -17 5t-26.5 -14.5t-33.5 -39.5q-35 -51 -113.5 -108.5t-139.5 -89.5l-61 -32q-369 -197 -458 -401q-48 -111 -28.5 -117.5z" />
|
||||
<glyph unicode="" d="M111 408q0 -33 5 -63q9 -56 44 -119.5t105 -108.5q31 -21 64 -16t62 23.5t57 49.5t48 61.5t35 60.5q32 66 39 184.5t-13 157.5q79 -80 122 -164t26 -184q-5 -33 -20.5 -69.5t-37.5 -80.5q-10 -19 -14.5 -29t-12 -26t-9 -23.5t-3 -19t2.5 -15.5t11 -9.5t19.5 -5t30.5 2.5 t42 8q57 20 91 34t87.5 44.5t87 64t65.5 88.5t47 122q38 172 -44.5 341.5t-246.5 278.5q22 -44 43 -129q39 -159 -32 -154q-15 2 -33 9q-79 33 -120.5 100t-44 175.5t48.5 257.5q-13 -8 -34 -23.5t-72.5 -66.5t-88.5 -105.5t-60 -138t-8 -166.5q2 -12 8 -41.5t8 -43t6 -39.5 t3.5 -39.5t-1 -33.5t-6 -31.5t-13.5 -24t-21 -20.5t-31 -12q-38 -10 -67 13t-40.5 61.5t-15 81.5t10.5 75q-52 -46 -83.5 -101t-39 -107t-7.5 -85z" />
|
||||
<glyph unicode="" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5t145.5 -23.5t132.5 -59t116.5 -83.5t97 -90t74.5 -85.5t49 -63.5t20 -30l26 -40l-26 -40q-6 -10 -20 -30t-49 -63.5t-74.5 -85.5t-97 -90t-116.5 -83.5t-132.5 -59t-145.5 -23.5 t-145.5 23.5t-132.5 59t-116.5 83.5t-97 90t-74.5 85.5t-49 63.5t-20 30zM120 600q7 -10 40.5 -58t56 -78.5t68 -77.5t87.5 -75t103 -49.5t125 -21.5t123.5 20t100.5 45.5t85.5 71.5t66.5 75.5t58 81.5t47 66q-1 1 -28.5 37.5t-42 55t-43.5 53t-57.5 63.5t-58.5 54 q49 -74 49 -163q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l105 105q-37 24 -75 72t-57 84l-20 36z" />
|
||||
<glyph unicode="" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5q61 0 121 -17l37 142h148l-314 -1200h-148l37 143q-82 21 -165 71.5t-140 102t-109.5 112t-72 88.5t-29.5 43zM120 600q210 -282 393 -336l37 141q-107 18 -178.5 101.5t-71.5 193.5 q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l47 47l23 87q-30 28 -59 69t-44 68l-14 26zM780 161l38 145q22 15 44.5 34t46 44t40.5 44t41 50.5t33.5 43.5t33 44t24.5 34q-97 127 -140 175l39 146q67 -54 131.5 -125.5t87.5 -103.5t36 -52l26 -40l-26 -40 q-7 -12 -25.5 -38t-63.5 -79.5t-95.5 -102.5t-124 -100t-146.5 -79z" />
|
||||
<glyph unicode="" d="M-97.5 34q13.5 -34 50.5 -34h1294q37 0 50.5 35.5t-7.5 67.5l-642 1056q-20 34 -48 36.5t-48 -29.5l-642 -1066q-21 -32 -7.5 -66zM155 200l445 723l445 -723h-345v100h-200v-100h-345zM500 600l100 -300l100 300v100h-200v-100z" />
|
||||
<glyph unicode="" d="M100 262v41q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44t106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -91 100 -113v-64q0 -20 -13 -28.5t-32 0.5l-94 78h-222l-94 -78q-19 -9 -32 -0.5t-13 28.5 v64q0 22 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5z" />
|
||||
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v750h-1100v-750zM0 900h1100v150q0 21 -14.5 35.5t-35.5 14.5h-150v100h-100v-100h-500v100h-100v-100h-150q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 100v100h100v-100h-100zM100 300v100h100v-100h-100z M100 500v100h100v-100h-100zM300 100v100h100v-100h-100zM300 300v100h100v-100h-100zM300 500v100h100v-100h-100zM500 100v100h100v-100h-100zM500 300v100h100v-100h-100zM500 500v100h100v-100h-100zM700 100v100h100v-100h-100zM700 300v100h100v-100h-100zM700 500 v100h100v-100h-100zM900 100v100h100v-100h-100zM900 300v100h100v-100h-100zM900 500v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M0 200v200h259l600 600h241v198l300 -295l-300 -300v197h-159l-600 -600h-341zM0 800h259l122 -122l141 142l-181 180h-341v-200zM678 381l141 142l122 -123h159v198l300 -295l-300 -300v197h-241z" />
|
||||
<glyph unicode="" d="M0 400v600q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5z" />
|
||||
<glyph unicode="" d="M100 600v200h300v-250q0 -113 6 -145q17 -92 102 -117q39 -11 92 -11q37 0 66.5 5.5t50 15.5t36 24t24 31.5t14 37.5t7 42t2.5 45t0 47v25v250h300v-200q0 -42 -3 -83t-15 -104t-31.5 -116t-58 -109.5t-89 -96.5t-129 -65.5t-174.5 -25.5t-174.5 25.5t-129 65.5t-89 96.5 t-58 109.5t-31.5 116t-15 104t-3 83zM100 900v300h300v-300h-300zM800 900v300h300v-300h-300z" />
|
||||
<glyph unicode="" d="M-30 411l227 -227l352 353l353 -353l226 227l-578 579z" />
|
||||
<glyph unicode="" d="M70 797l580 -579l578 579l-226 227l-353 -353l-352 353z" />
|
||||
<glyph unicode="" d="M-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196zM402 1000l215 -200h381v-400h-198l299 -283l299 283h-200v600h-796z" />
|
||||
<glyph unicode="" d="M18 939q-5 24 10 42q14 19 39 19h896l38 162q5 17 18.5 27.5t30.5 10.5h94q20 0 35 -14.5t15 -35.5t-15 -35.5t-35 -14.5h-54l-201 -961q-2 -4 -6 -10.5t-19 -17.5t-33 -11h-31v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-300v-50q0 -20 -14.5 -35t-35.5 -15 t-35.5 15t-14.5 35v50h-50q-21 0 -35.5 15t-14.5 35q0 21 14.5 35.5t35.5 14.5h535l48 200h-633q-32 0 -54.5 21t-27.5 43z" />
|
||||
<glyph unicode="" d="M0 0v800h1200v-800h-1200zM0 900v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-100h-1200z" />
|
||||
<glyph unicode="" d="M1 0l300 700h1200l-300 -700h-1200zM1 400v600h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-200h-1000z" />
|
||||
<glyph unicode="" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" />
|
||||
<glyph unicode="" d="M0 600l300 298v-198h600v198l300 -298l-300 -297v197h-600v-197z" />
|
||||
<glyph unicode="" d="M0 100v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM31 400l172 739q5 22 23 41.5t38 19.5h672q19 0 37.5 -22.5t23.5 -45.5l172 -732h-1138zM800 100h100v100h-100v-100z M1000 100h100v100h-100v-100z" />
|
||||
<glyph unicode="" d="M-101 600v50q0 24 25 49t50 38l25 13v-250l-11 5.5t-24 14t-30 21.5t-24 27.5t-11 31.5zM100 500v250v8v8v7t0.5 7t1.5 5.5t2 5t3 4t4.5 3.5t6 1.5t7.5 0.5h200l675 250v-850l-675 200h-38l47 -276q2 -12 -3 -17.5t-11 -6t-21 -0.5h-8h-83q-20 0 -34.5 14t-18.5 35 q-55 337 -55 351zM1100 200v850q0 21 14.5 35.5t35.5 14.5q20 0 35 -14.5t15 -35.5v-850q0 -20 -15 -35t-35 -15q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M74 350q0 21 13.5 35.5t33.5 14.5h18l117 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3 32t29 13h94q20 0 29 -10.5t3 -29.5q-18 -36 -18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q20 0 33.5 -14.5t13.5 -35.5q0 -20 -13 -40t-31 -27q-8 -3 -23 -8.5 t-65 -20t-103 -25t-132.5 -19.5t-158.5 -9q-125 0 -245.5 20.5t-178.5 40.5l-58 20q-18 7 -31 27.5t-13 40.5zM497 110q12 -49 40 -79.5t63 -30.5t63 30.5t39 79.5q-48 -6 -102 -6t-103 6z" />
|
||||
<glyph unicode="" d="M21 445l233 -45l-78 -224l224 78l45 -233l155 179l155 -179l45 233l224 -78l-78 224l234 45l-180 155l180 156l-234 44l78 225l-224 -78l-45 233l-155 -180l-155 180l-45 -233l-224 78l78 -225l-233 -44l179 -156z" />
|
||||
<glyph unicode="" d="M0 200h200v600h-200v-600zM300 275q0 -75 100 -75h61q124 -100 139 -100h250q46 0 83 57l238 344q29 31 29 74v100q0 44 -30.5 84.5t-69.5 40.5h-328q28 118 28 125v150q0 44 -30.5 84.5t-69.5 40.5h-50q-27 0 -51 -20t-38 -48l-96 -198l-145 -196q-20 -26 -20 -63v-400z M400 300v375l150 213l100 212h50v-175l-50 -225h450v-125l-250 -375h-214l-136 100h-100z" />
|
||||
<glyph unicode="" d="M0 400v600h200v-600h-200zM300 525v400q0 75 100 75h61q124 100 139 100h250q46 0 83 -57l238 -344q29 -31 29 -74v-100q0 -44 -30.5 -84.5t-69.5 -40.5h-328q28 -118 28 -125v-150q0 -44 -30.5 -84.5t-69.5 -40.5h-50q-27 0 -51 20t-38 48l-96 198l-145 196 q-20 26 -20 63zM400 525l150 -212l100 -213h50v175l-50 225h450v125l-250 375h-214l-136 -100h-100v-375z" />
|
||||
<glyph unicode="" d="M8 200v600h200v-600h-200zM308 275v525q0 17 14 35.5t28 28.5l14 9l362 230q14 6 25 6q17 0 29 -12l109 -112q14 -14 14 -34q0 -18 -11 -32l-85 -121h302q85 0 138.5 -38t53.5 -110t-54.5 -111t-138.5 -39h-107l-130 -339q-7 -22 -20.5 -41.5t-28.5 -19.5h-341 q-7 0 -90 81t-83 94zM408 289l100 -89h293l131 339q6 21 19.5 41t28.5 20h203q16 0 25 15t9 36q0 20 -9 34.5t-25 14.5h-457h-6.5h-7.5t-6.5 0.5t-6 1t-5 1.5t-5.5 2.5t-4 4t-4 5.5q-5 12 -5 20q0 14 10 27l147 183l-86 83l-339 -236v-503z" />
|
||||
<glyph unicode="" d="M-101 651q0 72 54 110t139 38l302 -1l-85 121q-11 16 -11 32q0 21 14 34l109 113q13 12 29 12q11 0 25 -6l365 -230q7 -4 17 -10.5t26.5 -26t16.5 -36.5v-526q0 -13 -86 -93.5t-94 -80.5h-341q-16 0 -29.5 20t-19.5 41l-130 339h-107q-84 0 -139 39t-55 111zM-1 601h222 q15 0 28.5 -20.5t19.5 -40.5l131 -339h293l107 89v502l-343 237l-87 -83l145 -184q10 -11 10 -26q0 -11 -5 -20q-1 -3 -3.5 -5.5l-4 -4t-5 -2.5t-5.5 -1.5t-6.5 -1t-6.5 -0.5h-7.5h-6.5h-476v-100zM1000 201v600h200v-600h-200z" />
|
||||
<glyph unicode="" d="M97 719l230 -363q4 -6 10.5 -15.5t26 -25t36.5 -15.5h525q13 0 94 83t81 90v342q0 15 -20 28.5t-41 19.5l-339 131v106q0 84 -39 139t-111 55t-110 -53.5t-38 -138.5v-302l-121 84q-15 12 -33.5 11.5t-32.5 -13.5l-112 -110q-22 -22 -6 -53zM172 739l83 86l183 -146 q22 -18 47 -5q3 1 5.5 3.5l4 4t2.5 5t1.5 5.5t1 6.5t0.5 6.5v7.5v6.5v456q0 22 25 31t50 -0.5t25 -30.5v-202q0 -16 20 -29.5t41 -19.5l339 -130v-294l-89 -100h-503zM400 0v200h600v-200h-600z" />
|
||||
<glyph unicode="" d="M2 585q-16 -31 6 -53l112 -110q13 -13 32 -13.5t34 10.5l121 85q0 -51 -0.5 -153.5t-0.5 -148.5q0 -84 38.5 -138t110.5 -54t111 55t39 139v106l339 131q20 6 40.5 19.5t20.5 28.5v342q0 7 -81 90t-94 83h-525q-17 0 -35.5 -14t-28.5 -28l-10 -15zM77 565l236 339h503 l89 -100v-294l-340 -130q-20 -6 -40 -20t-20 -29v-202q0 -22 -25 -31t-50 0t-25 31v456v14.5t-1.5 11.5t-5 12t-9.5 7q-24 13 -46 -5l-184 -146zM305 1104v200h600v-200h-600z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM298 701l2 -201h300l-2 -194l402 294l-402 298v-197h-300z" />
|
||||
<glyph unicode="" d="M0 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t231.5 47.5q122 0 232.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-218 -217.5t-300 -80t-299.5 80t-217.5 217.5t-80 299.5zM200 600l402 -294l-2 194h300l2 201h-300v197z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600h200v-300h200v300h200l-300 400z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600l300 -400l300 400h-200v300h-200v-300h-200z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM254 780q-8 -33 5.5 -92.5t7.5 -87.5q0 -9 17 -44t16 -60 q12 0 23 -5.5t23 -15t20 -13.5q24 -12 108 -42q22 -8 53 -31.5t59.5 -38.5t57.5 -11q8 -18 -15 -55t-20 -57q42 -71 87 -80q0 -6 -3 -15.5t-3.5 -14.5t4.5 -17q104 -3 221 112q30 29 47 47t34.5 49t20.5 62q-14 9 -37 9.5t-36 7.5q-14 7 -49 15t-52 19q-9 0 -39.5 -0.5 t-46.5 -1.5t-39 -6.5t-39 -16.5q-50 -35 -66 -12q-4 2 -3.5 25.5t0.5 25.5q-6 13 -26.5 17t-24.5 7q2 22 -2 41t-16.5 28t-38.5 -20q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q12 -19 32 -37.5t34 -27.5l14 -8q0 3 9.5 39.5t5.5 57.5 q-4 23 14.5 44.5t22.5 31.5q5 14 10 35t8.5 31t15.5 22.5t34 21.5q-6 18 10 37q8 0 23.5 -1.5t24.5 -1.5t20.5 4.5t20.5 15.5q-10 23 -30.5 42.5t-38 30t-49 26.5t-43.5 23q11 39 2 44q31 -13 58 -14.5t39 3.5l11 4q7 36 -16.5 53.5t-64.5 28.5t-56 23q-19 -3 -37 0 q-15 -12 -36.5 -21t-34.5 -12t-44 -8t-39 -6q-15 -3 -45.5 0.5t-45.5 -2.5q-21 -7 -52 -26.5t-34 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -90.5t-29.5 -79.5zM518 916q3 12 16 30t16 25q10 -10 18.5 -10t14 6t14.5 14.5t16 12.5q0 -24 17 -66.5t17 -43.5 q-9 2 -31 5t-36 5t-32 8t-30 14zM692 1003h1h-1z" />
|
||||
<glyph unicode="" d="M0 164.5q0 21.5 15 37.5l600 599q-33 101 6 201.5t135 154.5q164 92 306 -9l-259 -138l145 -232l251 126q13 -175 -151 -267q-123 -70 -253 -23l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1220" d="M0 196v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 596v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5zM0 996v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM600 596h500v100h-500v-100zM800 196h300v100h-300v-100zM900 996h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M100 1100v100h1000v-100h-1000zM150 1000h900l-350 -500v-300l-200 -200v500z" />
|
||||
<glyph unicode="" d="M0 200v200h1200v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500z M500 1000h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M0 0v400l129 -129l200 200l142 -142l-200 -200l129 -129h-400zM0 800l129 129l200 -200l142 142l-200 200l129 129h-400v-400zM729 329l142 142l200 -200l129 129v-400h-400l129 129zM729 871l200 200l-129 129h400v-400l-129 129l-200 -200z" />
|
||||
<glyph unicode="" d="M0 596q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 596q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM291 655 q0 23 15.5 38.5t38.5 15.5t39 -16t16 -38q0 -23 -16 -39t-39 -16q-22 0 -38 16t-16 39zM400 850q0 22 16 38.5t39 16.5q22 0 38 -16t16 -39t-16 -39t-38 -16q-23 0 -39 16.5t-16 38.5zM514 609q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 22 16 38.5t39 16.5 q22 0 38 -16t16 -39t-16 -39t-38 -16q-14 0 -29 10l-55 -145q17 -22 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5zM800 655q0 22 16 38t39 16t38.5 -15.5t15.5 -38.5t-16 -39t-38 -16q-23 0 -39 16t-16 39z" />
|
||||
<glyph unicode="" d="M-40 375q-13 -95 35 -173q35 -57 94 -89t129 -32q63 0 119 28q33 16 65 40.5t52.5 45.5t59.5 64q40 44 57 61l394 394q35 35 47 84t-3 96q-27 87 -117 104q-20 2 -29 2q-46 0 -78.5 -16.5t-67.5 -51.5l-389 -396l-7 -7l69 -67l377 373q20 22 39 38q23 23 50 23 q38 0 53 -36q16 -39 -20 -75l-547 -547q-52 -52 -125 -52q-55 0 -100 33t-54 96q-5 35 2.5 66t31.5 63t42 50t56 54q24 21 44 41l348 348q52 52 82.5 79.5t84 54t107.5 26.5q25 0 48 -4q95 -17 154 -94.5t51 -175.5q-7 -101 -98 -192l-252 -249l-253 -256l7 -7l69 -60 l517 511q67 67 95 157t11 183q-16 87 -67 154t-130 103q-69 33 -152 33q-107 0 -197 -55q-40 -24 -111 -95l-512 -512q-68 -68 -81 -163z" />
|
||||
<glyph unicode="" d="M80 784q0 131 98.5 229.5t230.5 98.5q143 0 241 -129q103 129 246 129q129 0 226 -98.5t97 -229.5q0 -46 -17.5 -91t-61 -99t-77 -89.5t-104.5 -105.5q-197 -191 -293 -322l-17 -23l-16 23q-43 58 -100 122.5t-92 99.5t-101 100q-71 70 -104.5 105.5t-77 89.5t-61 99 t-17.5 91zM250 784q0 -27 30.5 -70t61.5 -75.5t95 -94.5l22 -22q93 -90 190 -201q82 92 195 203l12 12q64 62 97.5 97t64.5 79t31 72q0 71 -48 119.5t-105 48.5q-74 0 -132 -83l-118 -171l-114 174q-51 80 -123 80q-60 0 -109.5 -49.5t-49.5 -118.5z" />
|
||||
<glyph unicode="" d="M57 353q0 -95 66 -159l141 -142q68 -66 159 -66q93 0 159 66l283 283q66 66 66 159t-66 159l-141 141q-8 9 -19 17l-105 -105l212 -212l-389 -389l-247 248l95 95l-18 18q-46 45 -75 101l-55 -55q-66 -66 -66 -159zM269 706q0 -93 66 -159l141 -141q7 -7 19 -17l105 105 l-212 212l389 389l247 -247l-95 -96l18 -17q47 -49 77 -100l29 29q35 35 62.5 88t27.5 96q0 93 -66 159l-141 141q-66 66 -159 66q-95 0 -159 -66l-283 -283q-66 -64 -66 -159z" />
|
||||
<glyph unicode="" d="M200 100v953q0 21 30 46t81 48t129 38t163 15t162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5zM300 300h600v700h-600v-700zM496 150q0 -43 30.5 -73.5t73.5 -30.5t73.5 30.5t30.5 73.5t-30.5 73.5t-73.5 30.5 t-73.5 -30.5t-30.5 -73.5z" />
|
||||
<glyph unicode="" d="M0 0l303 380l207 208l-210 212h300l267 279l-35 36q-15 14 -15 35t15 35q14 15 35 15t35 -15l283 -282q15 -15 15 -36t-15 -35q-14 -15 -35 -15t-35 15l-36 35l-279 -267v-300l-212 210l-208 -207z" />
|
||||
<glyph unicode="" d="M295 433h139q5 -77 48.5 -126.5t117.5 -64.5v335q-6 1 -15.5 4t-11.5 3q-46 14 -79 26.5t-72 36t-62.5 52t-40 72.5t-16.5 99q0 92 44 159.5t109 101t144 40.5v78h100v-79q38 -4 72.5 -13.5t75.5 -31.5t71 -53.5t51.5 -84t24.5 -118.5h-159q-8 72 -35 109.5t-101 50.5 v-307l64 -14q34 -7 64 -16.5t70 -31.5t67.5 -52t47.5 -80.5t20 -112.5q0 -139 -89 -224t-244 -96v-77h-100v78q-152 17 -237 104q-40 40 -52.5 93.5t-15.5 139.5zM466 889q0 -29 8 -51t16.5 -34t29.5 -22.5t31 -13.5t38 -10q7 -2 11 -3v274q-61 -8 -97.5 -37.5t-36.5 -102.5 zM700 237q170 18 170 151q0 64 -44 99.5t-126 60.5v-311z" />
|
||||
<glyph unicode="" d="M100 600v100h166q-24 49 -44 104q-10 26 -14.5 55.5t-3 72.5t25 90t68.5 87q97 88 263 88q129 0 230 -89t101 -208h-153q0 52 -34 89.5t-74 51.5t-76 14q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -28 16.5 -69.5t28 -62.5t41.5 -72h241v-100h-197q8 -50 -2.5 -115 t-31.5 -94q-41 -59 -99 -113q35 11 84 18t70 7q33 1 103 -16t103 -17q76 0 136 30l50 -147q-41 -25 -80.5 -36.5t-59 -13t-61.5 -1.5q-23 0 -128 33t-155 29q-39 -4 -82 -17t-66 -25l-24 -11l-55 145l16.5 11t15.5 10t13.5 9.5t14.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM602 900l298 300l298 -300h-198v-900h-200v900h-198z" />
|
||||
<glyph unicode="" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v200h100v-100h200v-100h-300zM700 400v100h300v-200h-99v-100h-100v100h99v100h-200zM700 700v500h300v-500h-100v100h-100v-100h-100zM801 900h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v500h300v-500h-100v100h-100v-100h-100zM700 700v200h100v-100h200v-100h-300zM700 1100v100h300v-200h-99v-100h-100v100h99v100h-200zM801 200h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 100v400h300v-500h-100v100h-200zM800 1100v100h200v-500h-100v400h-100zM901 200h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 400v100h200v-500h-100v400h-100zM800 800v400h300v-500h-100v100h-200zM901 900h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h500v-200h-500zM700 400v200h400v-200h-400zM700 700v200h300v-200h-300zM700 1000v200h200v-200h-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h200v-200h-200zM700 400v200h300v-200h-300zM700 700v200h400v-200h-400zM700 1000v200h500v-200h-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q162 0 281 -118.5t119 -281.5v-300q0 -165 -118.5 -282.5t-281.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 163 119 281.5t281 118.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-163 0 -281.5 117.5t-118.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM400 300l333 250l-333 250v-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 163 117.5 281.5t282.5 118.5h300q163 0 281.5 -119t118.5 -281v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 700l250 -333l250 333h-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -162 -118.5 -281t-281.5 -119h-300q-165 0 -282.5 118.5t-117.5 281.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 400h500l-250 333z" />
|
||||
<glyph unicode="" d="M0 400v300h300v200l400 -350l-400 -350v200h-300zM500 0v200h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-500v200h400q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-400z" />
|
||||
<glyph unicode="" d="M217 519q8 -19 31 -19h302q-155 -438 -160 -458q-5 -21 4 -32l9 -8h9q14 0 26 15q11 13 274.5 321.5t264.5 308.5q14 19 5 36q-8 17 -31 17l-301 -1q1 4 78 219.5t79 227.5q2 15 -5 27l-9 9h-9q-15 0 -25 -16q-4 -6 -98 -111.5t-228.5 -257t-209.5 -237.5q-16 -19 -6 -41 z" />
|
||||
<glyph unicode="" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q47 0 100 15v185h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h500v185q-14 4 -114 7.5t-193 5.5l-93 2q-165 0 -282.5 -117.5t-117.5 -282.5v-300zM600 400v300h300v200l400 -350l-400 -350v200h-300z " />
|
||||
<glyph unicode="" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q163 0 281.5 117.5t118.5 282.5v98l-78 73l-122 -123v-148q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h156l118 122l-74 78h-100q-165 0 -282.5 -117.5t-117.5 -282.5 v-300zM496 709l353 342l-149 149h500v-500l-149 149l-342 -353z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM406 600 q0 80 57 137t137 57t137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 800l445 -500l450 500h-295v400h-300v-400h-300zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 700h300v-300h300v300h295l-445 500zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 705l305 -305l596 596l-154 155l-442 -442l-150 151zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 988l97 -98l212 213l-97 97zM200 400l697 1l3 699l-250 -239l-149 149l-212 -212l149 -149zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM200 612l212 -212l98 97l-213 212zM300 1200l239 -250l-149 -149l212 -212l149 148l249 -237l-1 697zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M23 415l1177 784v-1079l-475 272l-310 -393v416h-392zM494 210l672 938l-672 -712v-226z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-850q0 -21 -15 -35.5t-35 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-218l-276 -275l-120 120l-126 -127h-378v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM581 306l123 123l120 -120l353 352l123 -123l-475 -476zM600 1000h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-269l-103 -103l-170 170l-298 -298h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200zM700 133l170 170l-170 170l127 127l170 -170l170 170l127 -128l-170 -169l170 -170 l-127 -127l-170 170l-170 -170z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-300h-400v-200h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300l300 -300l300 300h-200v300h-200v-300h-200zM600 1000v200h100v-200h-100z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-402l-200 200l-298 -298h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300h200v-300h200v300h200l-300 300zM600 1000v200h100v-200h-100z" />
|
||||
<glyph unicode="" d="M0 250q0 -21 14.5 -35.5t35.5 -14.5h1100q21 0 35.5 14.5t14.5 35.5v550h-1200v-550zM0 900h1200v150q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 300v200h400v-200h-400z" />
|
||||
<glyph unicode="" d="M0 400l300 298v-198h400v-200h-400v-198zM100 800v200h100v-200h-100zM300 800v200h100v-200h-100zM500 800v200h400v198l300 -298l-300 -298v198h-400zM800 300v200h100v-200h-100zM1000 300h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M100 700v400l50 100l50 -100v-300h100v300l50 100l50 -100v-300h100v300l50 100l50 -100v-400l-100 -203v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447zM800 597q0 -29 10.5 -55.5t25 -43t29 -28.5t25.5 -18l10 -5v-397q0 -21 14.5 -35.5 t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v1106q0 31 -18 40.5t-44 -7.5l-276 -116q-25 -17 -43.5 -51.5t-18.5 -65.5v-359z" />
|
||||
<glyph unicode="" d="M100 0h400v56q-75 0 -87.5 6t-12.5 44v394h500v-394q0 -38 -12.5 -44t-87.5 -6v-56h400v56q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v888q0 22 25 34.5t50 13.5l25 2v56h-400v-56q75 0 87.5 -6t12.5 -44v-394h-500v394q0 38 12.5 44t87.5 6v56h-400v-56q4 0 11 -0.5 t24 -3t30 -7t24 -15t11 -24.5v-888q0 -22 -25 -34.5t-50 -13.5l-25 -2v-56z" />
|
||||
<glyph unicode="" d="M0 300q0 -41 29.5 -70.5t70.5 -29.5h300q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-300q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM100 100h400l200 200h105l295 98v-298h-425l-100 -100h-375zM100 300v200h300v-200h-300zM100 600v200h300v-200h-300z M100 1000h400l200 -200v-98l295 98h105v200h-425l-100 100h-375zM700 402v163l400 133v-163z" />
|
||||
<glyph unicode="" d="M16.5 974.5q0.5 -21.5 16 -90t46.5 -140t104 -177.5t175 -208q103 -103 207.5 -176t180 -103.5t137 -47t92.5 -16.5l31 1l163 162q17 18 13.5 41t-22.5 37l-192 136q-19 14 -45 12t-42 -19l-118 -118q-142 101 -268 227t-227 268l118 118q17 17 20 41.5t-11 44.5 l-139 194q-14 19 -36.5 22t-40.5 -14l-162 -162q-1 -11 -0.5 -32.5z" />
|
||||
<glyph unicode="" d="M0 50v212q0 20 10.5 45.5t24.5 39.5l365 303v50q0 4 1 10.5t12 22.5t30 28.5t60 23t97 10.5t97 -10t60 -23.5t30 -27.5t12 -24l1 -10v-50l365 -303q14 -14 24.5 -39.5t10.5 -45.5v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-20 0 -35 14.5t-15 35.5zM0 712 q0 -21 14.5 -33.5t34.5 -8.5l202 33q20 4 34.5 21t14.5 38v146q141 24 300 24t300 -24v-146q0 -21 14.5 -38t34.5 -21l202 -33q20 -4 34.5 8.5t14.5 33.5v200q-6 8 -19 20.5t-63 45t-112 57t-171 45t-235 20.5q-92 0 -175 -10.5t-141.5 -27t-108.5 -36.5t-81.5 -40 t-53.5 -36.5t-31 -27.5l-9 -10v-200z" />
|
||||
<glyph unicode="" d="M100 0v100h1100v-100h-1100zM175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250z" />
|
||||
<glyph unicode="" d="M100 0h300v400q0 41 -29.5 70.5t-70.5 29.5h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-400zM500 0v1000q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-1000h-300zM900 0v700q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-700h-300z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h100v200h100v-200h100v500h-100v-200h-100v200h-100v-500zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v100h-200v300h200v100h-300v-500zM600 300h300v100h-200v300h200v100h-300v-500z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 550l300 -150v300zM600 400l300 150l-300 150v-300z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300v500h700v-500h-700zM300 400h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130v-300zM575 549 q0 -65 27 -107t68 -42h130v300h-130q-38 0 -66.5 -43t-28.5 -108z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v400h-200v100h-100v-500zM301 400v200h100v-200h-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 700v100h300v-300h-99v-100h-100v100h99v200h-200zM201 300v100h100v-100h-100zM601 300v100h100v-100h-100z M700 700v100h200v-500h-100v400h-100z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 500v200 l100 100h300v-100h-300v-200h300v-100h-300z" />
|
||||
<glyph unicode="" d="M0 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 400v400h300 l100 -100v-100h-100v100h-200v-100h200v-100h-200v-100h-100zM700 400v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M-14 494q0 -80 56.5 -137t135.5 -57h222v300h400v-300h128q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200h200v300h200v-300h200 l-300 -300z" />
|
||||
<glyph unicode="" d="M-14 494q0 -80 56.5 -137t135.5 -57h8l414 414l403 -403q94 26 154.5 104.5t60.5 178.5q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200l300 300 l300 -300h-200v-300h-200v300h-200z" />
|
||||
<glyph unicode="" d="M100 200h400v-155l-75 -45h350l-75 45v155h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170z" />
|
||||
<glyph unicode="" d="M121 700q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350l-75 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5 t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -11.5t1 -11.5q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5z" />
|
||||
</font>
|
||||
</defs></svg>
|
After Width: | Height: | Size: 62 KiB |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,196 @@
|
||||
<!--
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>%title%</title>
|
||||
<link rel="stylesheet" href="css/bootstrap.css">
|
||||
<link rel="stylesheet" href="css/bootstrap-theme.css">
|
||||
<link rel="stylesheet" href="css/jquery.fileupload.css">
|
||||
<link rel="stylesheet" href="css/index.css">
|
||||
<!--[if lt IE 9]>
|
||||
<script type="text/javascript" src="js/html5shiv.min.js"></script>
|
||||
<script type="text/javascript" src="js/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<script type="text/javascript" src="js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.ui.widget.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.jeditable.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.fileupload.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.iframe-transport.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/tmpl.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var _device = "%device%";
|
||||
</script>
|
||||
<script type="text/javascript" src="js/index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="page-header">
|
||||
<h1>%header%</h1>
|
||||
</div>
|
||||
|
||||
%prologue%
|
||||
|
||||
<div id="alerts"></div>
|
||||
|
||||
<div class="btn-toolbar">
|
||||
<button type="button" class="btn btn-primary fileinput-button" id="upload-file">
|
||||
<span class="glyphicon glyphicon-upload"></span> Upload Files…
|
||||
<input id="fileupload" type="file" name="files[]" multiple>
|
||||
</button>
|
||||
<button type="button" class="btn btn-success" id="create-folder">
|
||||
<span class="glyphicon glyphicon-folder-close"></span> Create Folder…
|
||||
</button>
|
||||
<button type="button" class="btn btn-default" id="reload">
|
||||
<span class="glyphicon glyphicon-refresh"></span> Refresh
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default uploading">
|
||||
<div class="panel-heading">File Uploads in Progress</div>
|
||||
<table class="table table-striped"><tbody id="uploads"></tbody></table>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<ol class="breadcrumb" id="path"></ol>
|
||||
</div>
|
||||
<table class="table table-striped"><tbody id="listing"></tbody></table>
|
||||
</div>
|
||||
|
||||
%epilogue%
|
||||
|
||||
<p class="footer">%footer%</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="create-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">Create Folder</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Please enter the name of the folder to be created:</p>
|
||||
<form onsubmit="return false">
|
||||
<input type="text" autocomplete="off" id="create-input">
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" id="create-confirm">Create Folder</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="move-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">Move Item</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Please enter the new location for this item:</p>
|
||||
<form onsubmit="return false">
|
||||
<input type="text" autocomplete="off" id="move-input">
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" id="move-confirm">Move Item</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/x-tmpl" id="template-listing">
|
||||
<tr class="row-file">
|
||||
<td class="column-icon">
|
||||
{% if (o.size != null) { %}
|
||||
<button type="button" class="btn btn-default btn-xs button-download">
|
||||
<span class="glyphicon glyphicon-download-alt"></span>
|
||||
</button>
|
||||
{% } else { %}
|
||||
<button type="button" class="btn btn-default btn-xs button-open">
|
||||
<span class="glyphicon glyphicon-folder-open"></span>
|
||||
</button>
|
||||
{% } %}
|
||||
</td>
|
||||
<td class="column-name"><p class="edit">{%=o.name%}</p></td>
|
||||
<td class="column-size">
|
||||
{% if (o.size != null) { %}
|
||||
<p>{%=formatFileSize(o.size)%}</p>
|
||||
{% } %}
|
||||
</td>
|
||||
<td class="column-move">
|
||||
<button type="button" class="btn btn-default btn-xs button-move">
|
||||
<span class="glyphicon glyphicon glyphicon-share-alt"></span>
|
||||
</button>
|
||||
</td>
|
||||
<td class="column-delete">
|
||||
<button type="button" class="btn btn-danger btn-xs button-delete">
|
||||
<span class="glyphicon glyphicon-trash"></span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</script>
|
||||
|
||||
<script type="text/x-tmpl" id="template-uploads">
|
||||
<tr class="row-file">
|
||||
<td class="column-icon">
|
||||
<button type="button" class="btn btn-warning btn-xs button-cancel">
|
||||
<span class="glyphicon glyphicon-ban-circle"></span>
|
||||
</button>
|
||||
</td>
|
||||
<td class="column-path"><p>{%=o.path%}</p></td>
|
||||
<td class="column-progress">
|
||||
<div class="progress">
|
||||
<div class="progress-bar" id="progress-bar"></div>
|
||||
</div>
|
||||
</ts>
|
||||
</tr>
|
||||
</script>
|
||||
|
||||
<script type="text/x-tmpl" id="template-alert">
|
||||
<div class="alert alert-{%=o.level%} alert-dismissable">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong>{%=o.title%}</strong>{%=o.description%}
|
||||
</div>
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,8 @@
|
||||
/*
|
||||
HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
|
||||
*/
|
||||
(function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();
|
||||
a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x<style>article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}</style>";
|
||||
c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="<xyz></xyz>";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||
|
||||
"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);
|
||||
if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d<h;d++)c.createElement(e[d]);return c}};l.html5=e;q(f)})(this,document);
|
@ -0,0 +1,316 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
var ENTER_KEYCODE = 13;
|
||||
|
||||
var _path = null;
|
||||
var _pendingReloads = [];
|
||||
var _reloadingDisabled = 0;
|
||||
|
||||
function formatFileSize(bytes) {
|
||||
if (bytes >= 1000000000) {
|
||||
return (bytes / 1000000000).toFixed(2) + ' GB';
|
||||
}
|
||||
if (bytes >= 1000000) {
|
||||
return (bytes / 1000000).toFixed(2) + ' MB';
|
||||
}
|
||||
return (bytes / 1000).toFixed(2) + ' KB';
|
||||
}
|
||||
|
||||
function _showError(message, textStatus, errorThrown) {
|
||||
$("#alerts").prepend(tmpl("template-alert", {
|
||||
level: "danger",
|
||||
title: (errorThrown != "" ? errorThrown : textStatus) + ": ",
|
||||
description: message
|
||||
}));
|
||||
}
|
||||
|
||||
function _disableReloads() {
|
||||
_reloadingDisabled += 1;
|
||||
}
|
||||
|
||||
function _enableReloads() {
|
||||
_reloadingDisabled -= 1;
|
||||
|
||||
if (_pendingReloads.length > 0) {
|
||||
_reload(_pendingReloads.shift());
|
||||
}
|
||||
}
|
||||
|
||||
function _reload(path) {
|
||||
if (_reloadingDisabled) {
|
||||
if ($.inArray(path, _pendingReloads) < 0) {
|
||||
_pendingReloads.push(path);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_disableReloads();
|
||||
$.ajax({
|
||||
url: 'list',
|
||||
type: 'GET',
|
||||
data: {path: path},
|
||||
dataType: 'json'
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
_showError("Failed retrieving contents of \"" + path + "\"", textStatus, errorThrown);
|
||||
}).done(function(data, textStatus, jqXHR) {
|
||||
var scrollPosition = $(document).scrollTop();
|
||||
|
||||
if (path != _path) {
|
||||
$("#path").empty();
|
||||
if (path == "/") {
|
||||
$("#path").append('<li class="active">' + _device + '</li>');
|
||||
} else {
|
||||
$("#path").append('<li data-path="/"><a>' + _device + '</a></li>');
|
||||
var components = path.split("/").slice(1, -1);
|
||||
for (var i = 0; i < components.length - 1; ++i) {
|
||||
var subpath = "/" + components.slice(0, i + 1).join("/") + "/";
|
||||
$("#path").append('<li data-path="' + subpath + '"><a>' + components[i] + '</a></li>');
|
||||
}
|
||||
$("#path > li").click(function(event) {
|
||||
_reload($(this).data("path"));
|
||||
event.preventDefault();
|
||||
});
|
||||
$("#path").append('<li class="active">' + components[components.length - 1] + '</li>');
|
||||
}
|
||||
_path = path;
|
||||
}
|
||||
|
||||
$("#listing").empty();
|
||||
for (var i = 0, file; file = data[i]; ++i) {
|
||||
$(tmpl("template-listing", file)).data(file).appendTo("#listing");
|
||||
}
|
||||
|
||||
$(".edit").editable(function(value, settings) {
|
||||
var name = $(this).parent().parent().data("name");
|
||||
if (value != name) {
|
||||
var path = $(this).parent().parent().data("path");
|
||||
$.ajax({
|
||||
url: 'move',
|
||||
type: 'POST',
|
||||
data: {oldPath: path, newPath: _path + value},
|
||||
dataType: 'json'
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
_showError("Failed moving \"" + path + "\" to \"" + _path + value + "\"", textStatus, errorThrown);
|
||||
}).always(function() {
|
||||
_reload(_path);
|
||||
});
|
||||
}
|
||||
return value;
|
||||
}, {
|
||||
onedit: function(settings, original) {
|
||||
_disableReloads();
|
||||
},
|
||||
onsubmit: function(settings, original) {
|
||||
_enableReloads();
|
||||
},
|
||||
onreset: function(settings, original) {
|
||||
_enableReloads();
|
||||
},
|
||||
tooltip: 'Click to rename...'
|
||||
});
|
||||
|
||||
$(".button-download").click(function(event) {
|
||||
var path = $(this).parent().parent().data("path");
|
||||
setTimeout(function() {
|
||||
window.location = "download?path=" + encodeURIComponent(path);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
$(".button-open").click(function(event) {
|
||||
var path = $(this).parent().parent().data("path");
|
||||
_reload(path);
|
||||
});
|
||||
|
||||
$(".button-move").click(function(event) {
|
||||
var path = $(this).parent().parent().data("path");
|
||||
if (path[path.length - 1] == "/") {
|
||||
path = path.slice(0, path.length - 1);
|
||||
}
|
||||
$("#move-input").data("path", path);
|
||||
$("#move-input").val(path);
|
||||
$("#move-modal").modal("show");
|
||||
});
|
||||
|
||||
$(".button-delete").click(function(event) {
|
||||
var path = $(this).parent().parent().data("path");
|
||||
$.ajax({
|
||||
url: 'delete',
|
||||
type: 'POST',
|
||||
data: {path: path},
|
||||
dataType: 'json'
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
_showError("Failed deleting \"" + path + "\"", textStatus, errorThrown);
|
||||
}).always(function() {
|
||||
_reload(_path);
|
||||
});
|
||||
});
|
||||
|
||||
$(document).scrollTop(scrollPosition);
|
||||
}).always(function() {
|
||||
_enableReloads();
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
// Workaround Firefox and IE not showing file selection dialog when clicking on "upload-file" <button>
|
||||
// Making it a <div> instead also works but then it the button doesn't work anymore with tab selection or accessibility
|
||||
$("#upload-file").click(function(event) {
|
||||
$("#fileupload").click();
|
||||
});
|
||||
|
||||
// Prevent event bubbling when using workaround above
|
||||
$("#fileupload").click(function(event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
$("#fileupload").fileupload({
|
||||
dropZone: $(document),
|
||||
pasteZone: null,
|
||||
autoUpload: true,
|
||||
sequentialUploads: true,
|
||||
// limitConcurrentUploads: 2,
|
||||
// forceIframeTransport: true,
|
||||
|
||||
url: 'upload',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
|
||||
start: function(e) {
|
||||
$(".uploading").show();
|
||||
},
|
||||
|
||||
stop: function(e) {
|
||||
$(".uploading").hide();
|
||||
},
|
||||
|
||||
add: function(e, data) {
|
||||
var file = data.files[0];
|
||||
data.formData = {
|
||||
path: _path
|
||||
};
|
||||
data.context = $(tmpl("template-uploads", {
|
||||
path: _path + file.name
|
||||
})).appendTo("#uploads");
|
||||
var jqXHR = data.submit();
|
||||
data.context.find("button").click(function(event) {
|
||||
jqXHR.abort();
|
||||
});
|
||||
},
|
||||
|
||||
progress: function(e, data) {
|
||||
var progress = parseInt(data.loaded / data.total * 100, 10);
|
||||
data.context.find(".progress-bar").css("width", progress + "%");
|
||||
},
|
||||
|
||||
done: function(e, data) {
|
||||
_reload(_path);
|
||||
},
|
||||
|
||||
fail: function(e, data) {
|
||||
var file = data.files[0];
|
||||
if (data.errorThrown != "abort") {
|
||||
_showError("Failed uploading \"" + file.name + "\" to \"" + _path + "\"", data.textStatus, data.errorThrown);
|
||||
}
|
||||
},
|
||||
|
||||
always: function(e, data) {
|
||||
data.context.remove();
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
$("#create-input").keypress(function(event) {
|
||||
if (event.keyCode == ENTER_KEYCODE) {
|
||||
$("#create-confirm").click();
|
||||
};
|
||||
});
|
||||
|
||||
$("#create-modal").on("shown.bs.modal", function(event) {
|
||||
$("#create-input").focus();
|
||||
$("#create-input").select();
|
||||
});
|
||||
|
||||
$("#create-folder").click(function(event) {
|
||||
$("#create-input").val("Untitled folder");
|
||||
$("#create-modal").modal("show");
|
||||
});
|
||||
|
||||
$("#create-confirm").click(function(event) {
|
||||
$("#create-modal").modal("hide");
|
||||
var name = $("#create-input").val();
|
||||
if (name != "") {
|
||||
$.ajax({
|
||||
url: 'create',
|
||||
type: 'POST',
|
||||
data: {path: _path + name},
|
||||
dataType: 'json'
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
_showError("Failed creating folder \"" + name + "\" in \"" + _path + "\"", textStatus, errorThrown);
|
||||
}).always(function() {
|
||||
_reload(_path);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$("#move-input").keypress(function(event) {
|
||||
if (event.keyCode == ENTER_KEYCODE) {
|
||||
$("#move-confirm").click();
|
||||
};
|
||||
});
|
||||
|
||||
$("#move-modal").on("shown.bs.modal", function(event) {
|
||||
$("#move-input").focus();
|
||||
$("#move-input").select();
|
||||
})
|
||||
|
||||
$("#move-confirm").click(function(event) {
|
||||
$("#move-modal").modal("hide");
|
||||
var oldPath = $("#move-input").data("path");
|
||||
var newPath = $("#move-input").val();
|
||||
if ((newPath != "") && (newPath[0] == "/") && (newPath != oldPath)) {
|
||||
$.ajax({
|
||||
url: 'move',
|
||||
type: 'POST',
|
||||
data: {oldPath: oldPath, newPath: newPath},
|
||||
dataType: 'json'
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
_showError("Failed moving \"" + oldPath + "\" to \"" + newPath + "\"", textStatus, errorThrown);
|
||||
}).always(function() {
|
||||
_reload(_path);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$("#reload").click(function(event) {
|
||||
_reload(_path);
|
||||
});
|
||||
|
||||
_reload("/");
|
||||
|
||||
});
|
1426
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/js/jquery.fileupload.js
vendored
Normal file
1426
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/js/jquery.fileupload.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* jQuery Iframe Transport Plugin 1.8.2
|
||||
* https://github.com/blueimp/jQuery-File-Upload
|
||||
*
|
||||
* Copyright 2011, Sebastian Tschan
|
||||
* https://blueimp.net
|
||||
*
|
||||
* Licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/* global define, window, document */
|
||||
|
||||
(function (factory) {
|
||||
'use strict';
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// Register as an anonymous AMD module:
|
||||
define(['jquery'], factory);
|
||||
} else {
|
||||
// Browser globals:
|
||||
factory(window.jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
'use strict';
|
||||
|
||||
// Helper variable to create unique names for the transport iframes:
|
||||
var counter = 0;
|
||||
|
||||
// The iframe transport accepts four additional options:
|
||||
// options.fileInput: a jQuery collection of file input fields
|
||||
// options.paramName: the parameter name for the file form data,
|
||||
// overrides the name property of the file input field(s),
|
||||
// can be a string or an array of strings.
|
||||
// options.formData: an array of objects with name and value properties,
|
||||
// equivalent to the return data of .serializeArray(), e.g.:
|
||||
// [{name: 'a', value: 1}, {name: 'b', value: 2}]
|
||||
// options.initialIframeSrc: the URL of the initial iframe src,
|
||||
// by default set to "javascript:false;"
|
||||
$.ajaxTransport('iframe', function (options) {
|
||||
if (options.async) {
|
||||
// javascript:false as initial iframe src
|
||||
// prevents warning popups on HTTPS in IE6:
|
||||
/*jshint scripturl: true */
|
||||
var initialIframeSrc = options.initialIframeSrc || 'javascript:false;',
|
||||
/*jshint scripturl: false */
|
||||
form,
|
||||
iframe,
|
||||
addParamChar;
|
||||
return {
|
||||
send: function (_, completeCallback) {
|
||||
form = $('<form style="display:none;"></form>');
|
||||
form.attr('accept-charset', options.formAcceptCharset);
|
||||
addParamChar = /\?/.test(options.url) ? '&' : '?';
|
||||
// XDomainRequest only supports GET and POST:
|
||||
if (options.type === 'DELETE') {
|
||||
options.url = options.url + addParamChar + '_method=DELETE';
|
||||
options.type = 'POST';
|
||||
} else if (options.type === 'PUT') {
|
||||
options.url = options.url + addParamChar + '_method=PUT';
|
||||
options.type = 'POST';
|
||||
} else if (options.type === 'PATCH') {
|
||||
options.url = options.url + addParamChar + '_method=PATCH';
|
||||
options.type = 'POST';
|
||||
}
|
||||
// IE versions below IE8 cannot set the name property of
|
||||
// elements that have already been added to the DOM,
|
||||
// so we set the name along with the iframe HTML markup:
|
||||
counter += 1;
|
||||
iframe = $(
|
||||
'<iframe src="' + initialIframeSrc +
|
||||
'" name="iframe-transport-' + counter + '"></iframe>'
|
||||
).bind('load', function () {
|
||||
var fileInputClones,
|
||||
paramNames = $.isArray(options.paramName) ?
|
||||
options.paramName : [options.paramName];
|
||||
iframe
|
||||
.unbind('load')
|
||||
.bind('load', function () {
|
||||
var response;
|
||||
// Wrap in a try/catch block to catch exceptions thrown
|
||||
// when trying to access cross-domain iframe contents:
|
||||
try {
|
||||
response = iframe.contents();
|
||||
// Google Chrome and Firefox do not throw an
|
||||
// exception when calling iframe.contents() on
|
||||
// cross-domain requests, so we unify the response:
|
||||
if (!response.length || !response[0].firstChild) {
|
||||
throw new Error();
|
||||
}
|
||||
} catch (e) {
|
||||
response = undefined;
|
||||
}
|
||||
// The complete callback returns the
|
||||
// iframe content document as response object:
|
||||
completeCallback(
|
||||
200,
|
||||
'success',
|
||||
{'iframe': response}
|
||||
);
|
||||
// Fix for IE endless progress bar activity bug
|
||||
// (happens on form submits to iframe targets):
|
||||
$('<iframe src="' + initialIframeSrc + '"></iframe>')
|
||||
.appendTo(form);
|
||||
window.setTimeout(function () {
|
||||
// Removing the form in a setTimeout call
|
||||
// allows Chrome's developer tools to display
|
||||
// the response result
|
||||
form.remove();
|
||||
}, 0);
|
||||
});
|
||||
form
|
||||
.prop('target', iframe.prop('name'))
|
||||
.prop('action', options.url)
|
||||
.prop('method', options.type);
|
||||
if (options.formData) {
|
||||
$.each(options.formData, function (index, field) {
|
||||
$('<input type="hidden"/>')
|
||||
.prop('name', field.name)
|
||||
.val(field.value)
|
||||
.appendTo(form);
|
||||
});
|
||||
}
|
||||
if (options.fileInput && options.fileInput.length &&
|
||||
options.type === 'POST') {
|
||||
fileInputClones = options.fileInput.clone();
|
||||
// Insert a clone for each file input field:
|
||||
options.fileInput.after(function (index) {
|
||||
return fileInputClones[index];
|
||||
});
|
||||
if (options.paramName) {
|
||||
options.fileInput.each(function (index) {
|
||||
$(this).prop(
|
||||
'name',
|
||||
paramNames[index] || options.paramName
|
||||
);
|
||||
});
|
||||
}
|
||||
// Appending the file input fields to the hidden form
|
||||
// removes them from their original location:
|
||||
form
|
||||
.append(options.fileInput)
|
||||
.prop('enctype', 'multipart/form-data')
|
||||
// enctype must be set as encoding for IE:
|
||||
.prop('encoding', 'multipart/form-data');
|
||||
// Remove the HTML5 form attribute from the input(s):
|
||||
options.fileInput.removeAttr('form');
|
||||
}
|
||||
form.submit();
|
||||
// Insert the file input fields at their original location
|
||||
// by replacing the clones with the originals:
|
||||
if (fileInputClones && fileInputClones.length) {
|
||||
options.fileInput.each(function (index, input) {
|
||||
var clone = $(fileInputClones[index]);
|
||||
// Restore the original name and form properties:
|
||||
$(input)
|
||||
.prop('name', clone.prop('name'))
|
||||
.attr('form', clone.attr('form'));
|
||||
clone.replaceWith(input);
|
||||
});
|
||||
}
|
||||
});
|
||||
form.append(iframe).appendTo(document.body);
|
||||
},
|
||||
abort: function () {
|
||||
if (iframe) {
|
||||
// javascript:false as iframe src aborts the request
|
||||
// and prevents warning popups on HTTPS in IE6.
|
||||
// concat is used to avoid the "Script URL" JSLint error:
|
||||
iframe
|
||||
.unbind('load')
|
||||
.prop('src', initialIframeSrc);
|
||||
}
|
||||
if (form) {
|
||||
form.remove();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// The iframe transport returns the iframe content document as response.
|
||||
// The following adds converters from iframe to text, json, html, xml
|
||||
// and script.
|
||||
// Please note that the Content-Type for JSON responses has to be text/plain
|
||||
// or text/html, if the browser doesn't include application/json in the
|
||||
// Accept header, else IE will show a download dialog.
|
||||
// The Content-Type for XML responses on the other hand has to be always
|
||||
// application/xml or text/xml, so IE properly parses the XML response.
|
||||
// See also
|
||||
// https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation
|
||||
$.ajaxSetup({
|
||||
converters: {
|
||||
'iframe text': function (iframe) {
|
||||
return iframe && $(iframe[0].body).text();
|
||||
},
|
||||
'iframe json': function (iframe) {
|
||||
return iframe && $.parseJSON($(iframe[0].body).text());
|
||||
},
|
||||
'iframe html': function (iframe) {
|
||||
return iframe && $(iframe[0].body).html();
|
||||
},
|
||||
'iframe xml': function (iframe) {
|
||||
var xmlDoc = iframe && iframe[0];
|
||||
return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc :
|
||||
$.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) ||
|
||||
$(xmlDoc.body).html());
|
||||
},
|
||||
'iframe script': function (iframe) {
|
||||
return iframe && $.globalEval($(iframe[0].body).text());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}));
|
@ -0,0 +1,546 @@
|
||||
/*
|
||||
* Jeditable - jQuery in place edit plugin
|
||||
*
|
||||
* Copyright (c) 2006-2013 Mika Tuupola, Dylan Verheul
|
||||
*
|
||||
* Licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Project home:
|
||||
* http://www.appelsiini.net/projects/jeditable
|
||||
*
|
||||
* Based on editable by Dylan Verheul <dylan_at_dyve.net>:
|
||||
* http://www.dyve.net/jquery/?editable
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Version 1.7.3
|
||||
*
|
||||
* ** means there is basic unit tests for this parameter.
|
||||
*
|
||||
* @name Jeditable
|
||||
* @type jQuery
|
||||
* @param String target (POST) URL or function to send edited content to **
|
||||
* @param Hash options additional options
|
||||
* @param String options[method] method to use to send edited content (POST or PUT) **
|
||||
* @param Function options[callback] Function to run after submitting edited content **
|
||||
* @param String options[name] POST parameter name of edited content
|
||||
* @param String options[id] POST parameter name of edited div id
|
||||
* @param Hash options[submitdata] Extra parameters to send when submitting edited content.
|
||||
* @param String options[type] text, textarea or select (or any 3rd party input type) **
|
||||
* @param Integer options[rows] number of rows if using textarea **
|
||||
* @param Integer options[cols] number of columns if using textarea **
|
||||
* @param Mixed options[height] 'auto', 'none' or height in pixels **
|
||||
* @param Mixed options[width] 'auto', 'none' or width in pixels **
|
||||
* @param String options[loadurl] URL to fetch input content before editing **
|
||||
* @param String options[loadtype] Request type for load url. Should be GET or POST.
|
||||
* @param String options[loadtext] Text to display while loading external content.
|
||||
* @param Mixed options[loaddata] Extra parameters to pass when fetching content before editing.
|
||||
* @param Mixed options[data] Or content given as paramameter. String or function.**
|
||||
* @param String options[indicator] indicator html to show when saving
|
||||
* @param String options[tooltip] optional tooltip text via title attribute **
|
||||
* @param String options[event] jQuery event such as 'click' of 'dblclick' **
|
||||
* @param String options[submit] submit button value, empty means no button **
|
||||
* @param String options[cancel] cancel button value, empty means no button **
|
||||
* @param String options[cssclass] CSS class to apply to input form. 'inherit' to copy from parent. **
|
||||
* @param String options[style] Style to apply to input form 'inherit' to copy from parent. **
|
||||
* @param String options[select] true or false, when true text is highlighted ??
|
||||
* @param String options[placeholder] Placeholder text or html to insert when element is empty. **
|
||||
* @param String options[onblur] 'cancel', 'submit', 'ignore' or function ??
|
||||
*
|
||||
* @param Function options[onsubmit] function(settings, original) { ... } called before submit
|
||||
* @param Function options[onreset] function(settings, original) { ... } called before reset
|
||||
* @param Function options[onerror] function(settings, original, xhr) { ... } called on error
|
||||
*
|
||||
* @param Hash options[ajaxoptions] jQuery Ajax options. See docs.jquery.com.
|
||||
*
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
$.fn.editable = function(target, options) {
|
||||
|
||||
if ('disable' == target) {
|
||||
$(this).data('disabled.editable', true);
|
||||
return;
|
||||
}
|
||||
if ('enable' == target) {
|
||||
$(this).data('disabled.editable', false);
|
||||
return;
|
||||
}
|
||||
if ('destroy' == target) {
|
||||
$(this)
|
||||
.unbind($(this).data('event.editable'))
|
||||
.removeData('disabled.editable')
|
||||
.removeData('event.editable');
|
||||
return;
|
||||
}
|
||||
|
||||
var settings = $.extend({}, $.fn.editable.defaults, {target:target}, options);
|
||||
|
||||
/* setup some functions */
|
||||
var plugin = $.editable.types[settings.type].plugin || function() { };
|
||||
var submit = $.editable.types[settings.type].submit || function() { };
|
||||
var buttons = $.editable.types[settings.type].buttons
|
||||
|| $.editable.types['defaults'].buttons;
|
||||
var content = $.editable.types[settings.type].content
|
||||
|| $.editable.types['defaults'].content;
|
||||
var element = $.editable.types[settings.type].element
|
||||
|| $.editable.types['defaults'].element;
|
||||
var reset = $.editable.types[settings.type].reset
|
||||
|| $.editable.types['defaults'].reset;
|
||||
var callback = settings.callback || function() { };
|
||||
var onedit = settings.onedit || function() { };
|
||||
var onsubmit = settings.onsubmit || function() { };
|
||||
var onreset = settings.onreset || function() { };
|
||||
var onerror = settings.onerror || reset;
|
||||
|
||||
/* Show tooltip. */
|
||||
if (settings.tooltip) {
|
||||
$(this).attr('title', settings.tooltip);
|
||||
}
|
||||
|
||||
settings.autowidth = 'auto' == settings.width;
|
||||
settings.autoheight = 'auto' == settings.height;
|
||||
|
||||
return this.each(function() {
|
||||
|
||||
/* Save this to self because this changes when scope changes. */
|
||||
var self = this;
|
||||
|
||||
/* Inlined block elements lose their width and height after first edit. */
|
||||
/* Save them for later use as workaround. */
|
||||
var savedwidth = $(self).width();
|
||||
var savedheight = $(self).height();
|
||||
|
||||
/* Save so it can be later used by $.editable('destroy') */
|
||||
$(this).data('event.editable', settings.event);
|
||||
|
||||
/* If element is empty add something clickable (if requested) */
|
||||
if (!$.trim($(this).html())) {
|
||||
$(this).html(settings.placeholder);
|
||||
}
|
||||
|
||||
$(this).bind(settings.event, function(e) {
|
||||
|
||||
/* Abort if element is disabled. */
|
||||
if (true === $(this).data('disabled.editable')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prevent throwing an exeption if edit field is clicked again. */
|
||||
if (self.editing) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Abort if onedit hook returns false. */
|
||||
if (false === onedit.apply(this, [settings, self])) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prevent default action and bubbling. */
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
/* Remove tooltip. */
|
||||
if (settings.tooltip) {
|
||||
$(self).removeAttr('title');
|
||||
}
|
||||
|
||||
/* Figure out how wide and tall we are, saved width and height. */
|
||||
/* Workaround for http://dev.jquery.com/ticket/2190 */
|
||||
if (0 == $(self).width()) {
|
||||
settings.width = savedwidth;
|
||||
settings.height = savedheight;
|
||||
} else {
|
||||
if (settings.width != 'none') {
|
||||
settings.width =
|
||||
settings.autowidth ? $(self).width() : settings.width;
|
||||
}
|
||||
if (settings.height != 'none') {
|
||||
settings.height =
|
||||
settings.autoheight ? $(self).height() : settings.height;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove placeholder text, replace is here because of IE. */
|
||||
if ($(this).html().toLowerCase().replace(/(;|"|\/)/g, '') ==
|
||||
settings.placeholder.toLowerCase().replace(/(;|"|\/)/g, '')) {
|
||||
$(this).html('');
|
||||
}
|
||||
|
||||
self.editing = true;
|
||||
self.revert = $(self).html();
|
||||
$(self).html('');
|
||||
|
||||
/* Create the form object. */
|
||||
var form = $('<form />');
|
||||
|
||||
/* Apply css or style or both. */
|
||||
if (settings.cssclass) {
|
||||
if ('inherit' == settings.cssclass) {
|
||||
form.attr('class', $(self).attr('class'));
|
||||
} else {
|
||||
form.attr('class', settings.cssclass);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.style) {
|
||||
if ('inherit' == settings.style) {
|
||||
form.attr('style', $(self).attr('style'));
|
||||
/* IE needs the second line or display wont be inherited. */
|
||||
form.css('display', $(self).css('display'));
|
||||
} else {
|
||||
form.attr('style', settings.style);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add main input element to form and store it in input. */
|
||||
var input = element.apply(form, [settings, self]);
|
||||
|
||||
/* Set input content via POST, GET, given data or existing value. */
|
||||
var input_content;
|
||||
|
||||
if (settings.loadurl) {
|
||||
var t = setTimeout(function() {
|
||||
input.disabled = true;
|
||||
content.apply(form, [settings.loadtext, settings, self]);
|
||||
}, 100);
|
||||
|
||||
var loaddata = {};
|
||||
loaddata[settings.id] = self.id;
|
||||
if ($.isFunction(settings.loaddata)) {
|
||||
$.extend(loaddata, settings.loaddata.apply(self, [self.revert, settings]));
|
||||
} else {
|
||||
$.extend(loaddata, settings.loaddata);
|
||||
}
|
||||
$.ajax({
|
||||
type : settings.loadtype,
|
||||
url : settings.loadurl,
|
||||
data : loaddata,
|
||||
async : false,
|
||||
success: function(result) {
|
||||
window.clearTimeout(t);
|
||||
input_content = result;
|
||||
input.disabled = false;
|
||||
}
|
||||
});
|
||||
} else if (settings.data) {
|
||||
input_content = settings.data;
|
||||
if ($.isFunction(settings.data)) {
|
||||
input_content = settings.data.apply(self, [self.revert, settings]);
|
||||
}
|
||||
} else {
|
||||
input_content = self.revert;
|
||||
}
|
||||
content.apply(form, [input_content, settings, self]);
|
||||
|
||||
input.attr('name', settings.name);
|
||||
|
||||
/* Add buttons to the form. */
|
||||
buttons.apply(form, [settings, self]);
|
||||
|
||||
/* Add created form to self. */
|
||||
$(self).append(form);
|
||||
|
||||
/* Attach 3rd party plugin if requested. */
|
||||
plugin.apply(form, [settings, self]);
|
||||
|
||||
/* Focus to first visible form element. */
|
||||
$(':input:visible:enabled:first', form).focus();
|
||||
|
||||
/* Highlight input contents when requested. */
|
||||
if (settings.select) {
|
||||
input.select();
|
||||
}
|
||||
|
||||
/* discard changes if pressing esc */
|
||||
input.keydown(function(e) {
|
||||
if (e.keyCode == 27) {
|
||||
e.preventDefault();
|
||||
reset.apply(form, [settings, self]);
|
||||
}
|
||||
});
|
||||
|
||||
/* Discard, submit or nothing with changes when clicking outside. */
|
||||
/* Do nothing is usable when navigating with tab. */
|
||||
var t;
|
||||
if ('cancel' == settings.onblur) {
|
||||
input.blur(function(e) {
|
||||
/* Prevent canceling if submit was clicked. */
|
||||
t = setTimeout(function() {
|
||||
reset.apply(form, [settings, self]);
|
||||
}, 500);
|
||||
});
|
||||
} else if ('submit' == settings.onblur) {
|
||||
input.blur(function(e) {
|
||||
/* Prevent double submit if submit was clicked. */
|
||||
t = setTimeout(function() {
|
||||
form.submit();
|
||||
}, 200);
|
||||
});
|
||||
} else if ($.isFunction(settings.onblur)) {
|
||||
input.blur(function(e) {
|
||||
settings.onblur.apply(self, [input.val(), settings]);
|
||||
});
|
||||
} else {
|
||||
input.blur(function(e) {
|
||||
/* TODO: maybe something here */
|
||||
});
|
||||
}
|
||||
|
||||
form.submit(function(e) {
|
||||
|
||||
if (t) {
|
||||
clearTimeout(t);
|
||||
}
|
||||
|
||||
/* Do no submit. */
|
||||
e.preventDefault();
|
||||
|
||||
/* Call before submit hook. */
|
||||
/* If it returns false abort submitting. */
|
||||
if (false !== onsubmit.apply(form, [settings, self])) {
|
||||
/* Custom inputs call before submit hook. */
|
||||
/* If it returns false abort submitting. */
|
||||
if (false !== submit.apply(form, [settings, self])) {
|
||||
|
||||
/* Check if given target is function */
|
||||
if ($.isFunction(settings.target)) {
|
||||
var str = settings.target.apply(self, [input.val(), settings]);
|
||||
$(self).html(str);
|
||||
self.editing = false;
|
||||
callback.apply(self, [self.innerHTML, settings]);
|
||||
/* TODO: this is not dry */
|
||||
if (!$.trim($(self).html())) {
|
||||
$(self).html(settings.placeholder);
|
||||
}
|
||||
} else {
|
||||
/* Add edited content and id of edited element to POST. */
|
||||
var submitdata = {};
|
||||
submitdata[settings.name] = input.val();
|
||||
submitdata[settings.id] = self.id;
|
||||
/* Add extra data to be POST:ed. */
|
||||
if ($.isFunction(settings.submitdata)) {
|
||||
$.extend(submitdata, settings.submitdata.apply(self, [self.revert, settings]));
|
||||
} else {
|
||||
$.extend(submitdata, settings.submitdata);
|
||||
}
|
||||
|
||||
/* Quick and dirty PUT support. */
|
||||
if ('PUT' == settings.method) {
|
||||
submitdata['_method'] = 'put';
|
||||
}
|
||||
|
||||
/* Show the saving indicator. */
|
||||
$(self).html(settings.indicator);
|
||||
|
||||
/* Defaults for ajaxoptions. */
|
||||
var ajaxoptions = {
|
||||
type : 'POST',
|
||||
data : submitdata,
|
||||
dataType: 'html',
|
||||
url : settings.target,
|
||||
success : function(result, status) {
|
||||
if (ajaxoptions.dataType == 'html') {
|
||||
$(self).html(result);
|
||||
}
|
||||
self.editing = false;
|
||||
callback.apply(self, [result, settings]);
|
||||
if (!$.trim($(self).html())) {
|
||||
$(self).html(settings.placeholder);
|
||||
}
|
||||
},
|
||||
error : function(xhr, status, error) {
|
||||
onerror.apply(form, [settings, self, xhr]);
|
||||
}
|
||||
};
|
||||
|
||||
/* Override with what is given in settings.ajaxoptions. */
|
||||
$.extend(ajaxoptions, settings.ajaxoptions);
|
||||
$.ajax(ajaxoptions);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Show tooltip again. */
|
||||
$(self).attr('title', settings.tooltip);
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
/* Privileged methods */
|
||||
this.reset = function(form) {
|
||||
/* Prevent calling reset twice when blurring. */
|
||||
if (this.editing) {
|
||||
/* Before reset hook, if it returns false abort reseting. */
|
||||
if (false !== onreset.apply(form, [settings, self])) {
|
||||
$(self).html(self.revert);
|
||||
self.editing = false;
|
||||
if (!$.trim($(self).html())) {
|
||||
$(self).html(settings.placeholder);
|
||||
}
|
||||
/* Show tooltip again. */
|
||||
if (settings.tooltip) {
|
||||
$(self).attr('title', settings.tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
$.editable = {
|
||||
types: {
|
||||
defaults: {
|
||||
element : function(settings, original) {
|
||||
var input = $('<input type="hidden"></input>');
|
||||
$(this).append(input);
|
||||
return(input);
|
||||
},
|
||||
content : function(string, settings, original) {
|
||||
$(':input:first', this).val(string);
|
||||
},
|
||||
reset : function(settings, original) {
|
||||
original.reset(this);
|
||||
},
|
||||
buttons : function(settings, original) {
|
||||
var form = this;
|
||||
if (settings.submit) {
|
||||
/* If given html string use that. */
|
||||
if (settings.submit.match(/>$/)) {
|
||||
var submit = $(settings.submit).click(function() {
|
||||
if (submit.attr("type") != "submit") {
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
/* Otherwise use button with given string as text. */
|
||||
} else {
|
||||
var submit = $('<button type="submit" />');
|
||||
submit.html(settings.submit);
|
||||
}
|
||||
$(this).append(submit);
|
||||
}
|
||||
if (settings.cancel) {
|
||||
/* If given html string use that. */
|
||||
if (settings.cancel.match(/>$/)) {
|
||||
var cancel = $(settings.cancel);
|
||||
/* otherwise use button with given string as text */
|
||||
} else {
|
||||
var cancel = $('<button type="cancel" />');
|
||||
cancel.html(settings.cancel);
|
||||
}
|
||||
$(this).append(cancel);
|
||||
|
||||
$(cancel).click(function(event) {
|
||||
if ($.isFunction($.editable.types[settings.type].reset)) {
|
||||
var reset = $.editable.types[settings.type].reset;
|
||||
} else {
|
||||
var reset = $.editable.types['defaults'].reset;
|
||||
}
|
||||
reset.apply(form, [settings, original]);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
text: {
|
||||
element : function(settings, original) {
|
||||
var input = $('<input />');
|
||||
if (settings.width != 'none') { input.width(settings.width); }
|
||||
if (settings.height != 'none') { input.height(settings.height); }
|
||||
/* https://bugzilla.mozilla.org/show_bug.cgi?id=236791 */
|
||||
//input[0].setAttribute('autocomplete','off');
|
||||
input.attr('autocomplete','off');
|
||||
$(this).append(input);
|
||||
return(input);
|
||||
}
|
||||
},
|
||||
textarea: {
|
||||
element : function(settings, original) {
|
||||
var textarea = $('<textarea />');
|
||||
if (settings.rows) {
|
||||
textarea.attr('rows', settings.rows);
|
||||
} else if (settings.height != "none") {
|
||||
textarea.height(settings.height);
|
||||
}
|
||||
if (settings.cols) {
|
||||
textarea.attr('cols', settings.cols);
|
||||
} else if (settings.width != "none") {
|
||||
textarea.width(settings.width);
|
||||
}
|
||||
$(this).append(textarea);
|
||||
return(textarea);
|
||||
}
|
||||
},
|
||||
select: {
|
||||
element : function(settings, original) {
|
||||
var select = $('<select />');
|
||||
$(this).append(select);
|
||||
return(select);
|
||||
},
|
||||
content : function(data, settings, original) {
|
||||
/* If it is string assume it is json. */
|
||||
if (String == data.constructor) {
|
||||
eval ('var json = ' + data);
|
||||
} else {
|
||||
/* Otherwise assume it is a hash already. */
|
||||
var json = data;
|
||||
}
|
||||
for (var key in json) {
|
||||
if (!json.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
if ('selected' == key) {
|
||||
continue;
|
||||
}
|
||||
var option = $('<option />').val(key).append(json[key]);
|
||||
$('select', this).append(option);
|
||||
}
|
||||
/* Loop option again to set selected. IE needed this... */
|
||||
$('select', this).children().each(function() {
|
||||
if ($(this).val() == json['selected'] ||
|
||||
$(this).text() == $.trim(original.revert)) {
|
||||
$(this).attr('selected', 'selected');
|
||||
}
|
||||
});
|
||||
/* Submit on change if no submit button defined. */
|
||||
if (!settings.submit) {
|
||||
var form = this;
|
||||
$('select', this).change(function() {
|
||||
form.submit();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/* Add new input type */
|
||||
addInputType: function(name, input) {
|
||||
$.editable.types[name] = input;
|
||||
}
|
||||
};
|
||||
|
||||
/* Publicly accessible defaults. */
|
||||
$.fn.editable.defaults = {
|
||||
name : 'value',
|
||||
id : 'id',
|
||||
type : 'text',
|
||||
width : 'auto',
|
||||
height : 'auto',
|
||||
event : 'click.editable',
|
||||
onblur : 'cancel',
|
||||
loadtype : 'GET',
|
||||
loadtext : 'Loading...',
|
||||
placeholder: 'Click to edit',
|
||||
loaddata : {},
|
||||
submitdata : {},
|
||||
ajaxoptions: {}
|
||||
};
|
||||
|
||||
})(jQuery);
|
4
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/js/jquery.min.js
vendored
Normal file
4
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
521
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/js/jquery.ui.widget.js
vendored
Normal file
521
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/js/jquery.ui.widget.js
vendored
Normal file
@ -0,0 +1,521 @@
|
||||
/*!
|
||||
* jQuery UI Widget 1.10.4
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright 2014 jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/jQuery.widget/
|
||||
*/
|
||||
(function( $, undefined ) {
|
||||
|
||||
var uuid = 0,
|
||||
slice = Array.prototype.slice,
|
||||
_cleanData = $.cleanData;
|
||||
$.cleanData = function( elems ) {
|
||||
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
|
||||
try {
|
||||
$( elem ).triggerHandler( "remove" );
|
||||
// http://bugs.jquery.com/ticket/8235
|
||||
} catch( e ) {}
|
||||
}
|
||||
_cleanData( elems );
|
||||
};
|
||||
|
||||
$.widget = function( name, base, prototype ) {
|
||||
var fullName, existingConstructor, constructor, basePrototype,
|
||||
// proxiedPrototype allows the provided prototype to remain unmodified
|
||||
// so that it can be used as a mixin for multiple widgets (#8876)
|
||||
proxiedPrototype = {},
|
||||
namespace = name.split( "." )[ 0 ];
|
||||
|
||||
name = name.split( "." )[ 1 ];
|
||||
fullName = namespace + "-" + name;
|
||||
|
||||
if ( !prototype ) {
|
||||
prototype = base;
|
||||
base = $.Widget;
|
||||
}
|
||||
|
||||
// create selector for plugin
|
||||
$.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
|
||||
return !!$.data( elem, fullName );
|
||||
};
|
||||
|
||||
$[ namespace ] = $[ namespace ] || {};
|
||||
existingConstructor = $[ namespace ][ name ];
|
||||
constructor = $[ namespace ][ name ] = function( options, element ) {
|
||||
// allow instantiation without "new" keyword
|
||||
if ( !this._createWidget ) {
|
||||
return new constructor( options, element );
|
||||
}
|
||||
|
||||
// allow instantiation without initializing for simple inheritance
|
||||
// must use "new" keyword (the code above always passes args)
|
||||
if ( arguments.length ) {
|
||||
this._createWidget( options, element );
|
||||
}
|
||||
};
|
||||
// extend with the existing constructor to carry over any static properties
|
||||
$.extend( constructor, existingConstructor, {
|
||||
version: prototype.version,
|
||||
// copy the object used to create the prototype in case we need to
|
||||
// redefine the widget later
|
||||
_proto: $.extend( {}, prototype ),
|
||||
// track widgets that inherit from this widget in case this widget is
|
||||
// redefined after a widget inherits from it
|
||||
_childConstructors: []
|
||||
});
|
||||
|
||||
basePrototype = new base();
|
||||
// we need to make the options hash a property directly on the new instance
|
||||
// otherwise we'll modify the options hash on the prototype that we're
|
||||
// inheriting from
|
||||
basePrototype.options = $.widget.extend( {}, basePrototype.options );
|
||||
$.each( prototype, function( prop, value ) {
|
||||
if ( !$.isFunction( value ) ) {
|
||||
proxiedPrototype[ prop ] = value;
|
||||
return;
|
||||
}
|
||||
proxiedPrototype[ prop ] = (function() {
|
||||
var _super = function() {
|
||||
return base.prototype[ prop ].apply( this, arguments );
|
||||
},
|
||||
_superApply = function( args ) {
|
||||
return base.prototype[ prop ].apply( this, args );
|
||||
};
|
||||
return function() {
|
||||
var __super = this._super,
|
||||
__superApply = this._superApply,
|
||||
returnValue;
|
||||
|
||||
this._super = _super;
|
||||
this._superApply = _superApply;
|
||||
|
||||
returnValue = value.apply( this, arguments );
|
||||
|
||||
this._super = __super;
|
||||
this._superApply = __superApply;
|
||||
|
||||
return returnValue;
|
||||
};
|
||||
})();
|
||||
});
|
||||
constructor.prototype = $.widget.extend( basePrototype, {
|
||||
// TODO: remove support for widgetEventPrefix
|
||||
// always use the name + a colon as the prefix, e.g., draggable:start
|
||||
// don't prefix for widgets that aren't DOM-based
|
||||
widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
|
||||
}, proxiedPrototype, {
|
||||
constructor: constructor,
|
||||
namespace: namespace,
|
||||
widgetName: name,
|
||||
widgetFullName: fullName
|
||||
});
|
||||
|
||||
// If this widget is being redefined then we need to find all widgets that
|
||||
// are inheriting from it and redefine all of them so that they inherit from
|
||||
// the new version of this widget. We're essentially trying to replace one
|
||||
// level in the prototype chain.
|
||||
if ( existingConstructor ) {
|
||||
$.each( existingConstructor._childConstructors, function( i, child ) {
|
||||
var childPrototype = child.prototype;
|
||||
|
||||
// redefine the child widget using the same prototype that was
|
||||
// originally used, but inherit from the new version of the base
|
||||
$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
|
||||
});
|
||||
// remove the list of existing child constructors from the old constructor
|
||||
// so the old child constructors can be garbage collected
|
||||
delete existingConstructor._childConstructors;
|
||||
} else {
|
||||
base._childConstructors.push( constructor );
|
||||
}
|
||||
|
||||
$.widget.bridge( name, constructor );
|
||||
};
|
||||
|
||||
$.widget.extend = function( target ) {
|
||||
var input = slice.call( arguments, 1 ),
|
||||
inputIndex = 0,
|
||||
inputLength = input.length,
|
||||
key,
|
||||
value;
|
||||
for ( ; inputIndex < inputLength; inputIndex++ ) {
|
||||
for ( key in input[ inputIndex ] ) {
|
||||
value = input[ inputIndex ][ key ];
|
||||
if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
|
||||
// Clone objects
|
||||
if ( $.isPlainObject( value ) ) {
|
||||
target[ key ] = $.isPlainObject( target[ key ] ) ?
|
||||
$.widget.extend( {}, target[ key ], value ) :
|
||||
// Don't extend strings, arrays, etc. with objects
|
||||
$.widget.extend( {}, value );
|
||||
// Copy everything else by reference
|
||||
} else {
|
||||
target[ key ] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
||||
$.widget.bridge = function( name, object ) {
|
||||
var fullName = object.prototype.widgetFullName || name;
|
||||
$.fn[ name ] = function( options ) {
|
||||
var isMethodCall = typeof options === "string",
|
||||
args = slice.call( arguments, 1 ),
|
||||
returnValue = this;
|
||||
|
||||
// allow multiple hashes to be passed on init
|
||||
options = !isMethodCall && args.length ?
|
||||
$.widget.extend.apply( null, [ options ].concat(args) ) :
|
||||
options;
|
||||
|
||||
if ( isMethodCall ) {
|
||||
this.each(function() {
|
||||
var methodValue,
|
||||
instance = $.data( this, fullName );
|
||||
if ( !instance ) {
|
||||
return $.error( "cannot call methods on " + name + " prior to initialization; " +
|
||||
"attempted to call method '" + options + "'" );
|
||||
}
|
||||
if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
|
||||
return $.error( "no such method '" + options + "' for " + name + " widget instance" );
|
||||
}
|
||||
methodValue = instance[ options ].apply( instance, args );
|
||||
if ( methodValue !== instance && methodValue !== undefined ) {
|
||||
returnValue = methodValue && methodValue.jquery ?
|
||||
returnValue.pushStack( methodValue.get() ) :
|
||||
methodValue;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.each(function() {
|
||||
var instance = $.data( this, fullName );
|
||||
if ( instance ) {
|
||||
instance.option( options || {} )._init();
|
||||
} else {
|
||||
$.data( this, fullName, new object( options, this ) );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
};
|
||||
};
|
||||
|
||||
$.Widget = function( /* options, element */ ) {};
|
||||
$.Widget._childConstructors = [];
|
||||
|
||||
$.Widget.prototype = {
|
||||
widgetName: "widget",
|
||||
widgetEventPrefix: "",
|
||||
defaultElement: "<div>",
|
||||
options: {
|
||||
disabled: false,
|
||||
|
||||
// callbacks
|
||||
create: null
|
||||
},
|
||||
_createWidget: function( options, element ) {
|
||||
element = $( element || this.defaultElement || this )[ 0 ];
|
||||
this.element = $( element );
|
||||
this.uuid = uuid++;
|
||||
this.eventNamespace = "." + this.widgetName + this.uuid;
|
||||
this.options = $.widget.extend( {},
|
||||
this.options,
|
||||
this._getCreateOptions(),
|
||||
options );
|
||||
|
||||
this.bindings = $();
|
||||
this.hoverable = $();
|
||||
this.focusable = $();
|
||||
|
||||
if ( element !== this ) {
|
||||
$.data( element, this.widgetFullName, this );
|
||||
this._on( true, this.element, {
|
||||
remove: function( event ) {
|
||||
if ( event.target === element ) {
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
this.document = $( element.style ?
|
||||
// element within the document
|
||||
element.ownerDocument :
|
||||
// element is window or document
|
||||
element.document || element );
|
||||
this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
|
||||
}
|
||||
|
||||
this._create();
|
||||
this._trigger( "create", null, this._getCreateEventData() );
|
||||
this._init();
|
||||
},
|
||||
_getCreateOptions: $.noop,
|
||||
_getCreateEventData: $.noop,
|
||||
_create: $.noop,
|
||||
_init: $.noop,
|
||||
|
||||
destroy: function() {
|
||||
this._destroy();
|
||||
// we can probably remove the unbind calls in 2.0
|
||||
// all event bindings should go through this._on()
|
||||
this.element
|
||||
.unbind( this.eventNamespace )
|
||||
// 1.9 BC for #7810
|
||||
// TODO remove dual storage
|
||||
.removeData( this.widgetName )
|
||||
.removeData( this.widgetFullName )
|
||||
// support: jquery <1.6.3
|
||||
// http://bugs.jquery.com/ticket/9413
|
||||
.removeData( $.camelCase( this.widgetFullName ) );
|
||||
this.widget()
|
||||
.unbind( this.eventNamespace )
|
||||
.removeAttr( "aria-disabled" )
|
||||
.removeClass(
|
||||
this.widgetFullName + "-disabled " +
|
||||
"ui-state-disabled" );
|
||||
|
||||
// clean up events and states
|
||||
this.bindings.unbind( this.eventNamespace );
|
||||
this.hoverable.removeClass( "ui-state-hover" );
|
||||
this.focusable.removeClass( "ui-state-focus" );
|
||||
},
|
||||
_destroy: $.noop,
|
||||
|
||||
widget: function() {
|
||||
return this.element;
|
||||
},
|
||||
|
||||
option: function( key, value ) {
|
||||
var options = key,
|
||||
parts,
|
||||
curOption,
|
||||
i;
|
||||
|
||||
if ( arguments.length === 0 ) {
|
||||
// don't return a reference to the internal hash
|
||||
return $.widget.extend( {}, this.options );
|
||||
}
|
||||
|
||||
if ( typeof key === "string" ) {
|
||||
// handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
|
||||
options = {};
|
||||
parts = key.split( "." );
|
||||
key = parts.shift();
|
||||
if ( parts.length ) {
|
||||
curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
|
||||
for ( i = 0; i < parts.length - 1; i++ ) {
|
||||
curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
|
||||
curOption = curOption[ parts[ i ] ];
|
||||
}
|
||||
key = parts.pop();
|
||||
if ( arguments.length === 1 ) {
|
||||
return curOption[ key ] === undefined ? null : curOption[ key ];
|
||||
}
|
||||
curOption[ key ] = value;
|
||||
} else {
|
||||
if ( arguments.length === 1 ) {
|
||||
return this.options[ key ] === undefined ? null : this.options[ key ];
|
||||
}
|
||||
options[ key ] = value;
|
||||
}
|
||||
}
|
||||
|
||||
this._setOptions( options );
|
||||
|
||||
return this;
|
||||
},
|
||||
_setOptions: function( options ) {
|
||||
var key;
|
||||
|
||||
for ( key in options ) {
|
||||
this._setOption( key, options[ key ] );
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
_setOption: function( key, value ) {
|
||||
this.options[ key ] = value;
|
||||
|
||||
if ( key === "disabled" ) {
|
||||
this.widget()
|
||||
.toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
|
||||
.attr( "aria-disabled", value );
|
||||
this.hoverable.removeClass( "ui-state-hover" );
|
||||
this.focusable.removeClass( "ui-state-focus" );
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
return this._setOption( "disabled", false );
|
||||
},
|
||||
disable: function() {
|
||||
return this._setOption( "disabled", true );
|
||||
},
|
||||
|
||||
_on: function( suppressDisabledCheck, element, handlers ) {
|
||||
var delegateElement,
|
||||
instance = this;
|
||||
|
||||
// no suppressDisabledCheck flag, shuffle arguments
|
||||
if ( typeof suppressDisabledCheck !== "boolean" ) {
|
||||
handlers = element;
|
||||
element = suppressDisabledCheck;
|
||||
suppressDisabledCheck = false;
|
||||
}
|
||||
|
||||
// no element argument, shuffle and use this.element
|
||||
if ( !handlers ) {
|
||||
handlers = element;
|
||||
element = this.element;
|
||||
delegateElement = this.widget();
|
||||
} else {
|
||||
// accept selectors, DOM elements
|
||||
element = delegateElement = $( element );
|
||||
this.bindings = this.bindings.add( element );
|
||||
}
|
||||
|
||||
$.each( handlers, function( event, handler ) {
|
||||
function handlerProxy() {
|
||||
// allow widgets to customize the disabled handling
|
||||
// - disabled as an array instead of boolean
|
||||
// - disabled class as method for disabling individual parts
|
||||
if ( !suppressDisabledCheck &&
|
||||
( instance.options.disabled === true ||
|
||||
$( this ).hasClass( "ui-state-disabled" ) ) ) {
|
||||
return;
|
||||
}
|
||||
return ( typeof handler === "string" ? instance[ handler ] : handler )
|
||||
.apply( instance, arguments );
|
||||
}
|
||||
|
||||
// copy the guid so direct unbinding works
|
||||
if ( typeof handler !== "string" ) {
|
||||
handlerProxy.guid = handler.guid =
|
||||
handler.guid || handlerProxy.guid || $.guid++;
|
||||
}
|
||||
|
||||
var match = event.match( /^(\w+)\s*(.*)$/ ),
|
||||
eventName = match[1] + instance.eventNamespace,
|
||||
selector = match[2];
|
||||
if ( selector ) {
|
||||
delegateElement.delegate( selector, eventName, handlerProxy );
|
||||
} else {
|
||||
element.bind( eventName, handlerProxy );
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_off: function( element, eventName ) {
|
||||
eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
|
||||
element.unbind( eventName ).undelegate( eventName );
|
||||
},
|
||||
|
||||
_delay: function( handler, delay ) {
|
||||
function handlerProxy() {
|
||||
return ( typeof handler === "string" ? instance[ handler ] : handler )
|
||||
.apply( instance, arguments );
|
||||
}
|
||||
var instance = this;
|
||||
return setTimeout( handlerProxy, delay || 0 );
|
||||
},
|
||||
|
||||
_hoverable: function( element ) {
|
||||
this.hoverable = this.hoverable.add( element );
|
||||
this._on( element, {
|
||||
mouseenter: function( event ) {
|
||||
$( event.currentTarget ).addClass( "ui-state-hover" );
|
||||
},
|
||||
mouseleave: function( event ) {
|
||||
$( event.currentTarget ).removeClass( "ui-state-hover" );
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_focusable: function( element ) {
|
||||
this.focusable = this.focusable.add( element );
|
||||
this._on( element, {
|
||||
focusin: function( event ) {
|
||||
$( event.currentTarget ).addClass( "ui-state-focus" );
|
||||
},
|
||||
focusout: function( event ) {
|
||||
$( event.currentTarget ).removeClass( "ui-state-focus" );
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_trigger: function( type, event, data ) {
|
||||
var prop, orig,
|
||||
callback = this.options[ type ];
|
||||
|
||||
data = data || {};
|
||||
event = $.Event( event );
|
||||
event.type = ( type === this.widgetEventPrefix ?
|
||||
type :
|
||||
this.widgetEventPrefix + type ).toLowerCase();
|
||||
// the original event may come from any element
|
||||
// so we need to reset the target on the new event
|
||||
event.target = this.element[ 0 ];
|
||||
|
||||
// copy original event properties over to the new event
|
||||
orig = event.originalEvent;
|
||||
if ( orig ) {
|
||||
for ( prop in orig ) {
|
||||
if ( !( prop in event ) ) {
|
||||
event[ prop ] = orig[ prop ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.element.trigger( event, data );
|
||||
return !( $.isFunction( callback ) &&
|
||||
callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
|
||||
event.isDefaultPrevented() );
|
||||
}
|
||||
};
|
||||
|
||||
$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
|
||||
$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
|
||||
if ( typeof options === "string" ) {
|
||||
options = { effect: options };
|
||||
}
|
||||
var hasOptions,
|
||||
effectName = !options ?
|
||||
method :
|
||||
options === true || typeof options === "number" ?
|
||||
defaultEffect :
|
||||
options.effect || defaultEffect;
|
||||
options = options || {};
|
||||
if ( typeof options === "number" ) {
|
||||
options = { duration: options };
|
||||
}
|
||||
hasOptions = !$.isEmptyObject( options );
|
||||
options.complete = callback;
|
||||
if ( options.delay ) {
|
||||
element.delay( options.delay );
|
||||
}
|
||||
if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
|
||||
element[ method ]( options );
|
||||
} else if ( effectName !== method && element[ effectName ] ) {
|
||||
element[ effectName ]( options.duration, options.easing, callback );
|
||||
} else {
|
||||
element.queue(function( next ) {
|
||||
$( this )[ method ]();
|
||||
if ( callback ) {
|
||||
callback.call( element[ 0 ] );
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
})( jQuery );
|
@ -0,0 +1,5 @@
|
||||
/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl
|
||||
* Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT
|
||||
* */
|
||||
|
||||
!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b<s.length;b++){var c=s[b],e=c.href,f=c.media,g=c.rel&&"stylesheet"===c.rel.toLowerCase();e&&g&&!o[e]&&(c.styleSheet&&c.styleSheet.rawCssText?(v(c.styleSheet.rawCssText,e,f),o[e]=!0):(!/^([a-zA-Z:]*\/\/)/.test(e)&&!r||e.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&("//"===e.substring(0,2)&&(e=a.location.protocol+e),d.push({href:e,media:f})))}w()};x(),c.update=x,c.getEmValue=t,a.addEventListener?a.addEventListener("resize",b,!1):a.attachEvent&&a.attachEvent("onresize",b)}}(this);
|
1
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/js/tmpl.min.js
vendored
Normal file
1
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/js/tmpl.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(a){"use strict";var b=function(a,c){var d=/[^\w\-\.:]/.test(a)?new Function(b.arg+",tmpl","var _e=tmpl.encode"+b.helper+",_s='"+a.replace(b.regexp,b.func)+"';return _s;"):b.cache[a]=b.cache[a]||b(b.load(a));return c?d(c,b):function(a){return d(a,b)}};b.cache={},b.load=function(a){return document.getElementById(a).innerHTML},b.regexp=/([\s'\\])(?!(?:[^{]|\{(?!%))*%\})|(?:\{%(=|#)([\s\S]+?)%\})|(\{%)|(%\})/g,b.func=function(a,b,c,d,e,f){return b?{"\n":"\\n","\r":"\\r"," ":"\\t"," ":" "}[b]||"\\"+b:c?"="===c?"'+_e("+d+")+'":"'+("+d+"==null?'':"+d+")+'":e?"';":f?"_s+='":void 0},b.encReg=/[<>&"'\x00]/g,b.encMap={"<":"<",">":">","&":"&",'"':""","'":"'"},b.encode=function(a){return(null==a?"":""+a).replace(b.encReg,function(a){return b.encMap[a]||""})},b.arg="o",b.helper=",print=function(s,e){_s+=e?(s==null?'':s):_e(s);},include=function(s,d){_s+=tmpl(s,d);}","function"==typeof define&&define.amd?define(function(){return b}):a.tmpl=b}(this);
|
201
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.h
Normal file
201
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.h
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServer.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class GCDWebUploader;
|
||||
|
||||
/**
|
||||
* Delegate methods for GCDWebUploader.
|
||||
*
|
||||
* @warning These methods are always called on the main thread in a serialized way.
|
||||
*/
|
||||
@protocol GCDWebUploaderDelegate <GCDWebServerDelegate>
|
||||
@optional
|
||||
|
||||
/**
|
||||
* This method is called whenever a file has been downloaded.
|
||||
*/
|
||||
- (void)webUploader:(GCDWebUploader*)uploader didDownloadFileAtPath:(NSString*)path;
|
||||
|
||||
/**
|
||||
* This method is called whenever a file has been uploaded.
|
||||
*/
|
||||
- (void)webUploader:(GCDWebUploader*)uploader didUploadFileAtPath:(NSString*)path;
|
||||
|
||||
/**
|
||||
* This method is called whenever a file or directory has been moved.
|
||||
*/
|
||||
- (void)webUploader:(GCDWebUploader*)uploader didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath;
|
||||
|
||||
/**
|
||||
* This method is called whenever a file or directory has been deleted.
|
||||
*/
|
||||
- (void)webUploader:(GCDWebUploader*)uploader didDeleteItemAtPath:(NSString*)path;
|
||||
|
||||
/**
|
||||
* This method is called whenever a directory has been created.
|
||||
*/
|
||||
- (void)webUploader:(GCDWebUploader*)uploader didCreateDirectoryAtPath:(NSString*)path;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebUploader subclass of GCDWebServer implements an HTML 5 web browser
|
||||
* interface for uploading or downloading files, and moving or deleting files
|
||||
* or directories.
|
||||
*
|
||||
* See the README.md file for more information about the features of GCDWebUploader.
|
||||
*
|
||||
* @warning For GCDWebUploader to work, "GCDWebUploader.bundle" must be added
|
||||
* to the resources of the Xcode target.
|
||||
*/
|
||||
@interface GCDWebUploader : GCDWebServer
|
||||
|
||||
/**
|
||||
* Returns the upload directory as specified when the uploader was initialized.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* uploadDirectory;
|
||||
|
||||
/**
|
||||
* Sets the delegate for the uploader.
|
||||
*/
|
||||
@property(nonatomic, weak, nullable) id<GCDWebUploaderDelegate> delegate;
|
||||
|
||||
/**
|
||||
* Sets which files are allowed to be operated on depending on their extension.
|
||||
*
|
||||
* The default value is nil i.e. all file extensions are allowed.
|
||||
*/
|
||||
@property(nonatomic, copy) NSArray<NSString*>* allowedFileExtensions;
|
||||
|
||||
/**
|
||||
* Sets if files and directories whose name start with a period are allowed to
|
||||
* be operated on.
|
||||
*
|
||||
* The default value is NO.
|
||||
*/
|
||||
@property(nonatomic) BOOL allowHiddenItems;
|
||||
|
||||
/**
|
||||
* Sets the title for the uploader web interface.
|
||||
*
|
||||
* The default value is the application name.
|
||||
*
|
||||
* @warning Any reserved HTML characters in the string value for this property
|
||||
* must have been replaced by character entities e.g. "&" becomes "&".
|
||||
*/
|
||||
@property(nonatomic, copy) NSString* title;
|
||||
|
||||
/**
|
||||
* Sets the header for the uploader web interface.
|
||||
*
|
||||
* The default value is the same as the title property.
|
||||
*
|
||||
* @warning Any reserved HTML characters in the string value for this property
|
||||
* must have been replaced by character entities e.g. "&" becomes "&".
|
||||
*/
|
||||
@property(nonatomic, copy) NSString* header;
|
||||
|
||||
/**
|
||||
* Sets the prologue for the uploader web interface.
|
||||
*
|
||||
* The default value is a short help text.
|
||||
*
|
||||
* @warning The string value for this property must be raw HTML
|
||||
* e.g. "<p>Some text</p>"
|
||||
*/
|
||||
@property(nonatomic, copy) NSString* prologue;
|
||||
|
||||
/**
|
||||
* Sets the epilogue for the uploader web interface.
|
||||
*
|
||||
* The default value is nil i.e. no epilogue.
|
||||
*
|
||||
* @warning The string value for this property must be raw HTML
|
||||
* e.g. "<p>Some text</p>"
|
||||
*/
|
||||
@property(nonatomic, copy) NSString* epilogue;
|
||||
|
||||
/**
|
||||
* Sets the footer for the uploader web interface.
|
||||
*
|
||||
* The default value is the application name and version.
|
||||
*
|
||||
* @warning Any reserved HTML characters in the string value for this property
|
||||
* must have been replaced by character entities e.g. "&" becomes "&".
|
||||
*/
|
||||
@property(nonatomic, copy) NSString* footer;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*/
|
||||
- (instancetype)initWithUploadDirectory:(NSString*)path;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Hooks to customize the behavior of GCDWebUploader.
|
||||
*
|
||||
* @warning These methods can be called on any GCD thread.
|
||||
*/
|
||||
@interface GCDWebUploader (Subclassing)
|
||||
|
||||
/**
|
||||
* This method is called to check if a file upload is allowed to complete.
|
||||
* The uploaded file is available for inspection at "tempPath".
|
||||
*
|
||||
* The default implementation returns YES.
|
||||
*/
|
||||
- (BOOL)shouldUploadFileAtPath:(NSString*)path withTemporaryFile:(NSString*)tempPath;
|
||||
|
||||
/**
|
||||
* This method is called to check if a file or directory is allowed to be moved.
|
||||
*
|
||||
* The default implementation returns YES.
|
||||
*/
|
||||
- (BOOL)shouldMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath;
|
||||
|
||||
/**
|
||||
* This method is called to check if a file or directory is allowed to be deleted.
|
||||
*
|
||||
* The default implementation returns YES.
|
||||
*/
|
||||
- (BOOL)shouldDeleteItemAtPath:(NSString*)path;
|
||||
|
||||
/**
|
||||
* This method is called to check if a directory is allowed to be created.
|
||||
*
|
||||
* The default implementation returns YES.
|
||||
*/
|
||||
- (BOOL)shouldCreateDirectoryAtPath:(NSString*)path;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
429
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.m
Normal file
429
pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.m
Normal file
@ -0,0 +1,429 @@
|
||||
/*
|
||||
Copyright (c) 2012-2019, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebUploader requires ARC
|
||||
#endif
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#endif
|
||||
|
||||
#import "GCDWebUploader.h"
|
||||
#import "GCDWebServerFunctions.h"
|
||||
|
||||
#import "GCDWebServerDataRequest.h"
|
||||
#import "GCDWebServerMultiPartFormRequest.h"
|
||||
#import "GCDWebServerURLEncodedFormRequest.h"
|
||||
|
||||
#import "GCDWebServerDataResponse.h"
|
||||
#import "GCDWebServerErrorResponse.h"
|
||||
#import "GCDWebServerFileResponse.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface GCDWebUploader (Methods)
|
||||
- (nullable GCDWebServerResponse*)listDirectory:(GCDWebServerRequest*)request;
|
||||
- (nullable GCDWebServerResponse*)downloadFile:(GCDWebServerRequest*)request;
|
||||
- (nullable GCDWebServerResponse*)uploadFile:(GCDWebServerMultiPartFormRequest*)request;
|
||||
- (nullable GCDWebServerResponse*)moveItem:(GCDWebServerURLEncodedFormRequest*)request;
|
||||
- (nullable GCDWebServerResponse*)deleteItem:(GCDWebServerURLEncodedFormRequest*)request;
|
||||
- (nullable GCDWebServerResponse*)createDirectory:(GCDWebServerURLEncodedFormRequest*)request;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@implementation GCDWebUploader
|
||||
|
||||
@dynamic delegate;
|
||||
|
||||
- (instancetype)initWithUploadDirectory:(NSString*)path {
|
||||
if ((self = [super init])) {
|
||||
NSString* bundlePath = [[NSBundle bundleForClass:[GCDWebUploader class]] pathForResource:@"GCDWebUploader" ofType:@"bundle"];
|
||||
if (bundlePath == nil) {
|
||||
return nil;
|
||||
}
|
||||
NSBundle* siteBundle = [NSBundle bundleWithPath:bundlePath];
|
||||
if (siteBundle == nil) {
|
||||
return nil;
|
||||
}
|
||||
_uploadDirectory = [path copy];
|
||||
GCDWebUploader* __unsafe_unretained server = self;
|
||||
|
||||
// Resource files
|
||||
[self addGETHandlerForBasePath:@"/" directoryPath:(NSString*)[siteBundle resourcePath] indexFilename:nil cacheAge:3600 allowRangeRequests:NO];
|
||||
|
||||
// Web page
|
||||
[self addHandlerForMethod:@"GET"
|
||||
path:@"/"
|
||||
requestClass:[GCDWebServerRequest class]
|
||||
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
NSString* device = [[UIDevice currentDevice] name];
|
||||
#else
|
||||
NSString* device = CFBridgingRelease(SCDynamicStoreCopyComputerName(NULL, NULL));
|
||||
#endif
|
||||
NSString* title = server.title;
|
||||
if (title == nil) {
|
||||
title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
||||
if (title == nil) {
|
||||
title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
|
||||
}
|
||||
#if !TARGET_OS_IPHONE
|
||||
if (title == nil) {
|
||||
title = [[NSProcessInfo processInfo] processName];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
NSString* header = server.header;
|
||||
if (header == nil) {
|
||||
header = title;
|
||||
}
|
||||
NSString* prologue = server.prologue;
|
||||
if (prologue == nil) {
|
||||
prologue = [siteBundle localizedStringForKey:@"PROLOGUE" value:@"" table:nil];
|
||||
}
|
||||
NSString* epilogue = server.epilogue;
|
||||
if (epilogue == nil) {
|
||||
epilogue = [siteBundle localizedStringForKey:@"EPILOGUE" value:@"" table:nil];
|
||||
}
|
||||
NSString* footer = server.footer;
|
||||
if (footer == nil) {
|
||||
NSString* name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
||||
if (name == nil) {
|
||||
name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
|
||||
}
|
||||
NSString* version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
||||
#if !TARGET_OS_IPHONE
|
||||
if (!name && !version) {
|
||||
name = @"OS X";
|
||||
version = [[NSProcessInfo processInfo] operatingSystemVersionString];
|
||||
}
|
||||
#endif
|
||||
footer = [NSString stringWithFormat:[siteBundle localizedStringForKey:@"FOOTER_FORMAT" value:@"" table:nil], name, version];
|
||||
}
|
||||
return [GCDWebServerDataResponse responseWithHTMLTemplate:(NSString*)[siteBundle pathForResource:@"index" ofType:@"html"]
|
||||
variables:@{
|
||||
@"device" : device,
|
||||
@"title" : title,
|
||||
@"header" : header,
|
||||
@"prologue" : prologue,
|
||||
@"epilogue" : epilogue,
|
||||
@"footer" : footer
|
||||
}];
|
||||
}];
|
||||
|
||||
// File listing
|
||||
[self addHandlerForMethod:@"GET"
|
||||
path:@"/list"
|
||||
requestClass:[GCDWebServerRequest class]
|
||||
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||
return [server listDirectory:request];
|
||||
}];
|
||||
|
||||
// File download
|
||||
[self addHandlerForMethod:@"GET"
|
||||
path:@"/download"
|
||||
requestClass:[GCDWebServerRequest class]
|
||||
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||
return [server downloadFile:request];
|
||||
}];
|
||||
|
||||
// File upload
|
||||
[self addHandlerForMethod:@"POST"
|
||||
path:@"/upload"
|
||||
requestClass:[GCDWebServerMultiPartFormRequest class]
|
||||
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||
return [server uploadFile:(GCDWebServerMultiPartFormRequest*)request];
|
||||
}];
|
||||
|
||||
// File and folder moving
|
||||
[self addHandlerForMethod:@"POST"
|
||||
path:@"/move"
|
||||
requestClass:[GCDWebServerURLEncodedFormRequest class]
|
||||
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||
return [server moveItem:(GCDWebServerURLEncodedFormRequest*)request];
|
||||
}];
|
||||
|
||||
// File and folder deletion
|
||||
[self addHandlerForMethod:@"POST"
|
||||
path:@"/delete"
|
||||
requestClass:[GCDWebServerURLEncodedFormRequest class]
|
||||
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||
return [server deleteItem:(GCDWebServerURLEncodedFormRequest*)request];
|
||||
}];
|
||||
|
||||
// Directory creation
|
||||
[self addHandlerForMethod:@"POST"
|
||||
path:@"/create"
|
||||
requestClass:[GCDWebServerURLEncodedFormRequest class]
|
||||
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||
return [server createDirectory:(GCDWebServerURLEncodedFormRequest*)request];
|
||||
}];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebUploader (Methods)
|
||||
|
||||
- (BOOL)_checkFileExtension:(NSString*)fileName {
|
||||
if (_allowedFileExtensions && ![_allowedFileExtensions containsObject:[[fileName pathExtension] lowercaseString]]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString*)_uniquePathForPath:(NSString*)path {
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
||||
NSString* directory = [path stringByDeletingLastPathComponent];
|
||||
NSString* file = [path lastPathComponent];
|
||||
NSString* base = [file stringByDeletingPathExtension];
|
||||
NSString* extension = [file pathExtension];
|
||||
int retries = 0;
|
||||
do {
|
||||
if (extension.length) {
|
||||
path = [directory stringByAppendingPathComponent:(NSString*)[[base stringByAppendingFormat:@" (%i)", ++retries] stringByAppendingPathExtension:extension]];
|
||||
} else {
|
||||
path = [directory stringByAppendingPathComponent:[base stringByAppendingFormat:@" (%i)", ++retries]];
|
||||
}
|
||||
} while ([[NSFileManager defaultManager] fileExistsAtPath:path]);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
- (GCDWebServerResponse*)listDirectory:(GCDWebServerRequest*)request {
|
||||
NSString* relativePath = [[request query] objectForKey:@"path"];
|
||||
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
|
||||
BOOL isDirectory = NO;
|
||||
if (!absolutePath || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||
}
|
||||
if (!isDirectory) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"\"%@\" is not a directory", relativePath];
|
||||
}
|
||||
|
||||
NSString* directoryName = [absolutePath lastPathComponent];
|
||||
if (!_allowHiddenItems && [directoryName hasPrefix:@"."]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Listing directory name \"%@\" is not allowed", directoryName];
|
||||
}
|
||||
|
||||
NSError* error = nil;
|
||||
NSArray* contents = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:absolutePath error:&error] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
|
||||
if (contents == nil) {
|
||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath];
|
||||
}
|
||||
|
||||
NSMutableArray* array = [NSMutableArray array];
|
||||
for (NSString* item in [contents sortedArrayUsingSelector:@selector(localizedStandardCompare:)]) {
|
||||
if (_allowHiddenItems || ![item hasPrefix:@"."]) {
|
||||
NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[absolutePath stringByAppendingPathComponent:item] error:NULL];
|
||||
NSString* type = [attributes objectForKey:NSFileType];
|
||||
if ([type isEqualToString:NSFileTypeRegular] && [self _checkFileExtension:item]) {
|
||||
[array addObject:@{
|
||||
@"path" : [relativePath stringByAppendingPathComponent:item],
|
||||
@"name" : item,
|
||||
@"size" : (NSNumber*)[attributes objectForKey:NSFileSize]
|
||||
}];
|
||||
} else if ([type isEqualToString:NSFileTypeDirectory]) {
|
||||
[array addObject:@{
|
||||
@"path" : [[relativePath stringByAppendingPathComponent:item] stringByAppendingString:@"/"],
|
||||
@"name" : item
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [GCDWebServerDataResponse responseWithJSONObject:array];
|
||||
}
|
||||
|
||||
- (GCDWebServerResponse*)downloadFile:(GCDWebServerRequest*)request {
|
||||
NSString* relativePath = [[request query] objectForKey:@"path"];
|
||||
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
|
||||
BOOL isDirectory = NO;
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||
}
|
||||
if (isDirectory) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"\"%@\" is a directory", relativePath];
|
||||
}
|
||||
|
||||
NSString* fileName = [absolutePath lastPathComponent];
|
||||
if (([fileName hasPrefix:@"."] && !_allowHiddenItems) || ![self _checkFileExtension:fileName]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Downlading file name \"%@\" is not allowed", fileName];
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(webUploader:didDownloadFileAtPath:)]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.delegate webUploader:self didDownloadFileAtPath:absolutePath];
|
||||
});
|
||||
}
|
||||
return [GCDWebServerFileResponse responseWithFile:absolutePath isAttachment:YES];
|
||||
}
|
||||
|
||||
- (GCDWebServerResponse*)uploadFile:(GCDWebServerMultiPartFormRequest*)request {
|
||||
NSRange range = [[request.headers objectForKey:@"Accept"] rangeOfString:@"application/json" options:NSCaseInsensitiveSearch];
|
||||
NSString* contentType = (range.location != NSNotFound ? @"application/json" : @"text/plain; charset=utf-8"); // Required when using iFrame transport (see https://github.com/blueimp/jQuery-File-Upload/wiki/Setup)
|
||||
|
||||
GCDWebServerMultiPartFile* file = [request firstFileForControlName:@"files[]"];
|
||||
if ((!_allowHiddenItems && [file.fileName hasPrefix:@"."]) || ![self _checkFileExtension:file.fileName]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploaded file name \"%@\" is not allowed", file.fileName];
|
||||
}
|
||||
NSString* relativePath = [[request firstArgumentForControlName:@"path"] string];
|
||||
NSString* absolutePath = [self _uniquePathForPath:[[_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)] stringByAppendingPathComponent:file.fileName]];
|
||||
|
||||
if (![self shouldUploadFileAtPath:absolutePath withTemporaryFile:file.temporaryPath]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploading file \"%@\" to \"%@\" is not permitted", file.fileName, relativePath];
|
||||
}
|
||||
|
||||
NSError* error = nil;
|
||||
if (![[NSFileManager defaultManager] moveItemAtPath:file.temporaryPath toPath:absolutePath error:&error]) {
|
||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving uploaded file to \"%@\"", relativePath];
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(webUploader:didUploadFileAtPath:)]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.delegate webUploader:self didUploadFileAtPath:absolutePath];
|
||||
});
|
||||
}
|
||||
return [GCDWebServerDataResponse responseWithJSONObject:@{} contentType:contentType];
|
||||
}
|
||||
|
||||
- (GCDWebServerResponse*)moveItem:(GCDWebServerURLEncodedFormRequest*)request {
|
||||
NSString* oldRelativePath = [request.arguments objectForKey:@"oldPath"];
|
||||
NSString* oldAbsolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(oldRelativePath)];
|
||||
BOOL isDirectory = NO;
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:oldAbsolutePath isDirectory:&isDirectory]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", oldRelativePath];
|
||||
}
|
||||
|
||||
NSString* newRelativePath = [request.arguments objectForKey:@"newPath"];
|
||||
NSString* newAbsolutePath = [self _uniquePathForPath:[_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(newRelativePath)]];
|
||||
|
||||
NSString* itemName = [newAbsolutePath lastPathComponent];
|
||||
if ((!_allowHiddenItems && [itemName hasPrefix:@"."]) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving to item name \"%@\" is not allowed", itemName];
|
||||
}
|
||||
|
||||
if (![self shouldMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Moving \"%@\" to \"%@\" is not permitted", oldRelativePath, newRelativePath];
|
||||
}
|
||||
|
||||
NSError* error = nil;
|
||||
if (![[NSFileManager defaultManager] moveItemAtPath:oldAbsolutePath toPath:newAbsolutePath error:&error]) {
|
||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed moving \"%@\" to \"%@\"", oldRelativePath, newRelativePath];
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(webUploader:didMoveItemFromPath:toPath:)]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.delegate webUploader:self didMoveItemFromPath:oldAbsolutePath toPath:newAbsolutePath];
|
||||
});
|
||||
}
|
||||
return [GCDWebServerDataResponse responseWithJSONObject:@{}];
|
||||
}
|
||||
|
||||
- (GCDWebServerResponse*)deleteItem:(GCDWebServerURLEncodedFormRequest*)request {
|
||||
NSString* relativePath = [request.arguments objectForKey:@"path"];
|
||||
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)];
|
||||
BOOL isDirectory = NO;
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||
}
|
||||
|
||||
NSString* itemName = [absolutePath lastPathComponent];
|
||||
if (([itemName hasPrefix:@"."] && !_allowHiddenItems) || (!isDirectory && ![self _checkFileExtension:itemName])) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting item name \"%@\" is not allowed", itemName];
|
||||
}
|
||||
|
||||
if (![self shouldDeleteItemAtPath:absolutePath]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Deleting \"%@\" is not permitted", relativePath];
|
||||
}
|
||||
|
||||
NSError* error = nil;
|
||||
if (![[NSFileManager defaultManager] removeItemAtPath:absolutePath error:&error]) {
|
||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed deleting \"%@\"", relativePath];
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(webUploader:didDeleteItemAtPath:)]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.delegate webUploader:self didDeleteItemAtPath:absolutePath];
|
||||
});
|
||||
}
|
||||
return [GCDWebServerDataResponse responseWithJSONObject:@{}];
|
||||
}
|
||||
|
||||
- (GCDWebServerResponse*)createDirectory:(GCDWebServerURLEncodedFormRequest*)request {
|
||||
NSString* relativePath = [request.arguments objectForKey:@"path"];
|
||||
NSString* absolutePath = [self _uniquePathForPath:[_uploadDirectory stringByAppendingPathComponent:GCDWebServerNormalizePath(relativePath)]];
|
||||
|
||||
NSString* directoryName = [absolutePath lastPathComponent];
|
||||
if (!_allowHiddenItems && [directoryName hasPrefix:@"."]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory name \"%@\" is not allowed", directoryName];
|
||||
}
|
||||
|
||||
if (![self shouldCreateDirectoryAtPath:absolutePath]) {
|
||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Creating directory \"%@\" is not permitted", relativePath];
|
||||
}
|
||||
|
||||
NSError* error = nil;
|
||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:NO attributes:nil error:&error]) {
|
||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed creating directory \"%@\"", relativePath];
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(webUploader:didCreateDirectoryAtPath:)]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.delegate webUploader:self didCreateDirectoryAtPath:absolutePath];
|
||||
});
|
||||
}
|
||||
return [GCDWebServerDataResponse responseWithJSONObject:@{}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebUploader (Subclassing)
|
||||
|
||||
- (BOOL)shouldUploadFileAtPath:(NSString*)path withTemporaryFile:(NSString*)tempPath {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)shouldMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)shouldDeleteItemAtPath:(NSString*)path {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)shouldCreateDirectoryAtPath:(NSString*)path {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
25
pkg/apple/WebServer/WebServer.h
Normal file
25
pkg/apple/WebServer/WebServer.h
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// WebServer.h
|
||||
// MAME4iOS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 1/15/19.
|
||||
// Copyright © 2019 Seleuco. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "GCDWebUploader.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WebServer : NSObject
|
||||
|
||||
@property (nonatomic,readonly,strong) GCDWebUploader* webUploader;
|
||||
|
||||
+(WebServer*)sharedInstance;
|
||||
|
||||
-(void)startUploader;
|
||||
-(void)stopUploader;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
50
pkg/apple/WebServer/WebServer.m
Normal file
50
pkg/apple/WebServer/WebServer.m
Normal file
@ -0,0 +1,50 @@
|
||||
//
|
||||
// WebServer.m
|
||||
// MAME4iOS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 1/15/19.
|
||||
// Copyright © 2019 Seleuco. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WebServer.h"
|
||||
|
||||
@implementation WebServer
|
||||
|
||||
#pragma mark - singleton method
|
||||
|
||||
+(WebServer*)sharedInstance {
|
||||
static dispatch_once_t predicate = 0;
|
||||
static id sharedObject = nil;
|
||||
dispatch_once(&predicate, ^{
|
||||
sharedObject = [[self alloc] init];
|
||||
});
|
||||
return sharedObject;
|
||||
}
|
||||
|
||||
#pragma mark Init
|
||||
|
||||
-(instancetype)init {
|
||||
if ( self = [super init] ) {
|
||||
#if TARGET_OS_IOS
|
||||
NSString* docsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
||||
#elif TARGET_OS_TV
|
||||
NSString* docsPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
|
||||
#endif
|
||||
_webUploader = [[GCDWebUploader alloc] initWithUploadDirectory:docsPath];
|
||||
_webUploader.allowHiddenItems = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)startUploader {
|
||||
if ( _webUploader.isRunning ) {
|
||||
[_webUploader stop];
|
||||
}
|
||||
[_webUploader start];
|
||||
}
|
||||
|
||||
-(void)stopUploader {
|
||||
[_webUploader stop];
|
||||
}
|
||||
|
||||
@end
|
@ -16,7 +16,7 @@ fi
|
||||
|
||||
ITEMS=""
|
||||
|
||||
CORES_DIR="${PROJECT_DIR}/iOS/modules"
|
||||
CORES_DIR="${PROJECT_DIR}/$1/modules"
|
||||
echo "Cores dir: ${CORES_DIR}"
|
||||
if [ -d "$CORES_DIR" ] ; then
|
||||
CORES=$(find "${CORES_DIR}" -depth -type d -name "*.framework" -or -name "*.dylib" -or -name "*.bundle" | sed -e "s/\(.*framework\)/\1\/Versions\/A\//")
|
||||
|
@ -5,7 +5,7 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>RetroArch</string>
|
||||
<string>RetroArchT</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
6
pkg/apple/iOS/Resources/Media.xcassets/Contents.json
Normal file
6
pkg/apple/iOS/Resources/Media.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "tv"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"layers" : [
|
||||
{
|
||||
"filename" : "Front.imagestacklayer"
|
||||
},
|
||||
{
|
||||
"filename" : "Middle.imagestacklayer"
|
||||
},
|
||||
{
|
||||
"filename" : "Back.imagestacklayer"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "tv"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "tv"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "tv",
|
||||
"filename" : "retroarch_logo_back.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "tv",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 359 B |
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"layers" : [
|
||||
{
|
||||
"filename" : "Front.imagestacklayer"
|
||||
},
|
||||
{
|
||||
"filename" : "Middle.imagestacklayer"
|
||||
},
|
||||
{
|
||||
"filename" : "Back.imagestacklayer"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "tv",
|
||||
"filename" : "retroarch_logo_front.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "tv",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "tv",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "tv",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
{
|
||||
"assets" : [
|
||||
{
|
||||
"size" : "1280x768",
|
||||
"idiom" : "tv",
|
||||
"filename" : "App Icon - App Store.imagestack",
|
||||
"role" : "primary-app-icon"
|
||||
},
|
||||
{
|
||||
"size" : "400x240",
|
||||
"idiom" : "tv",
|
||||
"filename" : "App Icon.imagestack",
|
||||
"role" : "primary-app-icon"
|
||||
},
|
||||
{
|
||||
"size" : "2320x720",
|
||||
"idiom" : "tv",
|
||||
"filename" : "Top Shelf Image Wide.imageset",
|
||||
"role" : "top-shelf-image-wide"
|
||||
},
|
||||
{
|
||||
"size" : "1920x720",
|
||||
"idiom" : "tv",
|
||||
"filename" : "Top Shelf Image.imageset",
|
||||
"role" : "top-shelf-image"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "tv",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "tv",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "tv-marketing",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "tv-marketing",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "tv",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "tv",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "tv-marketing",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "tv-marketing",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
6
pkg/apple/tvOS/Assets.xcassets/Contents.json
Normal file
6
pkg/apple/tvOS/Assets.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "tv",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "11.0",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "tv",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "9.0",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"layers" : [
|
||||
{
|
||||
"filename" : "Layer 1.imagestacklayer"
|
||||
},
|
||||
{
|
||||
"filename" : "Layer 2.imagestacklayer"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"canvasSize" : {
|
||||
"width" : 400,
|
||||
"height" : 240
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"images" : [
|
||||
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"frame-size" : {
|
||||
"width" : 400,
|
||||
"height" : 240
|
||||
},
|
||||
"frame-center" : {
|
||||
"x" : 200,
|
||||
"y" : 120
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"images" : [
|
||||
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"frame-size" : {
|
||||
"width" : 400,
|
||||
"height" : 240
|
||||
},
|
||||
"frame-center" : {
|
||||
"x" : 200,
|
||||
"y" : 120
|
||||
}
|
||||
}
|
||||
}
|
32
pkg/apple/tvOS/Info.plist
Normal file
32
pkg/apple/tvOS/Info.plist
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>RetroArch</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.7.5</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.7.5</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Automatic</string>
|
||||
</dict>
|
||||
</plist>
|
@ -31,6 +31,10 @@
|
||||
#if defined(HAVE_COCOATOUCH)
|
||||
#include <UIKit/UIKit.h>
|
||||
|
||||
#if TARGET_OS_TV
|
||||
#import <GameController/GameController.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_AVFOUNDATION
|
||||
#import <AVFoundation/AVCaptureOutput.h>
|
||||
#endif
|
||||
@ -59,8 +63,12 @@ typedef struct
|
||||
} apple_frontend_settings_t;
|
||||
extern apple_frontend_settings_t apple_frontend_settings;
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
@interface CocoaView : UIViewController<CLLocationManagerDelegate,
|
||||
AVCaptureAudioDataOutputSampleBufferDelegate>
|
||||
#elif TARGET_OS_TV
|
||||
@interface CocoaView : GCEventViewController
|
||||
#endif
|
||||
+ (CocoaView*)get;
|
||||
@end
|
||||
|
||||
|
@ -43,6 +43,11 @@
|
||||
#include "../../../location/location_driver.h"
|
||||
#include "../../../camera/camera_driver.h"
|
||||
|
||||
#ifdef HAVE_COCOATOUCH
|
||||
#import "GCDWebUploader.h"
|
||||
#import "WebServer.h"
|
||||
#endif
|
||||
|
||||
static CocoaView* g_instance;
|
||||
|
||||
#if defined(HAVE_COCOA)
|
||||
@ -56,6 +61,13 @@ void *nsview_get_ptr(void)
|
||||
void cocoagl_gfx_ctx_update(void);
|
||||
void *glkitview_init(void);
|
||||
|
||||
#ifdef HAVE_COCOATOUCH
|
||||
@interface CocoaView()<GCDWebUploaderDelegate> {
|
||||
|
||||
}
|
||||
@end
|
||||
#endif
|
||||
|
||||
@implementation CocoaView
|
||||
|
||||
#if defined(HAVE_COCOA)
|
||||
@ -87,8 +99,9 @@ void *glkitview_init(void);
|
||||
[self registerForDraggedTypes:[NSArray arrayWithObjects:NSColorPboardType, NSFilenamesPboardType, nil]];
|
||||
#elif defined(HAVE_COCOATOUCH)
|
||||
self.view = (__bridge GLKView*)glkitview_init();
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showPauseIndicator) name:UIApplicationWillEnterForegroundNotification object:nil];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return self;
|
||||
@ -152,7 +165,7 @@ void *glkitview_init(void);
|
||||
[self setNeedsDisplay: YES];
|
||||
}
|
||||
|
||||
#elif defined(HAVE_COCOATOUCH)
|
||||
#elif TARGET_OS_IOS
|
||||
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures
|
||||
{
|
||||
return UIRectEdgeBottom;
|
||||
@ -163,15 +176,6 @@ void *glkitview_init(void);
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
/* Pause Menus. */
|
||||
[self showPauseIndicator];
|
||||
if (@available(iOS 11.0, *)) {
|
||||
[self setNeedsUpdateOfHomeIndicatorAutoHidden];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
|
||||
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
||||
if (@available(iOS 11, *)) {
|
||||
@ -273,6 +277,58 @@ void *glkitview_init(void);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_COCOATOUCH
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
#if TARGET_OS_IOS
|
||||
/* Pause Menus. */
|
||||
[self showPauseIndicator];
|
||||
if (@available(iOS 11.0, *)) {
|
||||
[self setNeedsUpdateOfHomeIndicatorAutoHidden];
|
||||
}
|
||||
#elif TARGET_OS_TV
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
-(void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
#if TARGET_OS_TV
|
||||
[[WebServer sharedInstance] startUploader];
|
||||
[WebServer sharedInstance].webUploader.delegate = self;
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark GCDWebServerDelegate
|
||||
- (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server {
|
||||
NSMutableString *servers = [[NSMutableString alloc] init];
|
||||
if ( server.serverURL != nil ) {
|
||||
[servers appendString:[NSString stringWithFormat:@"%@",server.serverURL]];
|
||||
}
|
||||
if ( servers.length > 0 ) {
|
||||
[servers appendString:@"\n\n"];
|
||||
}
|
||||
if ( server.bonjourServerURL != nil ) {
|
||||
[servers appendString:[NSString stringWithFormat:@"%@",server.bonjourServerURL]];
|
||||
}
|
||||
#if TARGET_OS_TV
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Web Server Started" message:[NSString stringWithFormat:@"To transfer files from your computer, go to one of these addresses on your web browser:\n\n%@",servers] preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
}]];
|
||||
[self presentViewController:alert animated:YES completion:^{
|
||||
}];
|
||||
#elif TARGET_OS_IOS
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Web Server Started" message:[NSString stringWithFormat:@"To transfer ROMs from your computer, go to one of these addresses on your web browser:\n\n%@",servers] preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Stop Server" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[[WebServer sharedInstance] webUploader].delegate = nil;
|
||||
[[WebServer sharedInstance] stopUploader];
|
||||
}]];
|
||||
[self presentViewController:alert animated:YES completion:^{
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
#endif // end HAVE_COCOATOUCH
|
||||
|
||||
#ifdef HAVE_AVFOUNDATION
|
||||
#include "../../gfx/common/gl_common.h"
|
||||
|
||||
|
@ -338,7 +338,12 @@ enum
|
||||
|
||||
-(NSString*)documentsDirectory {
|
||||
if ( _documentsDirectory == nil ) {
|
||||
#if TARGET_OS_IOS
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
#elif TARGET_OS_TV
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||
#endif
|
||||
|
||||
_documentsDirectory = paths.firstObject;
|
||||
}
|
||||
return _documentsDirectory;
|
||||
@ -362,9 +367,11 @@ enum
|
||||
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||
[self.window makeKeyAndVisible];
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
self.mainmenu = [RAMainMenu new];
|
||||
self.mainmenu.last_menu = self.mainmenu;
|
||||
[self pushViewController:self.mainmenu animated:NO];
|
||||
#endif
|
||||
|
||||
[self refreshSystemConfig];
|
||||
[self showGameView];
|
||||
@ -441,7 +448,9 @@ enum
|
||||
|
||||
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
|
||||
{
|
||||
#if TARGET_OS_IOS
|
||||
[self setToolbarHidden:![[viewController toolbarItems] count] animated:YES];
|
||||
#endif
|
||||
[self refreshSystemConfig];
|
||||
}
|
||||
|
||||
@ -451,10 +460,15 @@ enum
|
||||
/* implicitly initializes your audio session */
|
||||
[self supportOtherAudioSessions];
|
||||
#endif
|
||||
[self popToRootViewControllerAnimated:NO];
|
||||
|
||||
[self popToRootViewControllerAnimated:NO];
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
[self setToolbarHidden:true animated:NO];
|
||||
[[UIApplication sharedApplication] setStatusBarHidden:true withAnimation:UIStatusBarAnimationNone];
|
||||
[[UIApplication sharedApplication] setIdleTimerDisabled:true];
|
||||
#endif
|
||||
|
||||
[[UIApplication sharedApplication] setIdleTimerDisabled:true];
|
||||
[self.window setRootViewController:[CocoaView get]];
|
||||
|
||||
ui_companion_cocoatouch_event_command(NULL, CMD_EVENT_AUDIO_START);
|
||||
@ -468,7 +482,10 @@ enum
|
||||
#endif
|
||||
rarch_enable_ui();
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
[[UIApplication sharedApplication] setStatusBarHidden:false withAnimation:UIStatusBarAnimationNone];
|
||||
#endif
|
||||
|
||||
[[UIApplication sharedApplication] setIdleTimerDisabled:false];
|
||||
[self.window setRootViewController:self];
|
||||
}
|
||||
@ -491,6 +508,7 @@ enum
|
||||
|
||||
- (void)refreshSystemConfig
|
||||
{
|
||||
#if TARGET_OS_IOS
|
||||
/* Get enabled orientations */
|
||||
apple_frontend_settings.orientation_flags = UIInterfaceOrientationMaskAll;
|
||||
|
||||
@ -499,15 +517,19 @@ enum
|
||||
else if (string_is_equal(apple_frontend_settings.orientations, "portrait"))
|
||||
apple_frontend_settings.orientation_flags = UIInterfaceOrientationMaskPortrait
|
||||
| UIInterfaceOrientationMaskPortraitUpsideDown;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)mainMenuRefresh
|
||||
{
|
||||
#if TARGET_OS_IOS
|
||||
[self.mainmenu reloadData];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)mainMenuPushPop: (bool)pushp
|
||||
{
|
||||
#if TARGET_OS_IOS
|
||||
if ( pushp )
|
||||
{
|
||||
self.menu_count++;
|
||||
@ -528,6 +550,7 @@ enum
|
||||
self.mainmenu = self.mainmenu.last_menu;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)supportOtherAudioSessions
|
||||
@ -542,7 +565,9 @@ enum
|
||||
|
||||
- (void)mainMenuRenderMessageBox:(NSString *)msg
|
||||
{
|
||||
#if TARGET_OS_IOS
|
||||
[self.mainmenu renderMessageBox:msg];
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
@ -693,7 +718,9 @@ static void ui_companion_cocoatouch_msg_queue_push(const char *msg,
|
||||
|
||||
if (ap && msg)
|
||||
{
|
||||
#if TARGET_OS_IOS
|
||||
[ap.mainmenu msgQueuePush: [NSString stringWithUTF8String:msg]];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user