diff --git a/.gitmodules b/.gitmodules index c50a1ba..19b3794 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,6 +19,9 @@ [submodule "tests/fs-test"] path = tests/fs-test url = https://github.com/sheinz/fs-test +[submodule "extras/bearssl/BearSSL"] + path = extras/bearssl/BearSSL + url = https://www.bearssl.org/git/BearSSL [submodule "extras/http-parser/http-parser"] path = extras/http-parser/http-parser url = https://github.com/nodejs/http-parser diff --git a/core/app_main.c b/core/app_main.c index ee27092..05ad952 100644 --- a/core/app_main.c +++ b/core/app_main.c @@ -21,6 +21,8 @@ #include "esp/spi_regs.h" #include "esp/dport_regs.h" #include "esp/wdev_regs.h" +#include "esp/wdt_regs.h" +#include "esp/rtcmem_regs.h" #include "esp/hwrand.h" #include "os_version.h" diff --git a/core/debug_dumps.c b/core/debug_dumps.c index c5af763..6300439 100644 --- a/core/debug_dumps.c +++ b/core/debug_dumps.c @@ -19,6 +19,7 @@ #include "xtensa_ops.h" #include "esp/rom.h" #include "esp/uart.h" +#include "esp/dport_regs.h" #include "espressif/esp_common.h" #include "esplibs/libmain.h" diff --git a/examples/crc_example/crc_config_perso.h b/examples/crc_example/crc_config_perso.h new file mode 100644 index 0000000..4f4b97d --- /dev/null +++ b/examples/crc_example/crc_config_perso.h @@ -0,0 +1,15 @@ +/* + * perso_config.h + * + * Created on: 11 févr. 2017 + * Author: lilian + */ + +#include "espressif/esp_common.h" +#include "FreeRTOS.h" + +#define CRC_DEBUG 0 +#define CRC_4BYTE_SUPPORT 0 +/* Use the defaults for everything else */ +#include_next "crc_config.h" + diff --git a/examples/crc_example/crc_main.c b/examples/crc_example/crc_main.c index 10bca3f..85f4434 100644 --- a/examples/crc_example/crc_main.c +++ b/examples/crc_example/crc_main.c @@ -9,7 +9,7 @@ #include "esp8266.h" #include -//extras +//crc lib #include "crc_generic.h" #define NUMBER_COMPUTE_TEST 1000 diff --git a/examples/http_get_bearssl/Makefile b/examples/http_get_bearssl/Makefile new file mode 100644 index 0000000..5f0b736 --- /dev/null +++ b/examples/http_get_bearssl/Makefile @@ -0,0 +1,6 @@ +PROGRAM=http_get_bearssl +EXTRA_COMPONENTS = extras/bearssl + +EXTRA_CFLAGS +=-DCONFIG_EPOCH_TIME=$(shell date --utc '+%s') + +include ../../common.mk diff --git a/examples/http_get_bearssl/http_get_bearssl.c b/examples/http_get_bearssl/http_get_bearssl.c new file mode 100644 index 0000000..593b423 --- /dev/null +++ b/examples/http_get_bearssl/http_get_bearssl.c @@ -0,0 +1,342 @@ +/* http_get_bearssl - HTTPS version of the http_get example, using BearSSL. + * + * Retrieves a JSON response from the howsmyssl.com API via HTTPS over TLS v1.2. + * + * Validates the server's certificate using a hardcoded public key. + * + * Adapted from the client_basic sample in BearSSL. + * + * Original Copyright (c) 2016 Thomas Pornin , MIT License. + * Additions Copyright (C) 2016 Stefan Schake, MIT License. + */ +#include "espressif/esp_common.h" +#include "esp/uart.h" +#include "esp/hwrand.h" + +#include + +#include "FreeRTOS.h" +#include "task.h" + +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include "lwip/netdb.h" +#include "lwip/dns.h" +#include "lwip/api.h" + +#include "ssid_config.h" + +#include "bearssl.h" + +#define CLOCK_SECONDS_PER_MINUTE (60UL) +#define CLOCK_MINUTES_PER_HOUR (60UL) +#define CLOCK_HOURS_PER_DAY (24UL) +#define CLOCK_SECONDS_PER_HOUR (CLOCK_MINUTES_PER_HOUR*CLOCK_SECONDS_PER_MINUTE) +#define CLOCK_SECONDS_PER_DAY (CLOCK_HOURS_PER_DAY*CLOCK_SECONDS_PER_HOUR) + +#define WEB_SERVER "www.howsmyssl.com" +#define WEB_PORT "443" +#define WEB_URL "https://www.howsmyssl.com/a/check" + +#define GET_REQUEST "GET "WEB_URL" HTTP/1.1\nHost: "WEB_SERVER"\n\n" + +/* + * Low-level data read callback for the simplified SSL I/O API. + */ +static int +sock_read(void *ctx, unsigned char *buf, size_t len) +{ + for (;;) { + ssize_t rlen; + + rlen = read(*(int *)ctx, buf, len); + if (rlen <= 0) { + if (rlen < 0 && errno == EINTR) { + continue; + } + return -1; + } + return (int)rlen; + } +} + +/* + * Low-level data write callback for the simplified SSL I/O API. + */ +static int +sock_write(void *ctx, const unsigned char *buf, size_t len) +{ + for (;;) { + ssize_t wlen; + + wlen = write(*(int *)ctx, buf, len); + if (wlen <= 0) { + if (wlen < 0 && errno == EINTR) { + continue; + } + return -1; + } + return (int)wlen; + } +} + +/* + * The hardcoded trust anchors. These are the two DN + public key that + * correspond to the self-signed certificates cert-root-rsa.pem and + * cert-root-ec.pem. + * + * C code for hardcoded trust anchors can be generated with the "brssl" + * command-line tool (with the "ta" command). + * + * Below is the imported "Let's Encrypt" root certificate, as howsmyssl + * is depending on it: + * + * https://letsencrypt.org/certs/letsencryptauthorityx3.pem + * + */ + +static const unsigned char TA0_DN[] = { + 0x30, 0x4A, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x0D, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x1A, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, + 0x79, 0x20, 0x58, 0x33 +}; + +static const unsigned char TA0_RSA_N[] = { + 0x9C, 0xD3, 0x0C, 0xF0, 0x5A, 0xE5, 0x2E, 0x47, 0xB7, 0x72, 0x5D, 0x37, + 0x83, 0xB3, 0x68, 0x63, 0x30, 0xEA, 0xD7, 0x35, 0x26, 0x19, 0x25, 0xE1, + 0xBD, 0xBE, 0x35, 0xF1, 0x70, 0x92, 0x2F, 0xB7, 0xB8, 0x4B, 0x41, 0x05, + 0xAB, 0xA9, 0x9E, 0x35, 0x08, 0x58, 0xEC, 0xB1, 0x2A, 0xC4, 0x68, 0x87, + 0x0B, 0xA3, 0xE3, 0x75, 0xE4, 0xE6, 0xF3, 0xA7, 0x62, 0x71, 0xBA, 0x79, + 0x81, 0x60, 0x1F, 0xD7, 0x91, 0x9A, 0x9F, 0xF3, 0xD0, 0x78, 0x67, 0x71, + 0xC8, 0x69, 0x0E, 0x95, 0x91, 0xCF, 0xFE, 0xE6, 0x99, 0xE9, 0x60, 0x3C, + 0x48, 0xCC, 0x7E, 0xCA, 0x4D, 0x77, 0x12, 0x24, 0x9D, 0x47, 0x1B, 0x5A, + 0xEB, 0xB9, 0xEC, 0x1E, 0x37, 0x00, 0x1C, 0x9C, 0xAC, 0x7B, 0xA7, 0x05, + 0xEA, 0xCE, 0x4A, 0xEB, 0xBD, 0x41, 0xE5, 0x36, 0x98, 0xB9, 0xCB, 0xFD, + 0x6D, 0x3C, 0x96, 0x68, 0xDF, 0x23, 0x2A, 0x42, 0x90, 0x0C, 0x86, 0x74, + 0x67, 0xC8, 0x7F, 0xA5, 0x9A, 0xB8, 0x52, 0x61, 0x14, 0x13, 0x3F, 0x65, + 0xE9, 0x82, 0x87, 0xCB, 0xDB, 0xFA, 0x0E, 0x56, 0xF6, 0x86, 0x89, 0xF3, + 0x85, 0x3F, 0x97, 0x86, 0xAF, 0xB0, 0xDC, 0x1A, 0xEF, 0x6B, 0x0D, 0x95, + 0x16, 0x7D, 0xC4, 0x2B, 0xA0, 0x65, 0xB2, 0x99, 0x04, 0x36, 0x75, 0x80, + 0x6B, 0xAC, 0x4A, 0xF3, 0x1B, 0x90, 0x49, 0x78, 0x2F, 0xA2, 0x96, 0x4F, + 0x2A, 0x20, 0x25, 0x29, 0x04, 0xC6, 0x74, 0xC0, 0xD0, 0x31, 0xCD, 0x8F, + 0x31, 0x38, 0x95, 0x16, 0xBA, 0xA8, 0x33, 0xB8, 0x43, 0xF1, 0xB1, 0x1F, + 0xC3, 0x30, 0x7F, 0xA2, 0x79, 0x31, 0x13, 0x3D, 0x2D, 0x36, 0xF8, 0xE3, + 0xFC, 0xF2, 0x33, 0x6A, 0xB9, 0x39, 0x31, 0xC5, 0xAF, 0xC4, 0x8D, 0x0D, + 0x1D, 0x64, 0x16, 0x33, 0xAA, 0xFA, 0x84, 0x29, 0xB6, 0xD4, 0x0B, 0xC0, + 0xD8, 0x7D, 0xC3, 0x93 +}; + +static const unsigned char TA0_RSA_E[] = { + 0x01, 0x00, 0x01 +}; + +static const br_x509_trust_anchor TAs[1] = { + { + { (unsigned char *)TA0_DN, sizeof TA0_DN }, + BR_X509_TA_CA, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N, + (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E, + } } + } + } +}; + +#define TAs_NUM 1 + +/* + * Buffer to store a record + BearSSL state + * We use MONO mode to save 16k of RAM. + * This could be even smaller by using max_fragment_len, but + * the howsmyssl.com server doesn't seem to support it. + */ +static unsigned char bearssl_buffer[BR_SSL_BUFSIZE_MONO]; + +static br_ssl_client_context sc; +static br_x509_minimal_context xc; +static br_sslio_context ioc; + +void http_get_task(void *pvParameters) +{ + int successes = 0, failures = 0; + int provisional_time = 0; + + while (1) { + /* + * Wait until we can resolve the DNS for the server, as an indication + * our network is probably working... + */ + const struct addrinfo hints = { + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + }; + struct addrinfo *res = NULL; + int dns_err = 0; + do { + if (res) + freeaddrinfo(res); + vTaskDelay(1000 / portTICK_PERIOD_MS); + dns_err = getaddrinfo(WEB_SERVER, WEB_PORT, &hints, &res); + } while(dns_err != 0 || res == NULL); + + int fd = socket(res->ai_family, res->ai_socktype, 0); + if (fd < 0) { + freeaddrinfo(res); + printf("socket failed\n"); + failures++; + continue; + } + + printf("Initializing BearSSL... "); + br_ssl_client_init_full(&sc, &xc, TAs, TAs_NUM); + + /* + * Set the I/O buffer to the provided array. We allocated a + * buffer large enough for full-duplex behaviour with all + * allowed sizes of SSL records, hence we set the last argument + * to 1 (which means "split the buffer into separate input and + * output areas"). + */ + br_ssl_engine_set_buffer(&sc.eng, bearssl_buffer, sizeof bearssl_buffer, 0); + + /* + * Inject some entropy from the ESP hardware RNG + * This is necessary because we don't support any of the BearSSL methods + */ + for (int i = 0; i < 10; i++) { + int rand = hwrand(); + br_ssl_engine_inject_entropy(&sc.eng, &rand, 4); + } + + /* + * Reset the client context, for a new handshake. We provide the + * target host name: it will be used for the SNI extension. The + * last parameter is 0: we are not trying to resume a session. + */ + br_ssl_client_reset(&sc, WEB_SERVER, 0); + + /* + * Initialise the simplified I/O wrapper context, to use our + * SSL client context, and the two callbacks for socket I/O. + */ + br_sslio_init(&ioc, &sc.eng, sock_read, &fd, sock_write, &fd); + printf("done.\r\n"); + + /* FIXME: set date & time using epoch time precompiler flag for now */ + provisional_time = CONFIG_EPOCH_TIME + (xTaskGetTickCount()/configTICK_RATE_HZ); + xc.days = (provisional_time / CLOCK_SECONDS_PER_DAY) + 719528; + xc.seconds = provisional_time % CLOCK_SECONDS_PER_DAY; + printf("Time: %02i:%02i\r\n", + (int)(xc.seconds / CLOCK_SECONDS_PER_HOUR), + (int)((xc.seconds % CLOCK_SECONDS_PER_HOUR)/CLOCK_SECONDS_PER_MINUTE) + ); + + if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) + { + close(fd); + freeaddrinfo(res); + printf("connect failed\n"); + failures++; + continue; + } + printf("Connected\r\n"); + + /* + * Note that while the context has, at that point, already + * assembled the ClientHello to send, nothing happened on the + * network yet. Real I/O will occur only with the next call. + * + * We write our simple HTTP request. We test the call + * for an error (-1), but this is not strictly necessary, since + * the error state "sticks": if the context fails for any reason + * (e.g. bad server certificate), then it will remain in failed + * state and all subsequent calls will return -1 as well. + */ + if (br_sslio_write_all(&ioc, GET_REQUEST, strlen(GET_REQUEST)) != BR_ERR_OK) { + close(fd); + freeaddrinfo(res); + printf("br_sslio_write_all failed: %d\r\n", br_ssl_engine_last_error(&sc.eng)); + failures++; + continue; + } + + /* + * SSL is a buffered protocol: we make sure that all our request + * bytes are sent onto the wire. + */ + br_sslio_flush(&ioc); + + /* + * Read and print the server response + */ + for (;;) + { + int rlen; + unsigned char buf[128]; + + bzero(buf, 128); + // Leave the final byte for zero termination + rlen = br_sslio_read(&ioc, buf, sizeof(buf) - 1); + + if (rlen < 0) { + break; + } + if (rlen > 0) { + printf("%s", buf); + } + } + + /* + * If reading the response failed for any reason, we detect it here + */ + if (br_ssl_engine_last_error(&sc.eng) != BR_ERR_OK) { + close(fd); + freeaddrinfo(res); + printf("failure, error = %d\r\n", br_ssl_engine_last_error(&sc.eng)); + failures++; + continue; + } + + printf("\r\n\r\nfree heap pre = %u\r\n", xPortGetFreeHeapSize()); + + /* + * Close the connection and start over after a delay + */ + close(fd); + freeaddrinfo(res); + + printf("free heap post = %u\r\n", xPortGetFreeHeapSize()); + + successes++; + printf("successes = %d failures = %d\r\n", successes, failures); + for(int countdown = 10; countdown >= 0; countdown--) { + printf("%d...\n", countdown); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + printf("Starting again!\r\n\r\n"); + } +} + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + struct sdk_station_config config = { + .ssid = WIFI_SSID, + .password = WIFI_PASS, + }; + + /* required to call wifi_set_opmode before station_set_config */ + sdk_wifi_set_opmode(STATION_MODE); + sdk_wifi_station_set_config(&config); + + xTaskCreate(&http_get_task, "get_task", 2048, NULL, 2, NULL); +} diff --git a/examples/http_server/Makefile b/examples/http_server/Makefile new file mode 100644 index 0000000..713083a --- /dev/null +++ b/examples/http_server/Makefile @@ -0,0 +1,14 @@ +PROGRAM=http_server + +EXTRA_CFLAGS=-DLWIP_HTTPD_CGI=1 -DLWIP_HTTPD_SSI=1 -I./fsdata + +#Enable debugging +#EXTRA_CFLAGS+=-DLWIP_DEBUG=1 -DHTTPD_DEBUG=LWIP_DBG_ON + +EXTRA_COMPONENTS=extras/mbedtls extras/httpd + +include ../../common.mk + +html: + @echo "Generating fsdata.." + cd fsdata && ./makefsdata diff --git a/examples/http_server/fsdata/fs/404.html b/examples/http_server/fsdata/fs/404.html new file mode 100644 index 0000000..0a03490 --- /dev/null +++ b/examples/http_server/fsdata/fs/404.html @@ -0,0 +1,24 @@ + + + + + + + + + HTTP Server + + + + +
+

404 - Page not found

+
Sorry, the page you are requesting was not found on this server.
+
+ + + diff --git a/examples/http_server/fsdata/fs/about.html b/examples/http_server/fsdata/fs/about.html new file mode 100644 index 0000000..46eb050 --- /dev/null +++ b/examples/http_server/fsdata/fs/about.html @@ -0,0 +1,25 @@ + + + + + + + + + HTTP Server + + + + +
+

About

+

This server is based on httpd from LwIP.

+

To enable debugging compile with flags -DLWIP_DEBUG=1 -DHTTPD_DEBUG=LWIP_DBG_ON.

+

For more info see HTTP Server documentation.

+
+ + diff --git a/examples/http_server/fsdata/fs/css/siimple.min.css b/examples/http_server/fsdata/fs/css/siimple.min.css new file mode 100644 index 0000000..a52f026 --- /dev/null +++ b/examples/http_server/fsdata/fs/css/siimple.min.css @@ -0,0 +1,8 @@ +/** + * siimple - Minimal CSS framework for flat and clean designs. + * @version v1.3.7 + * @link https://siimple.juanes.xyz/ + * @license MIT + */ + +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,300);ol,ol li,p,ul,ul li{line-height:28px}.alert,pre{width:calc(100% - 30px)}.alert,.btn{border-radius:5px}.heart:after{content:"\2764";color:#f45660}body,h1,h2,h3,h4,h5,h6{color:#526475}body{margin:0;padding:0;font-family:'Open Sans';font-size:16px;font-weight:300;background-color:#fff}.alert a,a{text-decoration:none;font-weight:400}blockquote{border-left:4px solid #6a7e95;padding:5px 5px 5px 20px}a{color:#09a0f6;transition:all .3s}a:hover{text-decoration:underline;cursor:pointer}p{margin-bottom:20px;margin-top:0;display:block}ol,ul{margin-bottom:16px;margin-top:0}.alert,h1,h2,h3,h4,h5,h6{font-weight:300;margin-top:0;margin-bottom:20px;display:block}small{color:#6a7e95;font-size:14px}h1{font-size:36px;line-height:50px}h2{font-size:32px;line-height:46px}h3{font-size:28px;line-height:42px}h4{font-size:24px;line-height:38px}h5{font-size:20px;line-height:34px}.alert,.btn,h6{font-size:16px}h6{line-height:30px}.alert{text-align:left;border-width:1px;border-style:solid;background-color:#E1F5FE;color:#03A9F4;border-color:#03A9F4;padding:16px 14px;padding:16px 14px;padding:16px 14px}.btn,.btn-outline{font-family:'Open Sans';font-weight:300;display:inline-block;transition:all .3s;-webkit-touch-callout:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;text-align:center;cursor:pointer;margin:5px 5px 20px}.alert-error{color:#D32F2F;background-color:#FFEBEE;border-color:#F44336}.alert-warning{background-color:#FFF8E1;color:#FF8F00;border-color:#FFC107}.alert-done{background-color:#E8F5E9;color:#388E3C;border-color:#4CAF50}.btn{text-decoration:none!important;line-height:28px;color:#fff;background-color:#09a0f6;border:0;padding:5px 25px}.btn:hover{text-decoration:none;opacity:.8}.btn-small{font-size:14px!important;line-height:20px!important;padding:4px 15px!important}.btn-big{font-size:22px!important;line-height:34px!important;padding:8px 30px!important}.btn-outline,pre{line-height:28px}.btn-outline{font-size:16px;text-decoration:none!important;border-radius:5px;color:#09a0f6;background-color:transparent;border:1px solid #09a0f6;padding:5px 25px}.btn-outline:hover{text-decoration:none;color:#fff;background-color:#09a0f6}code,pre{font-family:'Open Sans';font-size:16px;font-weight:300;border-radius:5px;background-color:#f1f5fa}code{color:#09a0f6;padding-left:6px;padding-right:6px}pre{display:block;padding:14px;margin-bottom:20px;color:#526475;overflow-x:auto}.form-input[disabled],.form-input[type=text],.form-input[type=password],.form-input[type=number],.form-input[type=email],.form-input[type=date]{color:#526475;padding:10px;outline:0;box-sizing:border-box;margin:0 5px 20px;font-family:'Open Sans';font-size:16px;font-weight:300;display:inline-block;transition:all .3s;height:40px}.form-input[type=text],.form-input[type=password],.form-input[type=number],.form-input[type=email]{width:100%;border:1px solid #d1e1e8;border-radius:5px;line-height:40px}.form-input[type=text]:focus,.form-input[type=password]:focus,.form-input[type=number]:focus,.form-input[type=email]:focus{border:1px solid #09a0f6}.form-input[type=date]{border:1px solid #d1e1e8;border-radius:5px;width:auto!important}.form-input[type=date]:focus{border:1px solid #09a0f6}.form-input[disabled]{width:100%;border:1px solid #d1e1e8;border-radius:5px;cursor:not-allowed;background-color:#d1e1e8}.form-input[disabled]:focus{border:1px solid #09a0f6}.form-input[type=submit],.form-input[type=button]{font-family:'Open Sans';font-size:16px;font-weight:300;-webkit-touch-callout:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;text-align:center;text-decoration:none!important;line-height:28px;display:inline-block;cursor:pointer;border-radius:5px;transition:all .3s;color:#fff;background-color:#09a0f6;border:0;margin:5px 5px 20px;padding:5px 25px}.form-select,.form-textarea{font-family:'Open Sans';font-size:16px;display:inline-block;width:100%;transition:all .3s;outline:0;box-sizing:border-box;margin:0 5px 20px;font-weight:300;color:#526475}.form-input[type=submit]:hover,.form-input[type=button]:hover{text-decoration:none;opacity:.8}.form-select{padding:6px 10px 10px;border:1px solid #d1e1e8;border-radius:5px;height:40px;background-color:#fff}.form-select:focus{border:1px solid #09a0f6}.form-textarea{padding:10px;border:1px solid #d1e1e8;border-radius:5px;resize:vertical}.form-textarea:focus{border:1px solid #09a0f6}.form-auto{width:auto!important}.grid{display:block;width:960px;margin-left:auto;margin-right:auto;min-height:40px}@media (max-width:960px){.grid{width:94%}}.grid-fluid,.row{width:100%}.row{display:inline-block;margin-left:0;margin-right:0}.row:after{content:" ";clear:both;display:table;line-height:0}.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-7,.col-8,.col-9{display:inline-block;vertical-align:top;float:left;padding:1%}.col-1{width:6.33%}.col-2{width:14.66%}.col-3{width:22.99%}.col-4{width:31.33%}.col-5{width:39.66%}.col-6{width:47.99%;display:inline-block;vertical-align:top;float:left;padding:1%}.col-7{width:56.33%}.col-8{width:64.66%}.col-9{width:72.99%}.col-10{width:81.33%}.col-11{width:89.66%}.col-12{width:97.99%}@media (max-width:400px){.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9{width:98%}}.table{display:table;width:100%;border-width:0;border-collapse:collapse;font-weight:300;color:#526475;margin-top:0;margin-bottom:20px}.table thead tr td{font-weight:400;border-bottom:2px solid #d1e1e8;background-color:#f6f8fa}.table tr td{border-bottom:1px solid #d1e1e8;padding-top:10px;padding-bottom:10px;padding-left:10px} \ No newline at end of file diff --git a/examples/http_server/fsdata/fs/css/style.css b/examples/http_server/fsdata/fs/css/style.css new file mode 100644 index 0000000..b5b27d6 --- /dev/null +++ b/examples/http_server/fsdata/fs/css/style.css @@ -0,0 +1,73 @@ +ul.navbar { + list-style-type: none; + margin-bottom: 32px; + padding: 0; + overflow: hidden; + background-color: #333; +} +ul.navbar li { + float: left; +} +ul.navbar li a { + display: block; + color: white; + text-align: center; + padding: 14px 16px; + text-decoration: none; +} +ul.navbar li a:hover:not(.active) { + background-color: #111; +} +ul.navbar li a.active { + background-color: #09a0f6; +} +@media screen and (max-width: 600px){ + ul.navbar li.right, + ul.navbar li {float: none;} +} +.onoffswitch { + position: relative; width: 90px; + -webkit-user-select:none; -moz-user-select:none; -ms-user-select: none; +} +.onoffswitch-checkbox { + display: none; +} +.onoffswitch-label { + display: block; overflow: hidden; cursor: pointer; + border: 2px solid #03A9F4; border-radius: 20px; +} +.onoffswitch-inner { + display: block; width: 200%; margin-left: -100%; + transition: margin 0.3s ease-in 0s; +} +.onoffswitch-inner:before, .onoffswitch-inner:after { + display: block; float: left; width: 50%; height: 30px; padding: 0; line-height: 30px; + font-size: 14px; color: white; font-family: Trebuchet, Arial, sans-serif; font-weight: bold; + box-sizing: border-box; +} +.onoffswitch-inner:before { + content: "ON"; + text-align: left; + padding-left: 14px; + background-color: #E1F5FE; color: #03A9F4; +} +.onoffswitch-inner:after { + content: "OFF"; + padding-right: 14px; + background-color: #FFFFFF; color: #999999; + text-align: right; +} +.onoffswitch-switch { + display: block; width: 18px; margin: 6px; + background: #FFFFFF; + position: absolute; top: 0; bottom: 0; + right: 56px; + border: 2px solid #03A9F4; border-radius: 20px; + transition: all 0.3s ease-in 0s; +} +.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner { + margin-left: 0; +} +.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch { + right: 0px; +} \ No newline at end of file diff --git a/examples/http_server/fsdata/fs/img/favicon.png b/examples/http_server/fsdata/fs/img/favicon.png new file mode 100644 index 0000000..126f24b Binary files /dev/null and b/examples/http_server/fsdata/fs/img/favicon.png differ diff --git a/examples/http_server/fsdata/fs/index.ssi b/examples/http_server/fsdata/fs/index.ssi new file mode 100644 index 0000000..3a21206 --- /dev/null +++ b/examples/http_server/fsdata/fs/index.ssi @@ -0,0 +1,75 @@ + + + + + + + + + HTTP Server + + + + +
+

ESP8266 HTTP Server

+ +
HTTP Server is up and running.
+ +

This is an example HTTP server with CGI and SSI support. The switch below will allow you to test CGI handler and turn + the blue LED on or off.

+ +
+
+ + +
+
+ +

Server Status

+ + + + + + + + + + + + + +
Uptime: seconds
Free heap: bytes
LED state:
+ +

How it works

+

Each time the server detects a tag of the form <!--#name--> in a .shtml, .ssi or .shtm file + where name appears as one of the tags supplied to http_set_ssi_handler in the pcConfigSSITags array, + an insert string is appended after the tag string in file and sent back to the client.

+

A CGI handler function is called each time the server is asked for a file + whose name was previously registered as a CGI function using a call to http_set_cgi_handler. + This function allows you to access the parameters provided along with the URI.

+
+ + + + diff --git a/examples/http_server/fsdata/fs/js/smoothie_min.js b/examples/http_server/fsdata/fs/js/smoothie_min.js new file mode 100644 index 0000000..87ce47d --- /dev/null +++ b/examples/http_server/fsdata/fs/js/smoothie_min.js @@ -0,0 +1,15 @@ +// MIT License: +// +// Copyright (c) 2010-2013, Joe Walnes +// 2013-2014, Drew Noakes +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +;(function(exports){var Util={extend:function(){arguments[0]=arguments[0]||{};for(var i=1;ithis.maxValue){this.maxValue=value}if(value=0&&this.data[i][0]>timestamp){i-=1}if(i===-1){this.data.splice(0,0,[timestamp,value])}else if(this.data.length>0&&this.data[i][0]===timestamp){if(sumRepeatedTimeStampValues){this.data[i][1]+=value;value=this.data[i][1]}else{this.data[i][1]=value}}else if(i=maxDataSetLength&&this.data[removeCount+1][0]0){timeSeries.resetBoundsTimerId=setInterval(function(){timeSeries.resetBounds()},timeSeries.options.resetBoundsInterval)}};SmoothieChart.prototype.removeTimeSeries=function(timeSeries){var numSeries=this.seriesSet.length;for(var i=0;i0.1||Math.abs(minValueDiff)>0.1;this.currentValueRange+=chartOptions.scaleSmoothing*valueRangeDiff;this.currentVisMinValue+=chartOptions.scaleSmoothing*minValueDiff}this.valueRange={min:chartMinValue,max:chartMaxValue}};SmoothieChart.prototype.render=function(canvas,time){var nowMillis=new Date().getTime();if(!this.isAnimatingScale){var maxIdleMillis=Math.min(1000/6,this.options.millisPerPixel);if(nowMillis-this.lastRenderTimeMillis0){context.beginPath();for(var t=time-(time%chartOptions.grid.millisPerLine);t>=oldestValidTime;t-=chartOptions.grid.millisPerLine){var gx=timeToXPixel(t);if(chartOptions.grid.sharpLines){gx-=0.5}context.moveTo(gx,0);context.lineTo(gx,dimensions.height)}context.stroke();context.closePath()}for(var v=1;v1){if(seriesOptions.fillStyle){context.lineTo(dimensions.width+seriesOptions.lineWidth+1,lastY);context.lineTo(dimensions.width+seriesOptions.lineWidth+1,dimensions.height+seriesOptions.lineWidth+1);context.lineTo(firstX,dimensions.height+seriesOptions.lineWidth);context.fillStyle=seriesOptions.fillStyle;context.fill()}if(seriesOptions.strokeStyle&&seriesOptions.strokeStyle!=='none'){context.stroke()}context.closePath()}context.restore()}if(!chartOptions.labels.disabled&&!isNaN(this.valueRange.min)&&!isNaN(this.valueRange.max)){var maxValueString=chartOptions.yMaxFormatter(this.valueRange.max,chartOptions.labels.precision),minValueString=chartOptions.yMinFormatter(this.valueRange.min,chartOptions.labels.precision),maxLabelPos=chartOptions.scrollBackwards?0:dimensions.width-context.measureText(maxValueString).width-2,minLabelPos=chartOptions.scrollBackwards?0:dimensions.width-context.measureText(minValueString).width-2;context.fillStyle=chartOptions.labels.fillStyle;context.fillText(maxValueString,maxLabelPos,chartOptions.labels.fontSize);context.fillText(minValueString,minLabelPos,dimensions.height-2)}if(chartOptions.timestampFormatter&&chartOptions.grid.millisPerLine>0){var textUntilX=chartOptions.scrollBackwards?context.measureText(minValueString).width:dimensions.width-context.measureText(minValueString).width+4;for(var t=time-(time%chartOptions.grid.millisPerLine);t>=oldestValidTime;t-=chartOptions.grid.millisPerLine){var gx=timeToXPixel(t);if((!chartOptions.scrollBackwards&&gxtextUntilX)){var tx=new Date(t),ts=chartOptions.timestampFormatter(tx),tsWidth=context.measureText(ts).width;textUntilX=chartOptions.scrollBackwards?gx+tsWidth+2:gx-tsWidth-2;context.fillStyle=chartOptions.labels.fillStyle;if(chartOptions.scrollBackwards){context.fillText(ts,gx,dimensions.height-2)}else{context.fillText(ts,gx-tsWidth,dimensions.height-2)}}}}context.restore();};SmoothieChart.timeFormatter=function(date){function pad2(number){return(number<10?'0':'')+number}return pad2(date.getHours())+':'+pad2(date.getMinutes())+':'+pad2(date.getSeconds())};exports.TimeSeries=TimeSeries;exports.SmoothieChart=SmoothieChart})(typeof exports==='undefined'?this:exports); \ No newline at end of file diff --git a/examples/http_server/fsdata/fs/websockets.html b/examples/http_server/fsdata/fs/websockets.html new file mode 100644 index 0000000..25b02c0 --- /dev/null +++ b/examples/http_server/fsdata/fs/websockets.html @@ -0,0 +1,130 @@ + + + + + + + + + HTTP Server + + + + +
+

WebSockets Demo

+
Loading..
+

This page is similar to the home page but uses WebSockets for real-time updates.

+
+ +

+

LED Control

+
+ + +
+
+ +

Server Status

+ + + + + + + + + + + + + +
Uptime:
Free heap:
LED state:
+ +

How it works

+

This demo uses 2 WebScokets. Status parameters are streamed by the server in JSON format every 2 seconds. + A websocket_task is created each time a specific URI is requested.

+

ADC values are being continuously polled by the client (i.e. your browser). + Each time a WebSocket frame is received on the server side, websocket_cb function is being called.

+
+ + + + + diff --git a/examples/http_server/fsdata/fsdata.c b/examples/http_server/fsdata/fsdata.c new file mode 100644 index 0000000..ac0b569 --- /dev/null +++ b/examples/http_server/fsdata/fsdata.c @@ -0,0 +1,3240 @@ +#include "httpd/fsdata.h" + +static const unsigned char data_index_ssi[] = { + /* /index.ssi */ + 0x2F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E, 0x73, 0x73, 0x69, 0, + 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x20, 0x4F, 0x4B, 0x0D, 0x0A, 0x6C, 0x77, 0x49, + 0x50, 0x2F, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x20, 0x28, 0x68, + 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x61, 0x76, 0x61, + 0x6E, 0x6E, 0x61, 0x68, 0x2E, 0x6E, 0x6F, 0x6E, 0x67, 0x6E, + 0x75, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x70, 0x72, 0x6F, 0x6A, + 0x65, 0x63, 0x74, 0x73, 0x2F, 0x6C, 0x77, 0x69, 0x70, 0x29, + 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, + 0x74, 0x79, 0x70, 0x65, 0x3A, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x2F, 0x68, 0x74, 0x6D, 0x6C, 0x0D, 0x0A, 0x0D, 0x0A, 0x3C, + 0x21, 0x44, 0x4F, 0x43, 0x54, 0x59, 0x50, 0x45, 0x20, 0x68, + 0x74, 0x6D, 0x6C, 0x3E, 0x0A, 0x3C, 0x68, 0x74, 0x6D, 0x6C, + 0x3E, 0x0A, 0x09, 0x3C, 0x68, 0x65, 0x61, 0x64, 0x3E, 0x0A, + 0x09, 0x09, 0x3C, 0x6D, 0x65, 0x74, 0x61, 0x20, 0x63, 0x68, + 0x61, 0x72, 0x73, 0x65, 0x74, 0x3D, 0x22, 0x75, 0x74, 0x66, + 0x2D, 0x38, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x6D, 0x65, + 0x74, 0x61, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x76, + 0x69, 0x65, 0x77, 0x70, 0x6F, 0x72, 0x74, 0x22, 0x20, 0x63, + 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x3D, 0x22, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x3D, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x2D, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2C, 0x20, 0x75, 0x73, + 0x65, 0x72, 0x2D, 0x73, 0x63, 0x61, 0x6C, 0x61, 0x62, 0x6C, + 0x65, 0x3D, 0x6E, 0x6F, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x3C, + 0x6C, 0x69, 0x6E, 0x6B, 0x20, 0x72, 0x65, 0x6C, 0x3D, 0x22, + 0x73, 0x74, 0x79, 0x6C, 0x65, 0x73, 0x68, 0x65, 0x65, 0x74, + 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x74, 0x65, + 0x78, 0x74, 0x2F, 0x63, 0x73, 0x73, 0x22, 0x20, 0x68, 0x72, + 0x65, 0x66, 0x3D, 0x22, 0x63, 0x73, 0x73, 0x2F, 0x73, 0x69, + 0x69, 0x6D, 0x70, 0x6C, 0x65, 0x2E, 0x6D, 0x69, 0x6E, 0x2E, + 0x63, 0x73, 0x73, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x6C, + 0x69, 0x6E, 0x6B, 0x20, 0x72, 0x65, 0x6C, 0x3D, 0x22, 0x73, + 0x74, 0x79, 0x6C, 0x65, 0x73, 0x68, 0x65, 0x65, 0x74, 0x22, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x74, 0x65, 0x78, + 0x74, 0x2F, 0x63, 0x73, 0x73, 0x22, 0x20, 0x68, 0x72, 0x65, + 0x66, 0x3D, 0x22, 0x63, 0x73, 0x73, 0x2F, 0x73, 0x74, 0x79, + 0x6C, 0x65, 0x2E, 0x63, 0x73, 0x73, 0x22, 0x3E, 0x0A, 0x09, + 0x09, 0x3C, 0x6C, 0x69, 0x6E, 0x6B, 0x20, 0x72, 0x65, 0x6C, + 0x3D, 0x22, 0x73, 0x68, 0x6F, 0x72, 0x74, 0x63, 0x75, 0x74, + 0x20, 0x69, 0x63, 0x6F, 0x6E, 0x22, 0x20, 0x68, 0x72, 0x65, + 0x66, 0x3D, 0x22, 0x69, 0x6D, 0x67, 0x2F, 0x66, 0x61, 0x76, + 0x69, 0x63, 0x6F, 0x6E, 0x2E, 0x70, 0x6E, 0x67, 0x22, 0x3E, + 0x0A, 0x09, 0x09, 0x3C, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x3E, + 0x48, 0x54, 0x54, 0x50, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x3C, 0x2F, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x3E, 0x0A, + 0x09, 0x3C, 0x2F, 0x68, 0x65, 0x61, 0x64, 0x3E, 0x0A, 0x09, + 0x3C, 0x62, 0x6F, 0x64, 0x79, 0x3E, 0x0A, 0x09, 0x09, 0x3C, + 0x75, 0x6C, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, + 0x6E, 0x61, 0x76, 0x62, 0x61, 0x72, 0x22, 0x3E, 0x0A, 0x09, + 0x09, 0x09, 0x3C, 0x6C, 0x69, 0x3E, 0x3C, 0x61, 0x20, 0x63, + 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x22, 0x20, 0x68, 0x72, 0x65, 0x66, 0x3D, 0x22, + 0x2F, 0x22, 0x3E, 0x48, 0x6F, 0x6D, 0x65, 0x3C, 0x2F, 0x61, + 0x3E, 0x3C, 0x2F, 0x6C, 0x69, 0x3E, 0x0A, 0x09, 0x09, 0x09, + 0x3C, 0x6C, 0x69, 0x3E, 0x3C, 0x61, 0x20, 0x68, 0x72, 0x65, + 0x66, 0x3D, 0x22, 0x77, 0x65, 0x62, 0x73, 0x6F, 0x63, 0x6B, + 0x65, 0x74, 0x73, 0x22, 0x3E, 0x57, 0x65, 0x62, 0x53, 0x6F, + 0x63, 0x6B, 0x65, 0x74, 0x73, 0x3C, 0x2F, 0x61, 0x3E, 0x3C, + 0x2F, 0x6C, 0x69, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x6C, + 0x69, 0x3E, 0x3C, 0x61, 0x20, 0x68, 0x72, 0x65, 0x66, 0x3D, + 0x22, 0x61, 0x62, 0x6F, 0x75, 0x74, 0x22, 0x3E, 0x41, 0x62, + 0x6F, 0x75, 0x74, 0x3C, 0x2F, 0x61, 0x3E, 0x3C, 0x2F, 0x6C, + 0x69, 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x2F, 0x75, 0x6C, 0x3E, + 0x0A, 0x0A, 0x09, 0x09, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, + 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x67, 0x72, 0x69, 0x64, + 0x20, 0x6D, 0x61, 0x69, 0x6E, 0x22, 0x3E, 0x0A, 0x09, 0x09, + 0x09, 0x3C, 0x68, 0x31, 0x3E, 0x45, 0x53, 0x50, 0x38, 0x32, + 0x36, 0x36, 0x20, 0x48, 0x54, 0x54, 0x50, 0x20, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x3C, 0x2F, 0x68, 0x31, 0x3E, 0x0A, + 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, + 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x61, 0x6C, 0x65, 0x72, + 0x74, 0x20, 0x61, 0x6C, 0x65, 0x72, 0x74, 0x2D, 0x64, 0x6F, + 0x6E, 0x65, 0x22, 0x3E, 0x48, 0x54, 0x54, 0x50, 0x20, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x75, + 0x70, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x72, 0x75, 0x6E, 0x6E, + 0x69, 0x6E, 0x67, 0x2E, 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, + 0x0A, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x70, 0x3E, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6E, 0x20, 0x65, + 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x20, 0x48, 0x54, 0x54, + 0x50, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x77, + 0x69, 0x74, 0x68, 0x20, 0x43, 0x47, 0x49, 0x20, 0x61, 0x6E, + 0x64, 0x20, 0x53, 0x53, 0x49, 0x20, 0x73, 0x75, 0x70, 0x70, + 0x6F, 0x72, 0x74, 0x2E, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, + 0x77, 0x69, 0x74, 0x63, 0x68, 0x20, 0x62, 0x65, 0x6C, 0x6F, + 0x77, 0x20, 0x77, 0x69, 0x6C, 0x6C, 0x20, 0x61, 0x6C, 0x6C, + 0x6F, 0x77, 0x20, 0x79, 0x6F, 0x75, 0x20, 0x74, 0x6F, 0x20, + 0x74, 0x65, 0x73, 0x74, 0x20, 0x43, 0x47, 0x49, 0x20, 0x68, + 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x20, 0x61, 0x6E, 0x64, + 0x20, 0x74, 0x75, 0x72, 0x6E, 0x0A, 0x09, 0x09, 0x09, 0x74, + 0x68, 0x65, 0x20, 0x62, 0x6C, 0x75, 0x65, 0x20, 0x4C, 0x45, + 0x44, 0x20, 0x6F, 0x6E, 0x20, 0x6F, 0x72, 0x20, 0x6F, 0x66, + 0x66, 0x2E, 0x3C, 0x2F, 0x70, 0x3E, 0x0A, 0x0A, 0x09, 0x09, + 0x09, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6C, 0x61, 0x73, + 0x73, 0x3D, 0x22, 0x63, 0x6F, 0x76, 0x65, 0x72, 0x22, 0x20, + 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3D, 0x22, 0x63, 0x65, 0x6E, + 0x74, 0x65, 0x72, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, + 0x3D, 0x22, 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, + 0x74, 0x63, 0x68, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x3C, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x3D, 0x22, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x62, + 0x6F, 0x78, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, + 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, + 0x68, 0x22, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, + 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, + 0x68, 0x2D, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x62, 0x6F, 0x78, + 0x22, 0x20, 0x69, 0x64, 0x3D, 0x22, 0x6C, 0x65, 0x64, 0x2D, + 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x22, 0x20, 0x6F, 0x6E, + 0x63, 0x6C, 0x69, 0x63, 0x6B, 0x3D, 0x22, 0x67, 0x70, 0x69, + 0x6F, 0x28, 0x29, 0x3B, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x3C, 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x20, 0x63, + 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x6F, 0x6E, 0x6F, 0x66, + 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, 0x6C, 0x61, + 0x62, 0x65, 0x6C, 0x22, 0x20, 0x66, 0x6F, 0x72, 0x3D, 0x22, + 0x6C, 0x65, 0x64, 0x2D, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, + 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3C, + 0x73, 0x70, 0x61, 0x6E, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, + 0x3D, 0x22, 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, + 0x74, 0x63, 0x68, 0x2D, 0x69, 0x6E, 0x6E, 0x65, 0x72, 0x22, + 0x3E, 0x3C, 0x2F, 0x73, 0x70, 0x61, 0x6E, 0x3E, 0x0A, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x73, 0x70, 0x61, 0x6E, + 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x6F, 0x6E, + 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, + 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x22, 0x3E, 0x3C, 0x2F, + 0x73, 0x70, 0x61, 0x6E, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x3C, 0x2F, 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x3E, 0x0A, + 0x09, 0x09, 0x09, 0x09, 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, + 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, + 0x0A, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x68, 0x31, 0x3E, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x3C, 0x2F, 0x68, 0x31, 0x3E, 0x0A, 0x09, 0x09, + 0x09, 0x3C, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x20, 0x63, 0x6C, + 0x61, 0x73, 0x73, 0x3D, 0x22, 0x74, 0x61, 0x62, 0x6C, 0x65, + 0x20, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x2D, 0x73, 0x74, 0x72, + 0x69, 0x70, 0x65, 0x64, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x3C, 0x74, 0x72, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x3C, 0x74, 0x64, 0x3E, 0x3C, 0x62, 0x3E, 0x55, 0x70, + 0x74, 0x69, 0x6D, 0x65, 0x3A, 0x3C, 0x2F, 0x62, 0x3E, 0x3C, + 0x2F, 0x74, 0x64, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x3C, 0x74, 0x64, 0x3E, 0x3C, 0x21, 0x2D, 0x2D, 0x23, 0x75, + 0x70, 0x74, 0x69, 0x6D, 0x65, 0x2D, 0x2D, 0x3E, 0x20, 0x73, + 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x73, 0x3C, 0x2F, 0x74, 0x64, + 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x2F, 0x74, 0x72, + 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x74, 0x72, 0x3E, + 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x74, 0x64, 0x3E, + 0x3C, 0x62, 0x3E, 0x46, 0x72, 0x65, 0x65, 0x20, 0x68, 0x65, + 0x61, 0x70, 0x3A, 0x3C, 0x2F, 0x62, 0x3E, 0x3C, 0x2F, 0x74, + 0x64, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x74, + 0x64, 0x3E, 0x3C, 0x21, 0x2D, 0x2D, 0x23, 0x68, 0x65, 0x61, + 0x70, 0x2D, 0x2D, 0x3E, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x3C, 0x2F, 0x74, 0x64, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x3C, 0x2F, 0x74, 0x72, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x3C, 0x74, 0x72, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x3C, 0x74, 0x64, 0x3E, 0x3C, 0x62, 0x3E, 0x4C, 0x45, 0x44, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3A, 0x3C, 0x2F, 0x62, + 0x3E, 0x3C, 0x2F, 0x74, 0x64, 0x3E, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x3C, 0x74, 0x64, 0x20, 0x69, 0x64, 0x3D, 0x22, + 0x6C, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x3E, + 0x3C, 0x21, 0x2D, 0x2D, 0x23, 0x6C, 0x65, 0x64, 0x2D, 0x2D, + 0x3E, 0x3C, 0x2F, 0x74, 0x64, 0x3E, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x3C, 0x2F, 0x74, 0x72, 0x3E, 0x0A, 0x09, 0x09, 0x09, + 0x3C, 0x2F, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x3E, 0x0A, 0x0A, + 0x09, 0x09, 0x09, 0x3C, 0x68, 0x31, 0x3E, 0x48, 0x6F, 0x77, + 0x20, 0x69, 0x74, 0x20, 0x77, 0x6F, 0x72, 0x6B, 0x73, 0x3C, + 0x2F, 0x68, 0x31, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x70, + 0x3E, 0x20, 0x45, 0x61, 0x63, 0x68, 0x20, 0x74, 0x69, 0x6D, + 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x73, + 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x6F, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x66, 0x6F, 0x72, 0x6D, 0x20, 0x3C, + 0x63, 0x6F, 0x64, 0x65, 0x3E, 0x26, 0x6C, 0x74, 0x3B, 0x21, + 0x2D, 0x2D, 0x23, 0x6E, 0x61, 0x6D, 0x65, 0x2D, 0x2D, 0x26, + 0x67, 0x74, 0x3B, 0x3C, 0x2F, 0x63, 0x6F, 0x64, 0x65, 0x3E, + 0x20, 0x69, 0x6E, 0x20, 0x61, 0x20, 0x2E, 0x73, 0x68, 0x74, + 0x6D, 0x6C, 0x2C, 0x20, 0x2E, 0x73, 0x73, 0x69, 0x20, 0x6F, + 0x72, 0x20, 0x2E, 0x73, 0x68, 0x74, 0x6D, 0x20, 0x66, 0x69, + 0x6C, 0x65, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x77, 0x68, 0x65, + 0x72, 0x65, 0x20, 0x3C, 0x63, 0x6F, 0x64, 0x65, 0x3E, 0x6E, + 0x61, 0x6D, 0x65, 0x3C, 0x2F, 0x63, 0x6F, 0x64, 0x65, 0x3E, + 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x73, 0x20, 0x61, + 0x73, 0x20, 0x6F, 0x6E, 0x65, 0x20, 0x6F, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x74, 0x61, 0x67, 0x73, 0x20, 0x73, 0x75, + 0x70, 0x70, 0x6C, 0x69, 0x65, 0x64, 0x20, 0x74, 0x6F, 0x20, + 0x3C, 0x63, 0x6F, 0x64, 0x65, 0x3E, 0x68, 0x74, 0x74, 0x70, + 0x5F, 0x73, 0x65, 0x74, 0x5F, 0x73, 0x73, 0x69, 0x5F, 0x68, + 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x3C, 0x2F, 0x63, 0x6F, + 0x64, 0x65, 0x3E, 0x20, 0x69, 0x6E, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x3C, 0x63, 0x6F, 0x64, 0x65, 0x3E, 0x70, 0x63, 0x43, + 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x53, 0x53, 0x49, 0x54, 0x61, + 0x67, 0x73, 0x3C, 0x2F, 0x63, 0x6F, 0x64, 0x65, 0x3E, 0x20, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x2C, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x73, 0x65, 0x72, 0x74, + 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, + 0x20, 0x61, 0x70, 0x70, 0x65, 0x6E, 0x64, 0x65, 0x64, 0x20, + 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x74, 0x61, 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, + 0x20, 0x69, 0x6E, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x20, 0x61, + 0x6E, 0x64, 0x20, 0x73, 0x65, 0x6E, 0x74, 0x20, 0x62, 0x61, + 0x63, 0x6B, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x2E, 0x3C, 0x2F, 0x70, + 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x70, 0x3E, 0x41, 0x20, + 0x43, 0x47, 0x49, 0x20, 0x68, 0x61, 0x6E, 0x64, 0x6C, 0x65, + 0x72, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, + 0x20, 0x69, 0x73, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x65, 0x64, + 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x74, 0x69, 0x6D, 0x65, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x20, 0x69, 0x73, 0x20, 0x61, 0x73, 0x6B, 0x65, 0x64, + 0x20, 0x66, 0x6F, 0x72, 0x20, 0x61, 0x20, 0x66, 0x69, 0x6C, + 0x65, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x77, 0x68, 0x6F, 0x73, + 0x65, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x77, 0x61, 0x73, + 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6F, 0x75, 0x73, 0x6C, + 0x79, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x43, 0x47, + 0x49, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, + 0x20, 0x75, 0x73, 0x69, 0x6E, 0x67, 0x20, 0x61, 0x20, 0x63, + 0x61, 0x6C, 0x6C, 0x20, 0x74, 0x6F, 0x20, 0x3C, 0x63, 0x6F, + 0x64, 0x65, 0x3E, 0x68, 0x74, 0x74, 0x70, 0x5F, 0x73, 0x65, + 0x74, 0x5F, 0x63, 0x67, 0x69, 0x5F, 0x68, 0x61, 0x6E, 0x64, + 0x6C, 0x65, 0x72, 0x3C, 0x2F, 0x63, 0x6F, 0x64, 0x65, 0x3E, + 0x2E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, + 0x61, 0x6C, 0x6C, 0x6F, 0x77, 0x73, 0x20, 0x79, 0x6F, 0x75, + 0x20, 0x74, 0x6F, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6D, + 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x76, + 0x69, 0x64, 0x65, 0x64, 0x20, 0x61, 0x6C, 0x6F, 0x6E, 0x67, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x55, 0x52, 0x49, 0x2E, 0x3C, 0x2F, 0x70, 0x3E, 0x0A, 0x09, + 0x09, 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, 0x0A, 0x0A, 0x09, + 0x09, 0x3C, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3E, 0x0A, + 0x09, 0x09, 0x09, 0x77, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x2E, + 0x6F, 0x6E, 0x6C, 0x6F, 0x61, 0x64, 0x20, 0x3D, 0x20, 0x66, + 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x28, 0x29, + 0x20, 0x7B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, + 0x20, 0x6C, 0x73, 0x20, 0x3D, 0x20, 0x64, 0x6F, 0x63, 0x75, + 0x6D, 0x65, 0x6E, 0x74, 0x2E, 0x67, 0x65, 0x74, 0x45, 0x6C, + 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, + 0x27, 0x6C, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x27, + 0x29, 0x2E, 0x69, 0x6E, 0x6E, 0x65, 0x72, 0x48, 0x54, 0x4D, + 0x4C, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x6C, 0x73, 0x20, + 0x3D, 0x20, 0x6C, 0x73, 0x2E, 0x73, 0x70, 0x6C, 0x69, 0x74, + 0x28, 0x2F, 0x2D, 0x2D, 0x3E, 0x2F, 0x29, 0x2E, 0x70, 0x6F, + 0x70, 0x28, 0x29, 0x2E, 0x74, 0x72, 0x69, 0x6D, 0x28, 0x29, + 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x64, 0x6F, 0x63, 0x75, + 0x6D, 0x65, 0x6E, 0x74, 0x2E, 0x67, 0x65, 0x74, 0x45, 0x6C, + 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, + 0x27, 0x6C, 0x65, 0x64, 0x2D, 0x73, 0x77, 0x69, 0x74, 0x63, + 0x68, 0x27, 0x29, 0x2E, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x65, + 0x64, 0x20, 0x3D, 0x20, 0x28, 0x6C, 0x73, 0x20, 0x3D, 0x3D, + 0x20, 0x27, 0x4F, 0x6E, 0x27, 0x29, 0x3B, 0x0A, 0x09, 0x09, + 0x09, 0x7D, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6E, + 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x67, 0x70, 0x69, 0x6F, + 0x28, 0x29, 0x20, 0x7B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x69, + 0x66, 0x20, 0x28, 0x64, 0x6F, 0x63, 0x75, 0x6D, 0x65, 0x6E, + 0x74, 0x2E, 0x67, 0x65, 0x74, 0x45, 0x6C, 0x65, 0x6D, 0x65, + 0x6E, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x27, 0x6C, 0x65, + 0x64, 0x2D, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x27, 0x29, + 0x2E, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x65, 0x64, 0x29, 0x0A, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x77, 0x69, 0x6E, 0x64, 0x6F, + 0x77, 0x2E, 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, + 0x2E, 0x68, 0x72, 0x65, 0x66, 0x20, 0x3D, 0x20, 0x27, 0x67, + 0x70, 0x69, 0x6F, 0x3F, 0x6F, 0x66, 0x66, 0x3D, 0x32, 0x27, + 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x65, 0x6C, 0x73, 0x65, + 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x77, 0x69, 0x6E, 0x64, + 0x6F, 0x77, 0x2E, 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, + 0x6E, 0x2E, 0x68, 0x72, 0x65, 0x66, 0x20, 0x3D, 0x20, 0x27, + 0x67, 0x70, 0x69, 0x6F, 0x3F, 0x6F, 0x6E, 0x3D, 0x32, 0x27, + 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x7D, 0x3B, 0x0A, 0x09, 0x09, + 0x3C, 0x2F, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3E, 0x0A, + 0x09, 0x3C, 0x2F, 0x62, 0x6F, 0x64, 0x79, 0x3E, 0x0A, 0x3C, + 0x2F, 0x68, 0x74, 0x6D, 0x6C, 0x3E, 0x0A, }; + +static const unsigned char data_404_html[] = { + /* /404.html */ + 0x2F, 0x34, 0x30, 0x34, 0x2E, 0x68, 0x74, 0x6D, 0x6C, 0, + 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30, 0x20, 0x34, + 0x30, 0x34, 0x20, 0x46, 0x69, 0x6C, 0x65, 0x20, 0x6E, 0x6F, + 0x74, 0x20, 0x66, 0x6F, 0x75, 0x6E, 0x64, 0x0D, 0x0A, 0x6C, + 0x77, 0x49, 0x50, 0x2F, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x20, + 0x28, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x61, + 0x76, 0x61, 0x6E, 0x6E, 0x61, 0x68, 0x2E, 0x6E, 0x6F, 0x6E, + 0x67, 0x6E, 0x75, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x70, 0x72, + 0x6F, 0x6A, 0x65, 0x63, 0x74, 0x73, 0x2F, 0x6C, 0x77, 0x69, + 0x70, 0x29, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, + 0x74, 0x2D, 0x74, 0x79, 0x70, 0x65, 0x3A, 0x20, 0x74, 0x65, + 0x78, 0x74, 0x2F, 0x68, 0x74, 0x6D, 0x6C, 0x0D, 0x0A, 0x0D, + 0x0A, 0x3C, 0x21, 0x44, 0x4F, 0x43, 0x54, 0x59, 0x50, 0x45, + 0x20, 0x68, 0x74, 0x6D, 0x6C, 0x3E, 0x0A, 0x3C, 0x68, 0x74, + 0x6D, 0x6C, 0x3E, 0x0A, 0x09, 0x3C, 0x68, 0x65, 0x61, 0x64, + 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x6D, 0x65, 0x74, 0x61, 0x20, + 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3D, 0x22, 0x75, + 0x74, 0x66, 0x2D, 0x38, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x3C, + 0x6D, 0x65, 0x74, 0x61, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, + 0x22, 0x76, 0x69, 0x65, 0x77, 0x70, 0x6F, 0x72, 0x74, 0x22, + 0x20, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x3D, 0x22, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x3D, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x2D, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2C, 0x20, + 0x75, 0x73, 0x65, 0x72, 0x2D, 0x73, 0x63, 0x61, 0x6C, 0x61, + 0x62, 0x6C, 0x65, 0x3D, 0x6E, 0x6F, 0x22, 0x3E, 0x0A, 0x09, + 0x09, 0x3C, 0x6C, 0x69, 0x6E, 0x6B, 0x20, 0x72, 0x65, 0x6C, + 0x3D, 0x22, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x73, 0x68, 0x65, + 0x65, 0x74, 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x22, + 0x74, 0x65, 0x78, 0x74, 0x2F, 0x63, 0x73, 0x73, 0x22, 0x20, + 0x68, 0x72, 0x65, 0x66, 0x3D, 0x22, 0x63, 0x73, 0x73, 0x2F, + 0x73, 0x69, 0x69, 0x6D, 0x70, 0x6C, 0x65, 0x2E, 0x6D, 0x69, + 0x6E, 0x2E, 0x63, 0x73, 0x73, 0x22, 0x3E, 0x0A, 0x09, 0x09, + 0x3C, 0x6C, 0x69, 0x6E, 0x6B, 0x20, 0x72, 0x65, 0x6C, 0x3D, + 0x22, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x73, 0x68, 0x65, 0x65, + 0x74, 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x74, + 0x65, 0x78, 0x74, 0x2F, 0x63, 0x73, 0x73, 0x22, 0x20, 0x68, + 0x72, 0x65, 0x66, 0x3D, 0x22, 0x63, 0x73, 0x73, 0x2F, 0x73, + 0x74, 0x79, 0x6C, 0x65, 0x2E, 0x63, 0x73, 0x73, 0x22, 0x3E, + 0x0A, 0x09, 0x09, 0x3C, 0x6C, 0x69, 0x6E, 0x6B, 0x20, 0x72, + 0x65, 0x6C, 0x3D, 0x22, 0x73, 0x68, 0x6F, 0x72, 0x74, 0x63, + 0x75, 0x74, 0x20, 0x69, 0x63, 0x6F, 0x6E, 0x22, 0x20, 0x68, + 0x72, 0x65, 0x66, 0x3D, 0x22, 0x69, 0x6D, 0x67, 0x2F, 0x66, + 0x61, 0x76, 0x69, 0x63, 0x6F, 0x6E, 0x2E, 0x70, 0x6E, 0x67, + 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x74, 0x69, 0x74, 0x6C, + 0x65, 0x3E, 0x48, 0x54, 0x54, 0x50, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x3C, 0x2F, 0x74, 0x69, 0x74, 0x6C, 0x65, + 0x3E, 0x0A, 0x09, 0x3C, 0x2F, 0x68, 0x65, 0x61, 0x64, 0x3E, + 0x0A, 0x09, 0x3C, 0x62, 0x6F, 0x64, 0x79, 0x3E, 0x0A, 0x09, + 0x09, 0x3C, 0x75, 0x6C, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, + 0x3D, 0x22, 0x6E, 0x61, 0x76, 0x62, 0x61, 0x72, 0x22, 0x3E, + 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x6C, 0x69, 0x3E, 0x3C, 0x61, + 0x20, 0x68, 0x72, 0x65, 0x66, 0x3D, 0x22, 0x2F, 0x22, 0x3E, + 0x48, 0x6F, 0x6D, 0x65, 0x3C, 0x2F, 0x61, 0x3E, 0x3C, 0x2F, + 0x6C, 0x69, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x6C, 0x69, + 0x3E, 0x3C, 0x61, 0x20, 0x68, 0x72, 0x65, 0x66, 0x3D, 0x22, + 0x77, 0x65, 0x62, 0x73, 0x6F, 0x63, 0x6B, 0x65, 0x74, 0x73, + 0x22, 0x3E, 0x57, 0x65, 0x62, 0x53, 0x6F, 0x63, 0x6B, 0x65, + 0x74, 0x73, 0x3C, 0x2F, 0x61, 0x3E, 0x3C, 0x2F, 0x6C, 0x69, + 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x6C, 0x69, 0x3E, 0x3C, + 0x61, 0x20, 0x68, 0x72, 0x65, 0x66, 0x3D, 0x22, 0x61, 0x62, + 0x6F, 0x75, 0x74, 0x22, 0x3E, 0x41, 0x62, 0x6F, 0x75, 0x74, + 0x3C, 0x2F, 0x61, 0x3E, 0x3C, 0x2F, 0x6C, 0x69, 0x3E, 0x0A, + 0x09, 0x09, 0x3C, 0x2F, 0x75, 0x6C, 0x3E, 0x0A, 0x0A, 0x09, + 0x09, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6C, 0x61, 0x73, + 0x73, 0x3D, 0x22, 0x67, 0x72, 0x69, 0x64, 0x20, 0x6D, 0x61, + 0x69, 0x6E, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x68, + 0x31, 0x3E, 0x34, 0x30, 0x34, 0x20, 0x2D, 0x20, 0x50, 0x61, + 0x67, 0x65, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x66, 0x6F, 0x75, + 0x6E, 0x64, 0x3C, 0x2F, 0x68, 0x31, 0x3E, 0x0A, 0x09, 0x09, + 0x09, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6C, 0x61, 0x73, + 0x73, 0x3D, 0x22, 0x61, 0x6C, 0x65, 0x72, 0x74, 0x20, 0x61, + 0x6C, 0x65, 0x72, 0x74, 0x2D, 0x65, 0x72, 0x72, 0x6F, 0x72, + 0x22, 0x3E, 0x53, 0x6F, 0x72, 0x72, 0x79, 0x2C, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x70, 0x61, 0x67, 0x65, 0x20, 0x79, 0x6F, + 0x75, 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x77, 0x61, 0x73, + 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x66, 0x6F, 0x75, 0x6E, 0x64, + 0x20, 0x6F, 0x6E, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2E, 0x3C, 0x2F, 0x64, 0x69, + 0x76, 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x2F, 0x64, 0x69, 0x76, + 0x3E, 0x0A, 0x09, 0x3C, 0x2F, 0x62, 0x6F, 0x64, 0x79, 0x3E, + 0x0A, 0x3C, 0x2F, 0x68, 0x74, 0x6D, 0x6C, 0x3E, 0x0A, 0x0A, +}; + +static const unsigned char data_websockets_html[] = { + /* /websockets.html */ + 0x2F, 0x77, 0x65, 0x62, 0x73, 0x6F, 0x63, 0x6B, 0x65, 0x74, 0x73, 0x2E, 0x68, 0x74, 0x6D, 0x6C, 0, + 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x20, 0x4F, 0x4B, 0x0D, 0x0A, 0x6C, 0x77, 0x49, + 0x50, 0x2F, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x20, 0x28, 0x68, + 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x61, 0x76, 0x61, + 0x6E, 0x6E, 0x61, 0x68, 0x2E, 0x6E, 0x6F, 0x6E, 0x67, 0x6E, + 0x75, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x70, 0x72, 0x6F, 0x6A, + 0x65, 0x63, 0x74, 0x73, 0x2F, 0x6C, 0x77, 0x69, 0x70, 0x29, + 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, + 0x74, 0x79, 0x70, 0x65, 0x3A, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x2F, 0x68, 0x74, 0x6D, 0x6C, 0x0D, 0x0A, 0x0D, 0x0A, 0x3C, + 0x21, 0x44, 0x4F, 0x43, 0x54, 0x59, 0x50, 0x45, 0x20, 0x68, + 0x74, 0x6D, 0x6C, 0x3E, 0x0A, 0x3C, 0x68, 0x74, 0x6D, 0x6C, + 0x3E, 0x0A, 0x09, 0x3C, 0x68, 0x65, 0x61, 0x64, 0x3E, 0x0A, + 0x09, 0x09, 0x3C, 0x6D, 0x65, 0x74, 0x61, 0x20, 0x63, 0x68, + 0x61, 0x72, 0x73, 0x65, 0x74, 0x3D, 0x22, 0x75, 0x74, 0x66, + 0x2D, 0x38, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x6D, 0x65, + 0x74, 0x61, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x76, + 0x69, 0x65, 0x77, 0x70, 0x6F, 0x72, 0x74, 0x22, 0x20, 0x63, + 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x3D, 0x22, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x3D, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x2D, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2C, 0x20, 0x75, 0x73, + 0x65, 0x72, 0x2D, 0x73, 0x63, 0x61, 0x6C, 0x61, 0x62, 0x6C, + 0x65, 0x3D, 0x6E, 0x6F, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x3C, + 0x6C, 0x69, 0x6E, 0x6B, 0x20, 0x72, 0x65, 0x6C, 0x3D, 0x22, + 0x73, 0x74, 0x79, 0x6C, 0x65, 0x73, 0x68, 0x65, 0x65, 0x74, + 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x74, 0x65, + 0x78, 0x74, 0x2F, 0x63, 0x73, 0x73, 0x22, 0x20, 0x68, 0x72, + 0x65, 0x66, 0x3D, 0x22, 0x63, 0x73, 0x73, 0x2F, 0x73, 0x69, + 0x69, 0x6D, 0x70, 0x6C, 0x65, 0x2E, 0x6D, 0x69, 0x6E, 0x2E, + 0x63, 0x73, 0x73, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x6C, + 0x69, 0x6E, 0x6B, 0x20, 0x72, 0x65, 0x6C, 0x3D, 0x22, 0x73, + 0x74, 0x79, 0x6C, 0x65, 0x73, 0x68, 0x65, 0x65, 0x74, 0x22, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x74, 0x65, 0x78, + 0x74, 0x2F, 0x63, 0x73, 0x73, 0x22, 0x20, 0x68, 0x72, 0x65, + 0x66, 0x3D, 0x22, 0x63, 0x73, 0x73, 0x2F, 0x73, 0x74, 0x79, + 0x6C, 0x65, 0x2E, 0x63, 0x73, 0x73, 0x22, 0x3E, 0x0A, 0x09, + 0x09, 0x3C, 0x6C, 0x69, 0x6E, 0x6B, 0x20, 0x72, 0x65, 0x6C, + 0x3D, 0x22, 0x73, 0x68, 0x6F, 0x72, 0x74, 0x63, 0x75, 0x74, + 0x20, 0x69, 0x63, 0x6F, 0x6E, 0x22, 0x20, 0x68, 0x72, 0x65, + 0x66, 0x3D, 0x22, 0x69, 0x6D, 0x67, 0x2F, 0x66, 0x61, 0x76, + 0x69, 0x63, 0x6F, 0x6E, 0x2E, 0x70, 0x6E, 0x67, 0x22, 0x3E, + 0x0A, 0x09, 0x09, 0x3C, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x3E, + 0x48, 0x54, 0x54, 0x50, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x3C, 0x2F, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x3E, 0x0A, + 0x09, 0x3C, 0x2F, 0x68, 0x65, 0x61, 0x64, 0x3E, 0x0A, 0x09, + 0x3C, 0x62, 0x6F, 0x64, 0x79, 0x3E, 0x0A, 0x09, 0x09, 0x3C, + 0x75, 0x6C, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, + 0x6E, 0x61, 0x76, 0x62, 0x61, 0x72, 0x22, 0x3E, 0x0A, 0x09, + 0x09, 0x09, 0x3C, 0x6C, 0x69, 0x3E, 0x3C, 0x61, 0x20, 0x68, + 0x72, 0x65, 0x66, 0x3D, 0x22, 0x2F, 0x22, 0x3E, 0x48, 0x6F, + 0x6D, 0x65, 0x3C, 0x2F, 0x61, 0x3E, 0x3C, 0x2F, 0x6C, 0x69, + 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x6C, 0x69, 0x3E, 0x3C, + 0x61, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x61, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x22, 0x20, 0x68, 0x72, 0x65, + 0x66, 0x3D, 0x22, 0x77, 0x65, 0x62, 0x73, 0x6F, 0x63, 0x6B, + 0x65, 0x74, 0x73, 0x22, 0x3E, 0x57, 0x65, 0x62, 0x53, 0x6F, + 0x63, 0x6B, 0x65, 0x74, 0x73, 0x3C, 0x2F, 0x61, 0x3E, 0x3C, + 0x2F, 0x6C, 0x69, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x6C, + 0x69, 0x3E, 0x3C, 0x61, 0x20, 0x68, 0x72, 0x65, 0x66, 0x3D, + 0x22, 0x61, 0x62, 0x6F, 0x75, 0x74, 0x22, 0x3E, 0x41, 0x62, + 0x6F, 0x75, 0x74, 0x3C, 0x2F, 0x61, 0x3E, 0x3C, 0x2F, 0x6C, + 0x69, 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x2F, 0x75, 0x6C, 0x3E, + 0x0A, 0x0A, 0x09, 0x09, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, + 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x67, 0x72, 0x69, 0x64, + 0x20, 0x6D, 0x61, 0x69, 0x6E, 0x22, 0x3E, 0x0A, 0x09, 0x09, + 0x09, 0x3C, 0x68, 0x31, 0x3E, 0x57, 0x65, 0x62, 0x53, 0x6F, + 0x63, 0x6B, 0x65, 0x74, 0x73, 0x20, 0x44, 0x65, 0x6D, 0x6F, + 0x3C, 0x2F, 0x68, 0x31, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, + 0x64, 0x69, 0x76, 0x20, 0x69, 0x64, 0x3D, 0x22, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x5F, 0x62, 0x6F, 0x78, 0x22, 0x20, + 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x61, 0x6C, 0x65, + 0x72, 0x74, 0x20, 0x61, 0x6C, 0x65, 0x72, 0x74, 0x2D, 0x69, + 0x6E, 0x66, 0x6F, 0x22, 0x3E, 0x4C, 0x6F, 0x61, 0x64, 0x69, + 0x6E, 0x67, 0x2E, 0x2E, 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, + 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x70, 0x3E, 0x54, 0x68, 0x69, + 0x73, 0x20, 0x70, 0x61, 0x67, 0x65, 0x20, 0x69, 0x73, 0x20, + 0x73, 0x69, 0x6D, 0x69, 0x6C, 0x61, 0x72, 0x20, 0x74, 0x6F, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x6F, 0x6D, 0x65, 0x20, + 0x70, 0x61, 0x67, 0x65, 0x20, 0x62, 0x75, 0x74, 0x20, 0x75, + 0x73, 0x65, 0x73, 0x20, 0x57, 0x65, 0x62, 0x53, 0x6F, 0x63, + 0x6B, 0x65, 0x74, 0x73, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x72, + 0x65, 0x61, 0x6C, 0x2D, 0x74, 0x69, 0x6D, 0x65, 0x20, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2E, 0x3C, 0x2F, 0x70, + 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x64, 0x69, 0x76, 0x20, + 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x63, 0x6F, 0x76, + 0x65, 0x72, 0x22, 0x20, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3D, + 0x22, 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x22, 0x3E, 0x0A, + 0x09, 0x09, 0x09, 0x09, 0x3C, 0x63, 0x61, 0x6E, 0x76, 0x61, + 0x73, 0x20, 0x69, 0x64, 0x3D, 0x22, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x43, 0x61, 0x6E, 0x76, 0x61, 0x73, 0x22, 0x20, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x3D, 0x22, 0x35, 0x31, 0x32, 0x22, + 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3D, 0x22, 0x31, + 0x30, 0x30, 0x22, 0x3E, 0x3C, 0x2F, 0x63, 0x61, 0x6E, 0x76, + 0x61, 0x73, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x70, + 0x2F, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x70, 0x3E, + 0x4C, 0x45, 0x44, 0x20, 0x43, 0x6F, 0x6E, 0x74, 0x72, 0x6F, + 0x6C, 0x3C, 0x2F, 0x70, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, + 0x3D, 0x22, 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, + 0x74, 0x63, 0x68, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x3C, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x3D, 0x22, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x62, + 0x6F, 0x78, 0x22, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, + 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, + 0x68, 0x22, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, + 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, + 0x68, 0x2D, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x62, 0x6F, 0x78, + 0x22, 0x20, 0x69, 0x64, 0x3D, 0x22, 0x6C, 0x65, 0x64, 0x2D, + 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x22, 0x20, 0x6F, 0x6E, + 0x63, 0x6C, 0x69, 0x63, 0x6B, 0x3D, 0x22, 0x67, 0x70, 0x69, + 0x6F, 0x28, 0x29, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x3C, 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x20, 0x63, 0x6C, + 0x61, 0x73, 0x73, 0x3D, 0x22, 0x6F, 0x6E, 0x6F, 0x66, 0x66, + 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, 0x6C, 0x61, 0x62, + 0x65, 0x6C, 0x22, 0x20, 0x66, 0x6F, 0x72, 0x3D, 0x22, 0x6C, + 0x65, 0x64, 0x2D, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x22, + 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x73, + 0x70, 0x61, 0x6E, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, + 0x22, 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, + 0x63, 0x68, 0x2D, 0x69, 0x6E, 0x6E, 0x65, 0x72, 0x22, 0x3E, + 0x3C, 0x2F, 0x73, 0x70, 0x61, 0x6E, 0x3E, 0x0A, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x3C, 0x73, 0x70, 0x61, 0x6E, 0x20, + 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x6F, 0x6E, 0x6F, + 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, 0x73, + 0x77, 0x69, 0x74, 0x63, 0x68, 0x22, 0x3E, 0x3C, 0x2F, 0x73, + 0x70, 0x61, 0x6E, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x3C, 0x2F, 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x3E, 0x0A, 0x09, + 0x09, 0x09, 0x09, 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, 0x0A, + 0x09, 0x09, 0x09, 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, 0x0A, + 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x68, 0x31, 0x3E, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x20, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x3C, 0x2F, 0x68, 0x31, 0x3E, 0x0A, 0x09, 0x09, 0x09, + 0x3C, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x20, 0x63, 0x6C, 0x61, + 0x73, 0x73, 0x3D, 0x22, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x20, + 0x74, 0x61, 0x62, 0x6C, 0x65, 0x2D, 0x73, 0x74, 0x72, 0x69, + 0x70, 0x65, 0x64, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x3C, 0x74, 0x72, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x3C, 0x74, 0x64, 0x3E, 0x3C, 0x62, 0x3E, 0x55, 0x70, 0x74, + 0x69, 0x6D, 0x65, 0x3A, 0x3C, 0x2F, 0x62, 0x3E, 0x3C, 0x2F, + 0x74, 0x64, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3C, + 0x74, 0x64, 0x20, 0x69, 0x64, 0x3D, 0x22, 0x75, 0x70, 0x74, + 0x69, 0x6D, 0x65, 0x22, 0x3E, 0x3C, 0x2F, 0x74, 0x64, 0x3E, + 0x0A, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x2F, 0x74, 0x72, 0x3E, + 0x0A, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x74, 0x72, 0x3E, 0x0A, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x74, 0x64, 0x3E, 0x3C, + 0x62, 0x3E, 0x46, 0x72, 0x65, 0x65, 0x20, 0x68, 0x65, 0x61, + 0x70, 0x3A, 0x3C, 0x2F, 0x62, 0x3E, 0x3C, 0x2F, 0x74, 0x64, + 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x74, 0x64, + 0x20, 0x69, 0x64, 0x3D, 0x22, 0x68, 0x65, 0x61, 0x70, 0x22, + 0x3E, 0x3C, 0x2F, 0x74, 0x64, 0x3E, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x3C, 0x2F, 0x74, 0x72, 0x3E, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x3C, 0x74, 0x72, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x3C, 0x74, 0x64, 0x3E, 0x3C, 0x62, 0x3E, 0x4C, 0x45, + 0x44, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3A, 0x3C, 0x2F, + 0x62, 0x3E, 0x3C, 0x2F, 0x74, 0x64, 0x3E, 0x0A, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x3C, 0x74, 0x64, 0x20, 0x69, 0x64, 0x3D, + 0x22, 0x6C, 0x65, 0x64, 0x22, 0x3E, 0x3C, 0x2F, 0x74, 0x64, + 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x3C, 0x2F, 0x74, 0x72, + 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x2F, 0x74, 0x61, 0x62, + 0x6C, 0x65, 0x3E, 0x0A, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x68, + 0x31, 0x3E, 0x48, 0x6F, 0x77, 0x20, 0x69, 0x74, 0x20, 0x77, + 0x6F, 0x72, 0x6B, 0x73, 0x3C, 0x2F, 0x68, 0x31, 0x3E, 0x0A, + 0x09, 0x09, 0x09, 0x3C, 0x70, 0x3E, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x64, 0x65, 0x6D, 0x6F, 0x20, 0x75, 0x73, 0x65, 0x73, + 0x20, 0x32, 0x20, 0x57, 0x65, 0x62, 0x53, 0x63, 0x6F, 0x6B, + 0x65, 0x74, 0x73, 0x2E, 0x20, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x65, 0x74, 0x65, + 0x72, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6D, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, + 0x69, 0x6E, 0x20, 0x4A, 0x53, 0x4F, 0x4E, 0x20, 0x66, 0x6F, + 0x72, 0x6D, 0x61, 0x74, 0x20, 0x65, 0x76, 0x65, 0x72, 0x79, + 0x20, 0x32, 0x20, 0x73, 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x73, + 0x2E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x41, 0x20, 0x3C, 0x63, + 0x6F, 0x64, 0x65, 0x3E, 0x77, 0x65, 0x62, 0x73, 0x6F, 0x63, + 0x6B, 0x65, 0x74, 0x5F, 0x74, 0x61, 0x73, 0x6B, 0x3C, 0x2F, + 0x63, 0x6F, 0x64, 0x65, 0x3E, 0x20, 0x69, 0x73, 0x20, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x65, 0x61, 0x63, + 0x68, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x20, 0x61, 0x20, 0x73, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x55, 0x52, + 0x49, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x65, 0x64, 0x2E, 0x3C, 0x2F, 0x70, 0x3E, 0x0A, + 0x09, 0x09, 0x09, 0x3C, 0x70, 0x3E, 0x41, 0x44, 0x43, 0x20, + 0x76, 0x61, 0x6C, 0x75, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, + 0x20, 0x62, 0x65, 0x69, 0x6E, 0x67, 0x20, 0x63, 0x6F, 0x6E, + 0x74, 0x69, 0x6E, 0x75, 0x6F, 0x75, 0x73, 0x6C, 0x79, 0x20, + 0x70, 0x6F, 0x6C, 0x6C, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, + 0x20, 0x28, 0x69, 0x2E, 0x65, 0x2E, 0x20, 0x79, 0x6F, 0x75, + 0x72, 0x20, 0x62, 0x72, 0x6F, 0x77, 0x73, 0x65, 0x72, 0x29, + 0x2E, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x45, 0x61, 0x63, 0x68, + 0x20, 0x74, 0x69, 0x6D, 0x65, 0x20, 0x61, 0x20, 0x57, 0x65, + 0x62, 0x53, 0x6F, 0x63, 0x6B, 0x65, 0x74, 0x20, 0x66, 0x72, + 0x61, 0x6D, 0x65, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x6F, 0x6E, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, + 0x73, 0x69, 0x64, 0x65, 0x2C, 0x20, 0x3C, 0x63, 0x6F, 0x64, + 0x65, 0x3E, 0x77, 0x65, 0x62, 0x73, 0x6F, 0x63, 0x6B, 0x65, + 0x74, 0x5F, 0x63, 0x62, 0x3C, 0x2F, 0x63, 0x6F, 0x64, 0x65, + 0x3E, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, + 0x20, 0x69, 0x73, 0x20, 0x62, 0x65, 0x69, 0x6E, 0x67, 0x20, + 0x63, 0x61, 0x6C, 0x6C, 0x65, 0x64, 0x2E, 0x3C, 0x2F, 0x70, + 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, + 0x0A, 0x0A, 0x09, 0x09, 0x3C, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x74, 0x65, + 0x78, 0x74, 0x2F, 0x6A, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x22, 0x20, 0x73, 0x72, 0x63, 0x3D, 0x22, + 0x6A, 0x73, 0x2F, 0x73, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, + 0x65, 0x5F, 0x6D, 0x69, 0x6E, 0x2E, 0x6A, 0x73, 0x22, 0x3E, + 0x3C, 0x2F, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3E, 0x0A, + 0x09, 0x09, 0x3C, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3E, + 0x0A, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x77, 0x73, + 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x72, + 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x3B, 0x0A, 0x09, 0x09, + 0x09, 0x76, 0x61, 0x72, 0x20, 0x73, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x20, 0x3D, 0x20, 0x6E, 0x65, 0x77, 0x20, 0x54, 0x69, + 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x28, 0x29, + 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x77, 0x69, 0x6E, 0x64, 0x6F, + 0x77, 0x2E, 0x6F, 0x6E, 0x6C, 0x6F, 0x61, 0x64, 0x20, 0x3D, + 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, + 0x29, 0x20, 0x7B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x77, 0x73, + 0x4F, 0x70, 0x65, 0x6E, 0x28, 0x29, 0x3B, 0x0A, 0x09, 0x09, + 0x09, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6F, 0x6C, + 0x6C, 0x69, 0x6E, 0x67, 0x28, 0x29, 0x3B, 0x0A, 0x09, 0x09, + 0x09, 0x7D, 0x0A, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6E, 0x63, + 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x65, 0x74, 0x4D, 0x73, + 0x67, 0x28, 0x63, 0x6C, 0x73, 0x2C, 0x20, 0x74, 0x65, 0x78, + 0x74, 0x29, 0x20, 0x7B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x73, + 0x62, 0x6F, 0x78, 0x20, 0x3D, 0x20, 0x64, 0x6F, 0x63, 0x75, + 0x6D, 0x65, 0x6E, 0x74, 0x2E, 0x67, 0x65, 0x74, 0x45, 0x6C, + 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, + 0x27, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5F, 0x62, 0x6F, + 0x78, 0x27, 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x73, + 0x62, 0x6F, 0x78, 0x2E, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x4E, + 0x61, 0x6D, 0x65, 0x20, 0x3D, 0x20, 0x22, 0x61, 0x6C, 0x65, + 0x72, 0x74, 0x20, 0x61, 0x6C, 0x65, 0x72, 0x74, 0x2D, 0x22, + 0x20, 0x2B, 0x20, 0x63, 0x6C, 0x73, 0x3B, 0x0A, 0x09, 0x09, + 0x09, 0x09, 0x73, 0x62, 0x6F, 0x78, 0x2E, 0x69, 0x6E, 0x6E, + 0x65, 0x72, 0x48, 0x54, 0x4D, 0x4C, 0x20, 0x3D, 0x20, 0x74, + 0x65, 0x78, 0x74, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x63, + 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2E, 0x6C, 0x6F, 0x67, + 0x28, 0x74, 0x65, 0x78, 0x74, 0x29, 0x3B, 0x0A, 0x09, 0x09, + 0x09, 0x7D, 0x0A, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6E, 0x63, + 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x50, 0x6F, 0x6C, 0x6C, 0x69, 0x6E, 0x67, 0x28, 0x29, 0x20, + 0x7B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, + 0x63, 0x68, 0x61, 0x72, 0x74, 0x20, 0x3D, 0x20, 0x6E, 0x65, + 0x77, 0x20, 0x53, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, + 0x43, 0x68, 0x61, 0x72, 0x74, 0x28, 0x7B, 0x6D, 0x69, 0x6C, + 0x6C, 0x69, 0x73, 0x50, 0x65, 0x72, 0x50, 0x69, 0x78, 0x65, + 0x6C, 0x3A, 0x31, 0x31, 0x2C, 0x67, 0x72, 0x69, 0x64, 0x3A, + 0x7B, 0x66, 0x69, 0x6C, 0x6C, 0x53, 0x74, 0x79, 0x6C, 0x65, + 0x3A, 0x27, 0x23, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x27, + 0x2C, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x53, 0x74, 0x79, + 0x6C, 0x65, 0x3A, 0x27, 0x23, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x27, 0x2C, 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x56, + 0x69, 0x73, 0x69, 0x62, 0x6C, 0x65, 0x3A, 0x66, 0x61, 0x6C, + 0x73, 0x65, 0x7D, 0x2C, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x73, 0x3A, 0x7B, 0x66, 0x69, + 0x6C, 0x6C, 0x53, 0x74, 0x79, 0x6C, 0x65, 0x3A, 0x27, 0x23, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x27, 0x7D, 0x2C, 0x6D, + 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3A, 0x31, 0x30, + 0x32, 0x34, 0x2C, 0x6D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, + 0x65, 0x3A, 0x30, 0x7D, 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x63, 0x68, 0x61, 0x72, 0x74, 0x2E, 0x61, 0x64, 0x64, + 0x54, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x28, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2C, 0x20, 0x7B, + 0x6C, 0x69, 0x6E, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x3A, + 0x32, 0x2C, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x53, 0x74, + 0x79, 0x6C, 0x65, 0x3A, 0x27, 0x23, 0x30, 0x33, 0x61, 0x39, + 0x66, 0x34, 0x27, 0x2C, 0x66, 0x69, 0x6C, 0x6C, 0x53, 0x74, + 0x79, 0x6C, 0x65, 0x3A, 0x27, 0x23, 0x66, 0x31, 0x66, 0x35, + 0x66, 0x61, 0x27, 0x7D, 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x63, 0x68, 0x61, 0x72, 0x74, 0x2E, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6D, 0x54, 0x6F, 0x28, 0x64, 0x6F, 0x63, 0x75, + 0x6D, 0x65, 0x6E, 0x74, 0x2E, 0x67, 0x65, 0x74, 0x45, 0x6C, + 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, + 0x22, 0x63, 0x68, 0x61, 0x72, 0x74, 0x43, 0x61, 0x6E, 0x76, + 0x61, 0x73, 0x22, 0x29, 0x2C, 0x20, 0x35, 0x30, 0x30, 0x29, + 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x74, 0x49, + 0x6E, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6C, 0x28, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x20, 0x7B, + 0x20, 0x77, 0x73, 0x57, 0x72, 0x69, 0x74, 0x65, 0x28, 0x27, + 0x41, 0x27, 0x29, 0x3B, 0x20, 0x7D, 0x2C, 0x20, 0x35, 0x30, + 0x30, 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x7D, 0x0A, 0x09, + 0x09, 0x09, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, + 0x20, 0x6F, 0x6E, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x28, 0x65, 0x76, 0x74, 0x29, 0x20, 0x7B, 0x0A, 0x09, 0x09, + 0x09, 0x09, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x20, + 0x3D, 0x20, 0x30, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x76, + 0x61, 0x72, 0x20, 0x64, 0x76, 0x20, 0x3D, 0x20, 0x6E, 0x65, + 0x77, 0x20, 0x44, 0x61, 0x74, 0x61, 0x56, 0x69, 0x65, 0x77, + 0x28, 0x65, 0x76, 0x74, 0x2E, 0x64, 0x61, 0x74, 0x61, 0x29, + 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, + 0x76, 0x61, 0x6C, 0x20, 0x3D, 0x20, 0x64, 0x76, 0x2E, 0x67, + 0x65, 0x74, 0x55, 0x69, 0x6E, 0x74, 0x31, 0x36, 0x28, 0x30, + 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, + 0x28, 0x76, 0x61, 0x6C, 0x20, 0x3D, 0x3D, 0x20, 0x30, 0x78, + 0x42, 0x45, 0x45, 0x46, 0x20, 0x7C, 0x7C, 0x20, 0x76, 0x61, + 0x6C, 0x20, 0x3D, 0x3D, 0x20, 0x30, 0x78, 0x44, 0x45, 0x41, + 0x44, 0x29, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x63, 0x6F, + 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2E, 0x6C, 0x6F, 0x67, 0x28, + 0x22, 0x4C, 0x45, 0x44, 0x20, 0x73, 0x77, 0x69, 0x74, 0x63, + 0x68, 0x65, 0x64, 0x22, 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x65, 0x6C, 0x73, 0x65, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x61, 0x70, + 0x70, 0x65, 0x6E, 0x64, 0x28, 0x6E, 0x65, 0x77, 0x20, 0x44, + 0x61, 0x74, 0x65, 0x28, 0x29, 0x2E, 0x67, 0x65, 0x74, 0x54, + 0x69, 0x6D, 0x65, 0x28, 0x29, 0x2C, 0x20, 0x76, 0x61, 0x6C, + 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x7D, 0x0A, 0x09, 0x09, + 0x09, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, + 0x77, 0x73, 0x4F, 0x70, 0x65, 0x6E, 0x28, 0x29, 0x20, 0x7B, + 0x0A, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x77, + 0x73, 0x20, 0x3D, 0x3D, 0x3D, 0x20, 0x75, 0x6E, 0x64, 0x65, + 0x66, 0x69, 0x6E, 0x65, 0x64, 0x20, 0x7C, 0x7C, 0x20, 0x77, + 0x73, 0x2E, 0x72, 0x65, 0x61, 0x64, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x20, 0x21, 0x3D, 0x20, 0x30, 0x29, 0x20, 0x7B, + 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, + 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x29, 0x0A, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x73, 0x65, 0x74, 0x4D, 0x73, + 0x67, 0x28, 0x22, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x22, 0x2C, + 0x20, 0x22, 0x57, 0x65, 0x62, 0x53, 0x6F, 0x63, 0x6B, 0x65, + 0x74, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0x2C, + 0x20, 0x72, 0x65, 0x74, 0x72, 0x79, 0x69, 0x6E, 0x67, 0x2E, + 0x2E, 0x22, 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x65, 0x6C, 0x73, 0x65, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x73, 0x65, 0x74, 0x4D, 0x73, 0x67, 0x28, 0x22, 0x69, + 0x6E, 0x66, 0x6F, 0x22, 0x2C, 0x20, 0x22, 0x4F, 0x70, 0x65, + 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x57, 0x65, 0x62, 0x53, 0x6F, + 0x63, 0x6B, 0x65, 0x74, 0x2E, 0x2E, 0x22, 0x29, 0x3B, 0x0A, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x77, 0x73, 0x20, 0x3D, 0x20, + 0x6E, 0x65, 0x77, 0x20, 0x57, 0x65, 0x62, 0x53, 0x6F, 0x63, + 0x6B, 0x65, 0x74, 0x28, 0x22, 0x77, 0x73, 0x3A, 0x2F, 0x2F, + 0x22, 0x20, 0x2B, 0x20, 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x69, + 0x6F, 0x6E, 0x2E, 0x68, 0x6F, 0x73, 0x74, 0x29, 0x3B, 0x0A, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x77, 0x73, 0x2E, 0x62, 0x69, + 0x6E, 0x61, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x20, 0x3D, + 0x20, 0x27, 0x61, 0x72, 0x72, 0x61, 0x79, 0x62, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x27, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x77, 0x73, 0x2E, 0x6F, 0x6E, 0x6F, 0x70, 0x65, 0x6E, + 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, + 0x6E, 0x28, 0x65, 0x76, 0x74, 0x29, 0x20, 0x7B, 0x20, 0x72, + 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x20, 0x3D, 0x20, 0x30, + 0x3B, 0x20, 0x73, 0x65, 0x74, 0x4D, 0x73, 0x67, 0x28, 0x22, + 0x64, 0x6F, 0x6E, 0x65, 0x22, 0x2C, 0x20, 0x22, 0x57, 0x65, + 0x62, 0x53, 0x6F, 0x63, 0x6B, 0x65, 0x74, 0x20, 0x69, 0x73, + 0x20, 0x6F, 0x70, 0x65, 0x6E, 0x2E, 0x22, 0x29, 0x3B, 0x20, + 0x7D, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x77, 0x73, + 0x2E, 0x6F, 0x6E, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x20, 0x3D, + 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, + 0x65, 0x76, 0x74, 0x29, 0x20, 0x7B, 0x20, 0x73, 0x65, 0x74, + 0x4D, 0x73, 0x67, 0x28, 0x22, 0x65, 0x72, 0x72, 0x6F, 0x72, + 0x22, 0x2C, 0x20, 0x22, 0x57, 0x65, 0x62, 0x53, 0x6F, 0x63, + 0x6B, 0x65, 0x74, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x21, + 0x22, 0x29, 0x3B, 0x20, 0x7D, 0x3B, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x77, 0x73, 0x2E, 0x6F, 0x6E, 0x6D, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, + 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x65, 0x76, 0x74, 0x29, + 0x20, 0x7B, 0x20, 0x6F, 0x6E, 0x4D, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x28, 0x65, 0x76, 0x74, 0x29, 0x3B, 0x20, 0x7D, + 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x77, 0x73, 0x4F, + 0x70, 0x65, 0x6E, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x28, + 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, + 0x74, 0x72, 0x69, 0x65, 0x73, 0x20, 0x3D, 0x20, 0x30, 0x3B, + 0x0A, 0x09, 0x09, 0x09, 0x09, 0x7D, 0x0A, 0x09, 0x09, 0x09, + 0x7D, 0x0A, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6E, 0x63, 0x74, + 0x69, 0x6F, 0x6E, 0x20, 0x77, 0x73, 0x4F, 0x70, 0x65, 0x6E, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x28, 0x29, 0x20, 0x7B, + 0x0A, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x75, + 0x72, 0x69, 0x20, 0x3D, 0x20, 0x22, 0x2F, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6D, 0x22, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x76, + 0x61, 0x72, 0x20, 0x77, 0x73, 0x20, 0x3D, 0x20, 0x6E, 0x65, + 0x77, 0x20, 0x57, 0x65, 0x62, 0x53, 0x6F, 0x63, 0x6B, 0x65, + 0x74, 0x28, 0x22, 0x77, 0x73, 0x3A, 0x2F, 0x2F, 0x22, 0x20, + 0x2B, 0x20, 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, + 0x2E, 0x68, 0x6F, 0x73, 0x74, 0x20, 0x2B, 0x20, 0x75, 0x72, + 0x69, 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x77, 0x73, + 0x2E, 0x6F, 0x6E, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x20, 0x3D, 0x20, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, + 0x6E, 0x28, 0x65, 0x76, 0x74, 0x29, 0x20, 0x7B, 0x0A, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, + 0x65, 0x2E, 0x6C, 0x6F, 0x67, 0x28, 0x65, 0x76, 0x74, 0x2E, + 0x64, 0x61, 0x74, 0x61, 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x76, 0x61, 0x72, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x73, 0x20, 0x3D, 0x20, 0x4A, 0x53, 0x4F, 0x4E, 0x2E, 0x70, + 0x61, 0x72, 0x73, 0x65, 0x28, 0x65, 0x76, 0x74, 0x2E, 0x64, + 0x61, 0x74, 0x61, 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x63, 0x6F, 0x6E, 0x73, 0x6F, 0x6C, 0x65, 0x2E, 0x6C, + 0x6F, 0x67, 0x28, 0x73, 0x74, 0x61, 0x74, 0x73, 0x29, 0x3B, + 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x64, 0x6F, 0x63, 0x75, + 0x6D, 0x65, 0x6E, 0x74, 0x2E, 0x67, 0x65, 0x74, 0x45, 0x6C, + 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, + 0x27, 0x75, 0x70, 0x74, 0x69, 0x6D, 0x65, 0x27, 0x29, 0x2E, + 0x69, 0x6E, 0x6E, 0x65, 0x72, 0x48, 0x54, 0x4D, 0x4C, 0x20, + 0x3D, 0x20, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2E, 0x75, 0x70, + 0x74, 0x69, 0x6D, 0x65, 0x20, 0x2B, 0x20, 0x27, 0x20, 0x73, + 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x73, 0x27, 0x3B, 0x0A, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x64, 0x6F, 0x63, 0x75, 0x6D, 0x65, + 0x6E, 0x74, 0x2E, 0x67, 0x65, 0x74, 0x45, 0x6C, 0x65, 0x6D, + 0x65, 0x6E, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x27, 0x68, + 0x65, 0x61, 0x70, 0x27, 0x29, 0x2E, 0x69, 0x6E, 0x6E, 0x65, + 0x72, 0x48, 0x54, 0x4D, 0x4C, 0x20, 0x3D, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x73, 0x2E, 0x68, 0x65, 0x61, 0x70, 0x20, 0x2B, + 0x20, 0x27, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x27, 0x3B, + 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x64, 0x6F, 0x63, 0x75, + 0x6D, 0x65, 0x6E, 0x74, 0x2E, 0x67, 0x65, 0x74, 0x45, 0x6C, + 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, + 0x27, 0x6C, 0x65, 0x64, 0x27, 0x29, 0x2E, 0x69, 0x6E, 0x6E, + 0x65, 0x72, 0x48, 0x54, 0x4D, 0x4C, 0x20, 0x3D, 0x20, 0x28, + 0x73, 0x74, 0x61, 0x74, 0x73, 0x2E, 0x6C, 0x65, 0x64, 0x20, + 0x3D, 0x3D, 0x20, 0x31, 0x29, 0x20, 0x3F, 0x20, 0x27, 0x4F, + 0x6E, 0x27, 0x20, 0x3A, 0x20, 0x27, 0x4F, 0x66, 0x66, 0x27, + 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x7D, 0x3B, 0x0A, 0x09, + 0x09, 0x09, 0x7D, 0x0A, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6E, + 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x77, 0x73, 0x57, 0x72, + 0x69, 0x74, 0x65, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, + 0x7B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, + 0x77, 0x73, 0x2E, 0x72, 0x65, 0x61, 0x64, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x20, 0x3D, 0x3D, 0x20, 0x33, 0x20, 0x7C, + 0x7C, 0x20, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x2B, + 0x2B, 0x20, 0x3E, 0x20, 0x35, 0x29, 0x0A, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x77, 0x73, 0x4F, 0x70, 0x65, 0x6E, 0x28, 0x29, + 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x65, 0x6C, 0x73, 0x65, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x77, 0x73, 0x2E, 0x72, 0x65, + 0x61, 0x64, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3D, + 0x3D, 0x20, 0x31, 0x29, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x77, 0x73, 0x2E, 0x73, 0x65, 0x6E, 0x64, 0x28, 0x64, 0x61, + 0x74, 0x61, 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x7D, 0x0A, + 0x09, 0x09, 0x09, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, + 0x6E, 0x20, 0x67, 0x70, 0x69, 0x6F, 0x28, 0x29, 0x20, 0x7B, + 0x0A, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x64, + 0x6F, 0x63, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x2E, 0x67, 0x65, + 0x74, 0x45, 0x6C, 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x42, 0x79, + 0x49, 0x64, 0x28, 0x27, 0x6C, 0x65, 0x64, 0x2D, 0x73, 0x77, + 0x69, 0x74, 0x63, 0x68, 0x27, 0x29, 0x2E, 0x63, 0x68, 0x65, + 0x63, 0x6B, 0x65, 0x64, 0x29, 0x0A, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x77, 0x73, 0x57, 0x72, 0x69, 0x74, 0x65, 0x28, 0x27, + 0x45, 0x27, 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x65, + 0x6C, 0x73, 0x65, 0x0A, 0x09, 0x09, 0x09, 0x09, 0x09, 0x77, + 0x73, 0x57, 0x72, 0x69, 0x74, 0x65, 0x28, 0x27, 0x44, 0x27, + 0x29, 0x3B, 0x0A, 0x09, 0x09, 0x09, 0x7D, 0x0A, 0x09, 0x09, + 0x3C, 0x2F, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3E, 0x0A, + 0x09, 0x3C, 0x2F, 0x62, 0x6F, 0x64, 0x79, 0x3E, 0x0A, 0x3C, + 0x2F, 0x68, 0x74, 0x6D, 0x6C, 0x3E, 0x0A, }; + +static const unsigned char data_about_html[] = { + /* /about.html */ + 0x2F, 0x61, 0x62, 0x6F, 0x75, 0x74, 0x2E, 0x68, 0x74, 0x6D, 0x6C, 0, + 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x20, 0x4F, 0x4B, 0x0D, 0x0A, 0x6C, 0x77, 0x49, + 0x50, 0x2F, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x20, 0x28, 0x68, + 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x61, 0x76, 0x61, + 0x6E, 0x6E, 0x61, 0x68, 0x2E, 0x6E, 0x6F, 0x6E, 0x67, 0x6E, + 0x75, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x70, 0x72, 0x6F, 0x6A, + 0x65, 0x63, 0x74, 0x73, 0x2F, 0x6C, 0x77, 0x69, 0x70, 0x29, + 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, + 0x74, 0x79, 0x70, 0x65, 0x3A, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x2F, 0x68, 0x74, 0x6D, 0x6C, 0x0D, 0x0A, 0x0D, 0x0A, 0x3C, + 0x21, 0x44, 0x4F, 0x43, 0x54, 0x59, 0x50, 0x45, 0x20, 0x68, + 0x74, 0x6D, 0x6C, 0x3E, 0x0A, 0x3C, 0x68, 0x74, 0x6D, 0x6C, + 0x3E, 0x0A, 0x09, 0x3C, 0x68, 0x65, 0x61, 0x64, 0x3E, 0x0A, + 0x09, 0x09, 0x3C, 0x6D, 0x65, 0x74, 0x61, 0x20, 0x63, 0x68, + 0x61, 0x72, 0x73, 0x65, 0x74, 0x3D, 0x22, 0x75, 0x74, 0x66, + 0x2D, 0x38, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x6D, 0x65, + 0x74, 0x61, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x3D, 0x22, 0x76, + 0x69, 0x65, 0x77, 0x70, 0x6F, 0x72, 0x74, 0x22, 0x20, 0x63, + 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x3D, 0x22, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x3D, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x2D, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2C, 0x20, 0x75, 0x73, + 0x65, 0x72, 0x2D, 0x73, 0x63, 0x61, 0x6C, 0x61, 0x62, 0x6C, + 0x65, 0x3D, 0x6E, 0x6F, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x3C, + 0x6C, 0x69, 0x6E, 0x6B, 0x20, 0x72, 0x65, 0x6C, 0x3D, 0x22, + 0x73, 0x74, 0x79, 0x6C, 0x65, 0x73, 0x68, 0x65, 0x65, 0x74, + 0x22, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x74, 0x65, + 0x78, 0x74, 0x2F, 0x63, 0x73, 0x73, 0x22, 0x20, 0x68, 0x72, + 0x65, 0x66, 0x3D, 0x22, 0x63, 0x73, 0x73, 0x2F, 0x73, 0x69, + 0x69, 0x6D, 0x70, 0x6C, 0x65, 0x2E, 0x6D, 0x69, 0x6E, 0x2E, + 0x63, 0x73, 0x73, 0x22, 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x6C, + 0x69, 0x6E, 0x6B, 0x20, 0x72, 0x65, 0x6C, 0x3D, 0x22, 0x73, + 0x74, 0x79, 0x6C, 0x65, 0x73, 0x68, 0x65, 0x65, 0x74, 0x22, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x22, 0x74, 0x65, 0x78, + 0x74, 0x2F, 0x63, 0x73, 0x73, 0x22, 0x20, 0x68, 0x72, 0x65, + 0x66, 0x3D, 0x22, 0x63, 0x73, 0x73, 0x2F, 0x73, 0x74, 0x79, + 0x6C, 0x65, 0x2E, 0x63, 0x73, 0x73, 0x22, 0x3E, 0x0A, 0x09, + 0x09, 0x3C, 0x6C, 0x69, 0x6E, 0x6B, 0x20, 0x72, 0x65, 0x6C, + 0x3D, 0x22, 0x73, 0x68, 0x6F, 0x72, 0x74, 0x63, 0x75, 0x74, + 0x20, 0x69, 0x63, 0x6F, 0x6E, 0x22, 0x20, 0x68, 0x72, 0x65, + 0x66, 0x3D, 0x22, 0x69, 0x6D, 0x67, 0x2F, 0x66, 0x61, 0x76, + 0x69, 0x63, 0x6F, 0x6E, 0x2E, 0x70, 0x6E, 0x67, 0x22, 0x3E, + 0x0A, 0x09, 0x09, 0x3C, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x3E, + 0x48, 0x54, 0x54, 0x50, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x3C, 0x2F, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x3E, 0x0A, + 0x09, 0x3C, 0x2F, 0x68, 0x65, 0x61, 0x64, 0x3E, 0x0A, 0x09, + 0x3C, 0x62, 0x6F, 0x64, 0x79, 0x3E, 0x0A, 0x09, 0x09, 0x3C, + 0x75, 0x6C, 0x20, 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, + 0x6E, 0x61, 0x76, 0x62, 0x61, 0x72, 0x22, 0x3E, 0x0A, 0x09, + 0x09, 0x09, 0x3C, 0x6C, 0x69, 0x3E, 0x3C, 0x61, 0x20, 0x68, + 0x72, 0x65, 0x66, 0x3D, 0x22, 0x2F, 0x22, 0x3E, 0x48, 0x6F, + 0x6D, 0x65, 0x3C, 0x2F, 0x61, 0x3E, 0x3C, 0x2F, 0x6C, 0x69, + 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, 0x6C, 0x69, 0x3E, 0x3C, + 0x61, 0x20, 0x68, 0x72, 0x65, 0x66, 0x3D, 0x22, 0x77, 0x65, + 0x62, 0x73, 0x6F, 0x63, 0x6B, 0x65, 0x74, 0x73, 0x22, 0x3E, + 0x57, 0x65, 0x62, 0x53, 0x6F, 0x63, 0x6B, 0x65, 0x74, 0x73, + 0x3C, 0x2F, 0x61, 0x3E, 0x3C, 0x2F, 0x6C, 0x69, 0x3E, 0x0A, + 0x09, 0x09, 0x09, 0x3C, 0x6C, 0x69, 0x3E, 0x3C, 0x61, 0x20, + 0x63, 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x61, 0x63, 0x74, + 0x69, 0x76, 0x65, 0x22, 0x20, 0x68, 0x72, 0x65, 0x66, 0x3D, + 0x22, 0x61, 0x62, 0x6F, 0x75, 0x74, 0x22, 0x3E, 0x41, 0x62, + 0x6F, 0x75, 0x74, 0x3C, 0x2F, 0x61, 0x3E, 0x3C, 0x2F, 0x6C, + 0x69, 0x3E, 0x0A, 0x09, 0x09, 0x3C, 0x2F, 0x75, 0x6C, 0x3E, + 0x0A, 0x0A, 0x09, 0x09, 0x3C, 0x64, 0x69, 0x76, 0x20, 0x63, + 0x6C, 0x61, 0x73, 0x73, 0x3D, 0x22, 0x67, 0x72, 0x69, 0x64, + 0x20, 0x6D, 0x61, 0x69, 0x6E, 0x22, 0x3E, 0x0A, 0x09, 0x09, + 0x09, 0x3C, 0x68, 0x31, 0x3E, 0x41, 0x62, 0x6F, 0x75, 0x74, + 0x3C, 0x2F, 0x68, 0x31, 0x3E, 0x0A, 0x09, 0x09, 0x09, 0x3C, + 0x70, 0x3E, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x62, 0x61, 0x73, + 0x65, 0x64, 0x20, 0x6F, 0x6E, 0x20, 0x68, 0x74, 0x74, 0x70, + 0x64, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x4C, 0x77, 0x49, + 0x50, 0x2E, 0x3C, 0x2F, 0x70, 0x3E, 0x0A, 0x09, 0x09, 0x09, + 0x3C, 0x70, 0x3E, 0x54, 0x6F, 0x20, 0x65, 0x6E, 0x61, 0x62, + 0x6C, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x69, + 0x6E, 0x67, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x66, 0x6C, 0x61, 0x67, + 0x73, 0x20, 0x2D, 0x44, 0x4C, 0x57, 0x49, 0x50, 0x5F, 0x44, + 0x45, 0x42, 0x55, 0x47, 0x3D, 0x31, 0x20, 0x2D, 0x44, 0x48, + 0x54, 0x54, 0x50, 0x44, 0x5F, 0x44, 0x45, 0x42, 0x55, 0x47, + 0x3D, 0x4C, 0x57, 0x49, 0x50, 0x5F, 0x44, 0x42, 0x47, 0x5F, + 0x4F, 0x4E, 0x2E, 0x3C, 0x2F, 0x70, 0x3E, 0x0A, 0x09, 0x09, + 0x09, 0x3C, 0x70, 0x3E, 0x46, 0x6F, 0x72, 0x20, 0x6D, 0x6F, + 0x72, 0x65, 0x20, 0x69, 0x6E, 0x66, 0x6F, 0x20, 0x73, 0x65, + 0x65, 0x20, 0x3C, 0x61, 0x20, 0x68, 0x72, 0x65, 0x66, 0x3D, + 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, + 0x77, 0x2E, 0x6E, 0x6F, 0x6E, 0x67, 0x6E, 0x75, 0x2E, 0x6F, + 0x72, 0x67, 0x2F, 0x6C, 0x77, 0x69, 0x70, 0x2F, 0x32, 0x5F, + 0x30, 0x5F, 0x30, 0x2F, 0x67, 0x72, 0x6F, 0x75, 0x70, 0x5F, + 0x5F, 0x68, 0x74, 0x74, 0x70, 0x64, 0x2E, 0x68, 0x74, 0x6D, + 0x6C, 0x22, 0x3E, 0x48, 0x54, 0x54, 0x50, 0x20, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x20, 0x64, 0x6F, 0x63, 0x75, 0x6D, + 0x65, 0x6E, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3C, 0x2F, + 0x61, 0x3E, 0x2E, 0x3C, 0x2F, 0x70, 0x3E, 0x0A, 0x09, 0x09, + 0x3C, 0x2F, 0x64, 0x69, 0x76, 0x3E, 0x0A, 0x09, 0x3C, 0x2F, + 0x62, 0x6F, 0x64, 0x79, 0x3E, 0x0A, 0x3C, 0x2F, 0x68, 0x74, + 0x6D, 0x6C, 0x3E, 0x0A, }; + +static const unsigned char data_js_smoothie_min_js[] = { + /* /js/smoothie_min.js */ + 0x2F, 0x6A, 0x73, 0x2F, 0x73, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, 0x5F, 0x6D, 0x69, 0x6E, 0x2E, 0x6A, 0x73, 0, + 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x20, 0x4F, 0x4B, 0x0D, 0x0A, 0x6C, 0x77, 0x49, + 0x50, 0x2F, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x20, 0x28, 0x68, + 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x61, 0x76, 0x61, + 0x6E, 0x6E, 0x61, 0x68, 0x2E, 0x6E, 0x6F, 0x6E, 0x67, 0x6E, + 0x75, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x70, 0x72, 0x6F, 0x6A, + 0x65, 0x63, 0x74, 0x73, 0x2F, 0x6C, 0x77, 0x69, 0x70, 0x29, + 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, + 0x74, 0x79, 0x70, 0x65, 0x3A, 0x20, 0x61, 0x70, 0x70, 0x6C, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x78, 0x2D, + 0x6A, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x0D, 0x0A, 0x0D, 0x0A, 0x0D, 0x0A, 0x2F, 0x2F, 0x20, 0x4D, + 0x49, 0x54, 0x20, 0x4C, 0x69, 0x63, 0x65, 0x6E, 0x73, 0x65, + 0x3A, 0x0A, 0x2F, 0x2F, 0x0A, 0x2F, 0x2F, 0x20, 0x43, 0x6F, + 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, + 0x29, 0x20, 0x32, 0x30, 0x31, 0x30, 0x2D, 0x32, 0x30, 0x31, + 0x33, 0x2C, 0x20, 0x4A, 0x6F, 0x65, 0x20, 0x57, 0x61, 0x6C, + 0x6E, 0x65, 0x73, 0x0A, 0x2F, 0x2F, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x32, 0x30, 0x31, 0x33, 0x2D, 0x32, 0x30, 0x31, 0x34, + 0x2C, 0x20, 0x44, 0x72, 0x65, 0x77, 0x20, 0x4E, 0x6F, 0x61, + 0x6B, 0x65, 0x73, 0x0A, 0x2F, 0x2F, 0x0A, 0x2F, 0x2F, 0x20, + 0x50, 0x65, 0x72, 0x6D, 0x69, 0x73, 0x73, 0x69, 0x6F, 0x6E, + 0x20, 0x69, 0x73, 0x20, 0x68, 0x65, 0x72, 0x65, 0x62, 0x79, + 0x20, 0x67, 0x72, 0x61, 0x6E, 0x74, 0x65, 0x64, 0x2C, 0x20, + 0x66, 0x72, 0x65, 0x65, 0x20, 0x6F, 0x66, 0x20, 0x63, 0x68, + 0x61, 0x72, 0x67, 0x65, 0x2C, 0x20, 0x74, 0x6F, 0x20, 0x61, + 0x6E, 0x79, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6F, 0x6E, 0x20, + 0x6F, 0x62, 0x74, 0x61, 0x69, 0x6E, 0x69, 0x6E, 0x67, 0x20, + 0x61, 0x20, 0x63, 0x6F, 0x70, 0x79, 0x0A, 0x2F, 0x2F, 0x20, + 0x6F, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x6F, + 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x61, 0x6E, 0x64, + 0x20, 0x61, 0x73, 0x73, 0x6F, 0x63, 0x69, 0x61, 0x74, 0x65, + 0x64, 0x20, 0x64, 0x6F, 0x63, 0x75, 0x6D, 0x65, 0x6E, 0x74, + 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x66, 0x69, 0x6C, 0x65, + 0x73, 0x20, 0x28, 0x74, 0x68, 0x65, 0x20, 0x22, 0x53, 0x6F, + 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x22, 0x29, 0x2C, 0x20, + 0x74, 0x6F, 0x20, 0x64, 0x65, 0x61, 0x6C, 0x0A, 0x2F, 0x2F, + 0x20, 0x69, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x6F, + 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x77, 0x69, 0x74, + 0x68, 0x6F, 0x75, 0x74, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, + 0x69, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x2C, 0x20, 0x69, 0x6E, + 0x63, 0x6C, 0x75, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x6F, 0x75, 0x74, 0x20, 0x6C, 0x69, 0x6D, 0x69, + 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x0A, 0x2F, 0x2F, + 0x20, 0x74, 0x6F, 0x20, 0x75, 0x73, 0x65, 0x2C, 0x20, 0x63, + 0x6F, 0x70, 0x79, 0x2C, 0x20, 0x6D, 0x6F, 0x64, 0x69, 0x66, + 0x79, 0x2C, 0x20, 0x6D, 0x65, 0x72, 0x67, 0x65, 0x2C, 0x20, + 0x70, 0x75, 0x62, 0x6C, 0x69, 0x73, 0x68, 0x2C, 0x20, 0x64, + 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x2C, + 0x20, 0x73, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x65, 0x6E, 0x73, + 0x65, 0x2C, 0x20, 0x61, 0x6E, 0x64, 0x2F, 0x6F, 0x72, 0x20, + 0x73, 0x65, 0x6C, 0x6C, 0x0A, 0x2F, 0x2F, 0x20, 0x63, 0x6F, + 0x70, 0x69, 0x65, 0x73, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, + 0x2C, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x70, + 0x65, 0x72, 0x6D, 0x69, 0x74, 0x20, 0x70, 0x65, 0x72, 0x73, + 0x6F, 0x6E, 0x73, 0x20, 0x74, 0x6F, 0x20, 0x77, 0x68, 0x6F, + 0x6D, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x6F, 0x66, 0x74, + 0x77, 0x61, 0x72, 0x65, 0x20, 0x69, 0x73, 0x0A, 0x2F, 0x2F, + 0x20, 0x66, 0x75, 0x72, 0x6E, 0x69, 0x73, 0x68, 0x65, 0x64, + 0x20, 0x74, 0x6F, 0x20, 0x64, 0x6F, 0x20, 0x73, 0x6F, 0x2C, + 0x20, 0x73, 0x75, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x20, 0x74, + 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x6F, 0x6C, 0x6C, + 0x6F, 0x77, 0x69, 0x6E, 0x67, 0x20, 0x63, 0x6F, 0x6E, 0x64, + 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x3A, 0x0A, 0x2F, 0x2F, + 0x0A, 0x2F, 0x2F, 0x20, 0x54, 0x68, 0x65, 0x20, 0x61, 0x62, + 0x6F, 0x76, 0x65, 0x20, 0x63, 0x6F, 0x70, 0x79, 0x72, 0x69, + 0x67, 0x68, 0x74, 0x20, 0x6E, 0x6F, 0x74, 0x69, 0x63, 0x65, + 0x20, 0x61, 0x6E, 0x64, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, + 0x70, 0x65, 0x72, 0x6D, 0x69, 0x73, 0x73, 0x69, 0x6F, 0x6E, + 0x20, 0x6E, 0x6F, 0x74, 0x69, 0x63, 0x65, 0x20, 0x73, 0x68, + 0x61, 0x6C, 0x6C, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6E, 0x63, + 0x6C, 0x75, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6E, 0x0A, 0x2F, + 0x2F, 0x20, 0x61, 0x6C, 0x6C, 0x20, 0x63, 0x6F, 0x70, 0x69, + 0x65, 0x73, 0x20, 0x6F, 0x72, 0x20, 0x73, 0x75, 0x62, 0x73, + 0x74, 0x61, 0x6E, 0x74, 0x69, 0x61, 0x6C, 0x20, 0x70, 0x6F, + 0x72, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x20, 0x6F, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, + 0x72, 0x65, 0x2E, 0x0A, 0x3B, 0x28, 0x66, 0x75, 0x6E, 0x63, + 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x65, 0x78, 0x70, 0x6F, 0x72, + 0x74, 0x73, 0x29, 0x7B, 0x76, 0x61, 0x72, 0x20, 0x55, 0x74, + 0x69, 0x6C, 0x3D, 0x7B, 0x65, 0x78, 0x74, 0x65, 0x6E, 0x64, + 0x3A, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, + 0x29, 0x7B, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, + 0x73, 0x5B, 0x30, 0x5D, 0x3D, 0x61, 0x72, 0x67, 0x75, 0x6D, + 0x65, 0x6E, 0x74, 0x73, 0x5B, 0x30, 0x5D, 0x7C, 0x7C, 0x7B, + 0x7D, 0x3B, 0x66, 0x6F, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, + 0x69, 0x3D, 0x31, 0x3B, 0x69, 0x3C, 0x61, 0x72, 0x67, 0x75, + 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x2E, 0x6C, 0x65, 0x6E, 0x67, + 0x74, 0x68, 0x3B, 0x69, 0x2B, 0x3D, 0x31, 0x29, 0x7B, 0x66, + 0x6F, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x6B, 0x65, 0x79, + 0x20, 0x69, 0x6E, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, + 0x6E, 0x74, 0x73, 0x5B, 0x69, 0x5D, 0x29, 0x7B, 0x69, 0x66, + 0x28, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, + 0x5B, 0x69, 0x5D, 0x2E, 0x68, 0x61, 0x73, 0x4F, 0x77, 0x6E, + 0x50, 0x72, 0x6F, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x6B, + 0x65, 0x79, 0x29, 0x29, 0x7B, 0x69, 0x66, 0x28, 0x74, 0x79, + 0x70, 0x65, 0x6F, 0x66, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6D, + 0x65, 0x6E, 0x74, 0x73, 0x5B, 0x69, 0x5D, 0x5B, 0x6B, 0x65, + 0x79, 0x5D, 0x29, 0x3D, 0x3D, 0x3D, 0x27, 0x6F, 0x62, 0x6A, + 0x65, 0x63, 0x74, 0x27, 0x29, 0x7B, 0x69, 0x66, 0x28, 0x61, + 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x5B, 0x69, + 0x5D, 0x5B, 0x6B, 0x65, 0x79, 0x5D, 0x69, 0x6E, 0x73, 0x74, + 0x61, 0x6E, 0x63, 0x65, 0x6F, 0x66, 0x20, 0x41, 0x72, 0x72, + 0x61, 0x79, 0x29, 0x7B, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, + 0x6E, 0x74, 0x73, 0x5B, 0x30, 0x5D, 0x5B, 0x6B, 0x65, 0x79, + 0x5D, 0x3D, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, + 0x73, 0x5B, 0x69, 0x5D, 0x5B, 0x6B, 0x65, 0x79, 0x5D, 0x7D, + 0x65, 0x6C, 0x73, 0x65, 0x7B, 0x61, 0x72, 0x67, 0x75, 0x6D, + 0x65, 0x6E, 0x74, 0x73, 0x5B, 0x30, 0x5D, 0x5B, 0x6B, 0x65, + 0x79, 0x5D, 0x3D, 0x55, 0x74, 0x69, 0x6C, 0x2E, 0x65, 0x78, + 0x74, 0x65, 0x6E, 0x64, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6D, + 0x65, 0x6E, 0x74, 0x73, 0x5B, 0x30, 0x5D, 0x5B, 0x6B, 0x65, + 0x79, 0x5D, 0x2C, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, + 0x74, 0x73, 0x5B, 0x69, 0x5D, 0x5B, 0x6B, 0x65, 0x79, 0x5D, + 0x29, 0x7D, 0x7D, 0x65, 0x6C, 0x73, 0x65, 0x7B, 0x61, 0x72, + 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x5B, 0x30, 0x5D, + 0x5B, 0x6B, 0x65, 0x79, 0x5D, 0x3D, 0x61, 0x72, 0x67, 0x75, + 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x5B, 0x69, 0x5D, 0x5B, 0x6B, + 0x65, 0x79, 0x5D, 0x7D, 0x7D, 0x7D, 0x7D, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6E, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6D, 0x65, + 0x6E, 0x74, 0x73, 0x5B, 0x30, 0x5D, 0x7D, 0x7D, 0x3B, 0x66, + 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x54, 0x69, + 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x28, 0x6F, + 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x29, 0x7B, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, + 0x3D, 0x55, 0x74, 0x69, 0x6C, 0x2E, 0x65, 0x78, 0x74, 0x65, + 0x6E, 0x64, 0x28, 0x7B, 0x7D, 0x2C, 0x54, 0x69, 0x6D, 0x65, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6C, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2C, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x29, + 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x63, 0x6C, 0x65, 0x61, + 0x72, 0x28, 0x29, 0x7D, 0x54, 0x69, 0x6D, 0x65, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x2E, 0x64, 0x65, 0x66, 0x61, 0x75, + 0x6C, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x3D, + 0x7B, 0x72, 0x65, 0x73, 0x65, 0x74, 0x42, 0x6F, 0x75, 0x6E, + 0x64, 0x73, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6C, + 0x3A, 0x33, 0x30, 0x30, 0x30, 0x2C, 0x72, 0x65, 0x73, 0x65, + 0x74, 0x42, 0x6F, 0x75, 0x6E, 0x64, 0x73, 0x3A, 0x74, 0x72, + 0x75, 0x65, 0x7D, 0x3B, 0x54, 0x69, 0x6D, 0x65, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x2E, 0x70, 0x72, 0x6F, 0x74, 0x6F, + 0x74, 0x79, 0x70, 0x65, 0x2E, 0x63, 0x6C, 0x65, 0x61, 0x72, + 0x3D, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, + 0x29, 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, 0x61, 0x74, + 0x61, 0x3D, 0x5B, 0x5D, 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x6D, 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x4E, + 0x75, 0x6D, 0x62, 0x65, 0x72, 0x2E, 0x4E, 0x61, 0x4E, 0x3B, + 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6D, 0x69, 0x6E, 0x56, 0x61, + 0x6C, 0x75, 0x65, 0x3D, 0x4E, 0x75, 0x6D, 0x62, 0x65, 0x72, + 0x2E, 0x4E, 0x61, 0x4E, 0x3B, 0x7D, 0x3B, 0x54, 0x69, 0x6D, + 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x70, 0x72, + 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2E, 0x72, 0x65, + 0x73, 0x65, 0x74, 0x42, 0x6F, 0x75, 0x6E, 0x64, 0x73, 0x3D, + 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, + 0x7B, 0x69, 0x66, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, + 0x61, 0x74, 0x61, 0x2E, 0x6C, 0x65, 0x6E, 0x67, 0x74, 0x68, + 0x29, 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6D, 0x61, 0x78, + 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x64, 0x61, 0x74, 0x61, 0x5B, 0x30, 0x5D, 0x5B, 0x31, + 0x5D, 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6D, 0x69, 0x6E, + 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x64, 0x61, 0x74, 0x61, 0x5B, 0x30, 0x5D, 0x5B, 0x31, + 0x5D, 0x3B, 0x66, 0x6F, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, + 0x69, 0x3D, 0x31, 0x3B, 0x69, 0x3C, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x64, 0x61, 0x74, 0x61, 0x2E, 0x6C, 0x65, 0x6E, 0x67, + 0x74, 0x68, 0x3B, 0x69, 0x2B, 0x3D, 0x31, 0x29, 0x7B, 0x76, + 0x61, 0x72, 0x20, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x74, + 0x68, 0x69, 0x73, 0x2E, 0x64, 0x61, 0x74, 0x61, 0x5B, 0x69, + 0x5D, 0x5B, 0x31, 0x5D, 0x3B, 0x69, 0x66, 0x28, 0x76, 0x61, + 0x6C, 0x75, 0x65, 0x3E, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6D, + 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x29, 0x7B, 0x74, + 0x68, 0x69, 0x73, 0x2E, 0x6D, 0x61, 0x78, 0x56, 0x61, 0x6C, + 0x75, 0x65, 0x3D, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x7D, 0x69, + 0x66, 0x28, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3C, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x6D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, + 0x65, 0x29, 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6D, 0x69, + 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x76, 0x61, 0x6C, + 0x75, 0x65, 0x7D, 0x7D, 0x7D, 0x65, 0x6C, 0x73, 0x65, 0x7B, + 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6D, 0x61, 0x78, 0x56, 0x61, + 0x6C, 0x75, 0x65, 0x3D, 0x4E, 0x75, 0x6D, 0x62, 0x65, 0x72, + 0x2E, 0x4E, 0x61, 0x4E, 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x6D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x4E, + 0x75, 0x6D, 0x62, 0x65, 0x72, 0x2E, 0x4E, 0x61, 0x4E, 0x7D, + 0x7D, 0x3B, 0x54, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x2E, 0x70, 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, + 0x70, 0x65, 0x2E, 0x61, 0x70, 0x70, 0x65, 0x6E, 0x64, 0x3D, + 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x74, + 0x69, 0x6D, 0x65, 0x73, 0x74, 0x61, 0x6D, 0x70, 0x2C, 0x76, + 0x61, 0x6C, 0x75, 0x65, 0x2C, 0x73, 0x75, 0x6D, 0x52, 0x65, + 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6D, 0x65, + 0x53, 0x74, 0x61, 0x6D, 0x70, 0x56, 0x61, 0x6C, 0x75, 0x65, + 0x73, 0x29, 0x7B, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3D, 0x74, + 0x68, 0x69, 0x73, 0x2E, 0x64, 0x61, 0x74, 0x61, 0x2E, 0x6C, + 0x65, 0x6E, 0x67, 0x74, 0x68, 0x2D, 0x31, 0x3B, 0x77, 0x68, + 0x69, 0x6C, 0x65, 0x28, 0x69, 0x3E, 0x3D, 0x30, 0x26, 0x26, + 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, 0x61, 0x74, 0x61, 0x5B, + 0x69, 0x5D, 0x5B, 0x30, 0x5D, 0x3E, 0x74, 0x69, 0x6D, 0x65, + 0x73, 0x74, 0x61, 0x6D, 0x70, 0x29, 0x7B, 0x69, 0x2D, 0x3D, + 0x31, 0x7D, 0x69, 0x66, 0x28, 0x69, 0x3D, 0x3D, 0x3D, 0x2D, + 0x31, 0x29, 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, 0x61, + 0x74, 0x61, 0x2E, 0x73, 0x70, 0x6C, 0x69, 0x63, 0x65, 0x28, + 0x30, 0x2C, 0x30, 0x2C, 0x5B, 0x74, 0x69, 0x6D, 0x65, 0x73, + 0x74, 0x61, 0x6D, 0x70, 0x2C, 0x76, 0x61, 0x6C, 0x75, 0x65, + 0x5D, 0x29, 0x7D, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x69, 0x66, + 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, 0x61, 0x74, 0x61, + 0x2E, 0x6C, 0x65, 0x6E, 0x67, 0x74, 0x68, 0x3E, 0x30, 0x26, + 0x26, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, 0x61, 0x74, 0x61, + 0x5B, 0x69, 0x5D, 0x5B, 0x30, 0x5D, 0x3D, 0x3D, 0x3D, 0x74, + 0x69, 0x6D, 0x65, 0x73, 0x74, 0x61, 0x6D, 0x70, 0x29, 0x7B, + 0x69, 0x66, 0x28, 0x73, 0x75, 0x6D, 0x52, 0x65, 0x70, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6D, 0x65, 0x53, 0x74, + 0x61, 0x6D, 0x70, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x73, 0x29, + 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, 0x61, 0x74, 0x61, + 0x5B, 0x69, 0x5D, 0x5B, 0x31, 0x5D, 0x2B, 0x3D, 0x76, 0x61, + 0x6C, 0x75, 0x65, 0x3B, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3D, + 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, 0x61, 0x74, 0x61, 0x5B, + 0x69, 0x5D, 0x5B, 0x31, 0x5D, 0x7D, 0x65, 0x6C, 0x73, 0x65, + 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, 0x61, 0x74, 0x61, + 0x5B, 0x69, 0x5D, 0x5B, 0x31, 0x5D, 0x3D, 0x76, 0x61, 0x6C, + 0x75, 0x65, 0x7D, 0x7D, 0x65, 0x6C, 0x73, 0x65, 0x20, 0x69, + 0x66, 0x28, 0x69, 0x3C, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, + 0x61, 0x74, 0x61, 0x2E, 0x6C, 0x65, 0x6E, 0x67, 0x74, 0x68, + 0x2D, 0x31, 0x29, 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, + 0x61, 0x74, 0x61, 0x2E, 0x73, 0x70, 0x6C, 0x69, 0x63, 0x65, + 0x28, 0x69, 0x2B, 0x31, 0x2C, 0x30, 0x2C, 0x5B, 0x74, 0x69, + 0x6D, 0x65, 0x73, 0x74, 0x61, 0x6D, 0x70, 0x2C, 0x76, 0x61, + 0x6C, 0x75, 0x65, 0x5D, 0x29, 0x7D, 0x65, 0x6C, 0x73, 0x65, + 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, 0x61, 0x74, 0x61, + 0x2E, 0x70, 0x75, 0x73, 0x68, 0x28, 0x5B, 0x74, 0x69, 0x6D, + 0x65, 0x73, 0x74, 0x61, 0x6D, 0x70, 0x2C, 0x76, 0x61, 0x6C, + 0x75, 0x65, 0x5D, 0x29, 0x7D, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x6D, 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x69, + 0x73, 0x4E, 0x61, 0x4E, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x6D, 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x29, 0x3F, + 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3A, 0x4D, 0x61, 0x74, 0x68, + 0x2E, 0x6D, 0x61, 0x78, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x6D, 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x2C, 0x76, + 0x61, 0x6C, 0x75, 0x65, 0x29, 0x3B, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x6D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3D, + 0x69, 0x73, 0x4E, 0x61, 0x4E, 0x28, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x6D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x29, + 0x3F, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3A, 0x4D, 0x61, 0x74, + 0x68, 0x2E, 0x6D, 0x69, 0x6E, 0x28, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x6D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x2C, + 0x76, 0x61, 0x6C, 0x75, 0x65, 0x29, 0x7D, 0x3B, 0x54, 0x69, + 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x70, + 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2E, 0x64, + 0x72, 0x6F, 0x70, 0x4F, 0x6C, 0x64, 0x44, 0x61, 0x74, 0x61, + 0x3D, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, + 0x6F, 0x6C, 0x64, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6C, 0x69, + 0x64, 0x54, 0x69, 0x6D, 0x65, 0x2C, 0x6D, 0x61, 0x78, 0x44, + 0x61, 0x74, 0x61, 0x53, 0x65, 0x74, 0x4C, 0x65, 0x6E, 0x67, + 0x74, 0x68, 0x29, 0x7B, 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, + 0x6D, 0x6F, 0x76, 0x65, 0x43, 0x6F, 0x75, 0x6E, 0x74, 0x3D, + 0x30, 0x3B, 0x77, 0x68, 0x69, 0x6C, 0x65, 0x28, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x64, 0x61, 0x74, 0x61, 0x2E, 0x6C, 0x65, + 0x6E, 0x67, 0x74, 0x68, 0x2D, 0x72, 0x65, 0x6D, 0x6F, 0x76, + 0x65, 0x43, 0x6F, 0x75, 0x6E, 0x74, 0x3E, 0x3D, 0x6D, 0x61, + 0x78, 0x44, 0x61, 0x74, 0x61, 0x53, 0x65, 0x74, 0x4C, 0x65, + 0x6E, 0x67, 0x74, 0x68, 0x26, 0x26, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x64, 0x61, 0x74, 0x61, 0x5B, 0x72, 0x65, 0x6D, 0x6F, + 0x76, 0x65, 0x43, 0x6F, 0x75, 0x6E, 0x74, 0x2B, 0x31, 0x5D, + 0x5B, 0x30, 0x5D, 0x3C, 0x6F, 0x6C, 0x64, 0x65, 0x73, 0x74, + 0x56, 0x61, 0x6C, 0x69, 0x64, 0x54, 0x69, 0x6D, 0x65, 0x29, + 0x7B, 0x72, 0x65, 0x6D, 0x6F, 0x76, 0x65, 0x43, 0x6F, 0x75, + 0x6E, 0x74, 0x2B, 0x3D, 0x31, 0x7D, 0x69, 0x66, 0x28, 0x72, + 0x65, 0x6D, 0x6F, 0x76, 0x65, 0x43, 0x6F, 0x75, 0x6E, 0x74, + 0x21, 0x3D, 0x3D, 0x30, 0x29, 0x7B, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x64, 0x61, 0x74, 0x61, 0x2E, 0x73, 0x70, 0x6C, 0x69, + 0x63, 0x65, 0x28, 0x30, 0x2C, 0x72, 0x65, 0x6D, 0x6F, 0x76, + 0x65, 0x43, 0x6F, 0x75, 0x6E, 0x74, 0x29, 0x7D, 0x7D, 0x3B, + 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x53, + 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, 0x61, + 0x72, 0x74, 0x28, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, + 0x29, 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6F, 0x70, 0x74, + 0x69, 0x6F, 0x6E, 0x73, 0x3D, 0x55, 0x74, 0x69, 0x6C, 0x2E, + 0x65, 0x78, 0x74, 0x65, 0x6E, 0x64, 0x28, 0x7B, 0x7D, 0x2C, + 0x53, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, + 0x61, 0x72, 0x74, 0x2E, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, + 0x74, 0x43, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, + 0x6F, 0x6E, 0x73, 0x2C, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x29, 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x73, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x53, 0x65, 0x74, 0x3D, 0x5B, 0x5D, + 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6E, 0x74, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x52, 0x61, + 0x6E, 0x67, 0x65, 0x3D, 0x31, 0x3B, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x56, 0x69, + 0x73, 0x4D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3D, + 0x30, 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6C, 0x61, 0x73, + 0x74, 0x52, 0x65, 0x6E, 0x64, 0x65, 0x72, 0x54, 0x69, 0x6D, + 0x65, 0x4D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x3D, 0x30, 0x7D, + 0x53, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, + 0x61, 0x72, 0x74, 0x2E, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, + 0x74, 0x43, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, + 0x6F, 0x6E, 0x73, 0x3D, 0x7B, 0x6D, 0x69, 0x6C, 0x6C, 0x69, + 0x73, 0x50, 0x65, 0x72, 0x50, 0x69, 0x78, 0x65, 0x6C, 0x3A, + 0x32, 0x30, 0x2C, 0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x44, + 0x70, 0x69, 0x53, 0x63, 0x61, 0x6C, 0x69, 0x6E, 0x67, 0x3A, + 0x74, 0x72, 0x75, 0x65, 0x2C, 0x79, 0x4D, 0x69, 0x6E, 0x46, + 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x74, 0x65, 0x72, 0x3A, 0x66, + 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x6D, 0x69, + 0x6E, 0x2C, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, + 0x6E, 0x29, 0x7B, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x46, 0x6C, 0x6F, 0x61, 0x74, + 0x28, 0x6D, 0x69, 0x6E, 0x29, 0x2E, 0x74, 0x6F, 0x46, 0x69, + 0x78, 0x65, 0x64, 0x28, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, + 0x69, 0x6F, 0x6E, 0x29, 0x7D, 0x2C, 0x79, 0x4D, 0x61, 0x78, + 0x46, 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x74, 0x65, 0x72, 0x3A, + 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x6D, + 0x61, 0x78, 0x2C, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, + 0x6F, 0x6E, 0x29, 0x7B, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, + 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x46, 0x6C, 0x6F, 0x61, + 0x74, 0x28, 0x6D, 0x61, 0x78, 0x29, 0x2E, 0x74, 0x6F, 0x46, + 0x69, 0x78, 0x65, 0x64, 0x28, 0x70, 0x72, 0x65, 0x63, 0x69, + 0x73, 0x69, 0x6F, 0x6E, 0x29, 0x7D, 0x2C, 0x6D, 0x61, 0x78, + 0x56, 0x61, 0x6C, 0x75, 0x65, 0x53, 0x63, 0x61, 0x6C, 0x65, + 0x3A, 0x31, 0x2C, 0x6D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, + 0x65, 0x53, 0x63, 0x61, 0x6C, 0x65, 0x3A, 0x31, 0x2C, 0x69, + 0x6E, 0x74, 0x65, 0x72, 0x70, 0x6F, 0x6C, 0x61, 0x74, 0x69, + 0x6F, 0x6E, 0x3A, 0x27, 0x62, 0x65, 0x7A, 0x69, 0x65, 0x72, + 0x27, 0x2C, 0x73, 0x63, 0x61, 0x6C, 0x65, 0x53, 0x6D, 0x6F, + 0x6F, 0x74, 0x68, 0x69, 0x6E, 0x67, 0x3A, 0x30, 0x2E, 0x31, + 0x32, 0x35, 0x2C, 0x6D, 0x61, 0x78, 0x44, 0x61, 0x74, 0x61, + 0x53, 0x65, 0x74, 0x4C, 0x65, 0x6E, 0x67, 0x74, 0x68, 0x3A, + 0x32, 0x2C, 0x73, 0x63, 0x72, 0x6F, 0x6C, 0x6C, 0x42, 0x61, + 0x63, 0x6B, 0x77, 0x61, 0x72, 0x64, 0x73, 0x3A, 0x66, 0x61, + 0x6C, 0x73, 0x65, 0x2C, 0x67, 0x72, 0x69, 0x64, 0x3A, 0x7B, + 0x66, 0x69, 0x6C, 0x6C, 0x53, 0x74, 0x79, 0x6C, 0x65, 0x3A, + 0x27, 0x23, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x27, 0x2C, + 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x53, 0x74, 0x79, 0x6C, + 0x65, 0x3A, 0x27, 0x23, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x27, 0x2C, 0x6C, 0x69, 0x6E, 0x65, 0x57, 0x69, 0x64, 0x74, + 0x68, 0x3A, 0x31, 0x2C, 0x73, 0x68, 0x61, 0x72, 0x70, 0x4C, + 0x69, 0x6E, 0x65, 0x73, 0x3A, 0x66, 0x61, 0x6C, 0x73, 0x65, + 0x2C, 0x6D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x50, 0x65, 0x72, + 0x4C, 0x69, 0x6E, 0x65, 0x3A, 0x31, 0x30, 0x30, 0x30, 0x2C, + 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6C, 0x53, 0x65, + 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x3A, 0x32, 0x2C, 0x62, + 0x6F, 0x72, 0x64, 0x65, 0x72, 0x56, 0x69, 0x73, 0x69, 0x62, + 0x6C, 0x65, 0x3A, 0x74, 0x72, 0x75, 0x65, 0x7D, 0x2C, 0x6C, + 0x61, 0x62, 0x65, 0x6C, 0x73, 0x3A, 0x7B, 0x66, 0x69, 0x6C, + 0x6C, 0x53, 0x74, 0x79, 0x6C, 0x65, 0x3A, 0x27, 0x23, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x27, 0x2C, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6C, 0x65, 0x64, 0x3A, 0x66, 0x61, 0x6C, 0x73, + 0x65, 0x2C, 0x66, 0x6F, 0x6E, 0x74, 0x53, 0x69, 0x7A, 0x65, + 0x3A, 0x31, 0x30, 0x2C, 0x66, 0x6F, 0x6E, 0x74, 0x46, 0x61, + 0x6D, 0x69, 0x6C, 0x79, 0x3A, 0x27, 0x6D, 0x6F, 0x6E, 0x6F, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x27, 0x2C, 0x70, 0x72, 0x65, + 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x32, 0x7D, 0x2C, + 0x68, 0x6F, 0x72, 0x69, 0x7A, 0x6F, 0x6E, 0x74, 0x61, 0x6C, + 0x4C, 0x69, 0x6E, 0x65, 0x73, 0x3A, 0x5B, 0x5D, 0x7D, 0x3B, + 0x53, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, + 0x61, 0x72, 0x74, 0x2E, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, + 0x65, 0x43, 0x6F, 0x6D, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, + 0x6C, 0x69, 0x74, 0x79, 0x3D, 0x28, 0x66, 0x75, 0x6E, 0x63, + 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x7B, 0x76, 0x61, 0x72, + 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x6E, + 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x46, 0x72, 0x61, + 0x6D, 0x65, 0x3D, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, + 0x6E, 0x28, 0x63, 0x61, 0x6C, 0x6C, 0x62, 0x61, 0x63, 0x6B, + 0x2C, 0x65, 0x6C, 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x29, 0x7B, + 0x76, 0x61, 0x72, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, + 0x46, 0x72, 0x61, 0x6D, 0x65, 0x3D, 0x77, 0x69, 0x6E, 0x64, + 0x6F, 0x77, 0x2E, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x46, + 0x72, 0x61, 0x6D, 0x65, 0x7C, 0x7C, 0x77, 0x69, 0x6E, 0x64, + 0x6F, 0x77, 0x2E, 0x77, 0x65, 0x62, 0x6B, 0x69, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x6E, 0x69, 0x6D, + 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x46, 0x72, 0x61, 0x6D, 0x65, + 0x7C, 0x7C, 0x77, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x2E, 0x6D, + 0x6F, 0x7A, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, + 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x46, 0x72, + 0x61, 0x6D, 0x65, 0x7C, 0x7C, 0x77, 0x69, 0x6E, 0x64, 0x6F, + 0x77, 0x2E, 0x6F, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x46, + 0x72, 0x61, 0x6D, 0x65, 0x7C, 0x7C, 0x77, 0x69, 0x6E, 0x64, + 0x6F, 0x77, 0x2E, 0x6D, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, + 0x6E, 0x46, 0x72, 0x61, 0x6D, 0x65, 0x7C, 0x7C, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x63, 0x61, 0x6C, + 0x6C, 0x62, 0x61, 0x63, 0x6B, 0x29, 0x7B, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6E, 0x20, 0x77, 0x69, 0x6E, 0x64, 0x6F, 0x77, + 0x2E, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, + 0x74, 0x28, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, + 0x28, 0x29, 0x7B, 0x63, 0x61, 0x6C, 0x6C, 0x62, 0x61, 0x63, + 0x6B, 0x28, 0x6E, 0x65, 0x77, 0x20, 0x44, 0x61, 0x74, 0x65, + 0x28, 0x29, 0x2E, 0x67, 0x65, 0x74, 0x54, 0x69, 0x6D, 0x65, + 0x28, 0x29, 0x29, 0x7D, 0x2C, 0x31, 0x36, 0x29, 0x7D, 0x3B, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, + 0x69, 0x6F, 0x6E, 0x46, 0x72, 0x61, 0x6D, 0x65, 0x2E, 0x63, + 0x61, 0x6C, 0x6C, 0x28, 0x77, 0x69, 0x6E, 0x64, 0x6F, 0x77, + 0x2C, 0x63, 0x61, 0x6C, 0x6C, 0x62, 0x61, 0x63, 0x6B, 0x2C, + 0x65, 0x6C, 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x29, 0x7D, 0x2C, + 0x63, 0x61, 0x6E, 0x63, 0x65, 0x6C, 0x41, 0x6E, 0x69, 0x6D, + 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x46, 0x72, 0x61, 0x6D, 0x65, + 0x3D, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, + 0x69, 0x64, 0x29, 0x7B, 0x76, 0x61, 0x72, 0x20, 0x63, 0x61, + 0x6E, 0x63, 0x65, 0x6C, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, + 0x69, 0x6F, 0x6E, 0x46, 0x72, 0x61, 0x6D, 0x65, 0x3D, 0x77, + 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x2E, 0x63, 0x61, 0x6E, 0x63, + 0x65, 0x6C, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, + 0x6E, 0x46, 0x72, 0x61, 0x6D, 0x65, 0x7C, 0x7C, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x69, 0x64, 0x29, + 0x7B, 0x63, 0x6C, 0x65, 0x61, 0x72, 0x54, 0x69, 0x6D, 0x65, + 0x6F, 0x75, 0x74, 0x28, 0x69, 0x64, 0x29, 0x7D, 0x3B, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x63, 0x61, 0x6E, 0x63, + 0x65, 0x6C, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, + 0x6E, 0x46, 0x72, 0x61, 0x6D, 0x65, 0x2E, 0x63, 0x61, 0x6C, + 0x6C, 0x28, 0x77, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x2C, 0x69, + 0x64, 0x29, 0x7D, 0x3B, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, + 0x7B, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x6E, + 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x46, 0x72, 0x61, + 0x6D, 0x65, 0x3A, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x46, + 0x72, 0x61, 0x6D, 0x65, 0x2C, 0x63, 0x61, 0x6E, 0x63, 0x65, + 0x6C, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, + 0x46, 0x72, 0x61, 0x6D, 0x65, 0x3A, 0x63, 0x61, 0x6E, 0x63, + 0x65, 0x6C, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, + 0x6E, 0x46, 0x72, 0x61, 0x6D, 0x65, 0x7D, 0x7D, 0x29, 0x28, + 0x29, 0x3B, 0x53, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, + 0x43, 0x68, 0x61, 0x72, 0x74, 0x2E, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6C, 0x74, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x50, + 0x72, 0x65, 0x73, 0x65, 0x6E, 0x74, 0x61, 0x74, 0x69, 0x6F, + 0x6E, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x3D, 0x7B, + 0x6C, 0x69, 0x6E, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x3A, + 0x31, 0x2C, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x53, 0x74, + 0x79, 0x6C, 0x65, 0x3A, 0x27, 0x23, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x27, 0x7D, 0x3B, 0x53, 0x6D, 0x6F, 0x6F, 0x74, + 0x68, 0x69, 0x65, 0x43, 0x68, 0x61, 0x72, 0x74, 0x2E, 0x70, + 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2E, 0x61, + 0x64, 0x64, 0x54, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x3D, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, + 0x6E, 0x28, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x2C, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, + 0x29, 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x73, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x53, 0x65, 0x74, 0x2E, 0x70, 0x75, 0x73, + 0x68, 0x28, 0x7B, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x3A, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x2C, 0x6F, 0x70, 0x74, 0x69, 0x6F, + 0x6E, 0x73, 0x3A, 0x55, 0x74, 0x69, 0x6C, 0x2E, 0x65, 0x78, + 0x74, 0x65, 0x6E, 0x64, 0x28, 0x7B, 0x7D, 0x2C, 0x53, 0x6D, + 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, 0x61, 0x72, + 0x74, 0x2E, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x53, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x50, 0x72, 0x65, 0x73, 0x65, + 0x6E, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x4F, 0x70, 0x74, + 0x69, 0x6F, 0x6E, 0x73, 0x2C, 0x6F, 0x70, 0x74, 0x69, 0x6F, + 0x6E, 0x73, 0x29, 0x7D, 0x29, 0x3B, 0x69, 0x66, 0x28, 0x74, + 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, + 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x72, 0x65, + 0x73, 0x65, 0x74, 0x42, 0x6F, 0x75, 0x6E, 0x64, 0x73, 0x26, + 0x26, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x2E, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, + 0x72, 0x65, 0x73, 0x65, 0x74, 0x42, 0x6F, 0x75, 0x6E, 0x64, + 0x73, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6C, 0x3E, + 0x30, 0x29, 0x7B, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x2E, 0x72, 0x65, 0x73, 0x65, 0x74, 0x42, + 0x6F, 0x75, 0x6E, 0x64, 0x73, 0x54, 0x69, 0x6D, 0x65, 0x72, + 0x49, 0x64, 0x3D, 0x73, 0x65, 0x74, 0x49, 0x6E, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6C, 0x28, 0x66, 0x75, 0x6E, 0x63, 0x74, + 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x7B, 0x74, 0x69, 0x6D, 0x65, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x72, 0x65, 0x73, + 0x65, 0x74, 0x42, 0x6F, 0x75, 0x6E, 0x64, 0x73, 0x28, 0x29, + 0x7D, 0x2C, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x2E, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, + 0x2E, 0x72, 0x65, 0x73, 0x65, 0x74, 0x42, 0x6F, 0x75, 0x6E, + 0x64, 0x73, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6C, + 0x29, 0x7D, 0x7D, 0x3B, 0x53, 0x6D, 0x6F, 0x6F, 0x74, 0x68, + 0x69, 0x65, 0x43, 0x68, 0x61, 0x72, 0x74, 0x2E, 0x70, 0x72, + 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2E, 0x72, 0x65, + 0x6D, 0x6F, 0x76, 0x65, 0x54, 0x69, 0x6D, 0x65, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x3D, 0x66, 0x75, 0x6E, 0x63, 0x74, + 0x69, 0x6F, 0x6E, 0x28, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x29, 0x7B, 0x76, 0x61, 0x72, 0x20, + 0x6E, 0x75, 0x6D, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x3D, + 0x74, 0x68, 0x69, 0x73, 0x2E, 0x73, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x53, 0x65, 0x74, 0x2E, 0x6C, 0x65, 0x6E, 0x67, 0x74, + 0x68, 0x3B, 0x66, 0x6F, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, + 0x69, 0x3D, 0x30, 0x3B, 0x69, 0x3C, 0x6E, 0x75, 0x6D, 0x53, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x3B, 0x69, 0x2B, 0x3D, 0x31, + 0x29, 0x7B, 0x69, 0x66, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x53, 0x65, 0x74, 0x5B, + 0x69, 0x5D, 0x2E, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x3D, 0x3D, 0x3D, 0x74, 0x69, 0x6D, 0x65, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x29, 0x7B, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x53, + 0x65, 0x74, 0x2E, 0x73, 0x70, 0x6C, 0x69, 0x63, 0x65, 0x28, + 0x69, 0x2C, 0x31, 0x29, 0x3B, 0x62, 0x72, 0x65, 0x61, 0x6B, + 0x7D, 0x7D, 0x69, 0x66, 0x28, 0x74, 0x69, 0x6D, 0x65, 0x53, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x72, 0x65, 0x73, 0x65, + 0x74, 0x42, 0x6F, 0x75, 0x6E, 0x64, 0x73, 0x54, 0x69, 0x6D, + 0x65, 0x72, 0x49, 0x64, 0x29, 0x7B, 0x63, 0x6C, 0x65, 0x61, + 0x72, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6C, 0x28, + 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x2E, 0x72, 0x65, 0x73, 0x65, 0x74, 0x42, 0x6F, 0x75, 0x6E, + 0x64, 0x73, 0x54, 0x69, 0x6D, 0x65, 0x72, 0x49, 0x64, 0x29, + 0x7D, 0x7D, 0x3B, 0x53, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, + 0x65, 0x43, 0x68, 0x61, 0x72, 0x74, 0x2E, 0x70, 0x72, 0x6F, + 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2E, 0x67, 0x65, 0x74, + 0x54, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x3D, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x74, 0x69, 0x6D, + 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x29, 0x7B, 0x76, + 0x61, 0x72, 0x20, 0x6E, 0x75, 0x6D, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x3D, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x73, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x53, 0x65, 0x74, 0x2E, 0x6C, 0x65, + 0x6E, 0x67, 0x74, 0x68, 0x3B, 0x66, 0x6F, 0x72, 0x28, 0x76, + 0x61, 0x72, 0x20, 0x69, 0x3D, 0x30, 0x3B, 0x69, 0x3C, 0x6E, + 0x75, 0x6D, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x3B, 0x69, + 0x2B, 0x3D, 0x31, 0x29, 0x7B, 0x69, 0x66, 0x28, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x53, + 0x65, 0x74, 0x5B, 0x69, 0x5D, 0x2E, 0x74, 0x69, 0x6D, 0x65, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x3D, 0x3D, 0x3D, 0x74, + 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x29, + 0x7B, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x53, + 0x65, 0x74, 0x5B, 0x69, 0x5D, 0x2E, 0x6F, 0x70, 0x74, 0x69, + 0x6F, 0x6E, 0x73, 0x7D, 0x7D, 0x7D, 0x3B, 0x53, 0x6D, 0x6F, + 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, 0x61, 0x72, 0x74, + 0x2E, 0x70, 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, + 0x2E, 0x62, 0x72, 0x69, 0x6E, 0x67, 0x54, 0x6F, 0x46, 0x72, + 0x6F, 0x6E, 0x74, 0x3D, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, + 0x6F, 0x6E, 0x28, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x29, 0x7B, 0x76, 0x61, 0x72, 0x20, 0x6E, + 0x75, 0x6D, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x3D, 0x74, + 0x68, 0x69, 0x73, 0x2E, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x53, 0x65, 0x74, 0x2E, 0x6C, 0x65, 0x6E, 0x67, 0x74, 0x68, + 0x3B, 0x66, 0x6F, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, + 0x3D, 0x30, 0x3B, 0x69, 0x3C, 0x6E, 0x75, 0x6D, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x3B, 0x69, 0x2B, 0x3D, 0x31, 0x29, + 0x7B, 0x69, 0x66, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x73, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x53, 0x65, 0x74, 0x5B, 0x69, + 0x5D, 0x2E, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x3D, 0x3D, 0x3D, 0x74, 0x69, 0x6D, 0x65, 0x53, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x29, 0x7B, 0x76, 0x61, 0x72, + 0x20, 0x73, 0x65, 0x74, 0x3D, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x53, 0x65, 0x74, 0x2E, + 0x73, 0x70, 0x6C, 0x69, 0x63, 0x65, 0x28, 0x69, 0x2C, 0x31, + 0x29, 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x73, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x53, 0x65, 0x74, 0x2E, 0x70, 0x75, 0x73, + 0x68, 0x28, 0x73, 0x65, 0x74, 0x5B, 0x30, 0x5D, 0x29, 0x3B, + 0x62, 0x72, 0x65, 0x61, 0x6B, 0x7D, 0x7D, 0x7D, 0x3B, 0x53, + 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, 0x61, + 0x72, 0x74, 0x2E, 0x70, 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, + 0x70, 0x65, 0x2E, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x54, + 0x6F, 0x3D, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, + 0x28, 0x63, 0x61, 0x6E, 0x76, 0x61, 0x73, 0x2C, 0x64, 0x65, + 0x6C, 0x61, 0x79, 0x4D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x29, + 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x63, 0x61, 0x6E, 0x76, + 0x61, 0x73, 0x3D, 0x63, 0x61, 0x6E, 0x76, 0x61, 0x73, 0x3B, + 0x74, 0x68, 0x69, 0x73, 0x2E, 0x64, 0x65, 0x6C, 0x61, 0x79, + 0x3D, 0x64, 0x65, 0x6C, 0x61, 0x79, 0x4D, 0x69, 0x6C, 0x6C, + 0x69, 0x73, 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x28, 0x29, 0x7D, 0x3B, 0x53, 0x6D, 0x6F, + 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, 0x61, 0x72, 0x74, + 0x2E, 0x70, 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, + 0x2E, 0x72, 0x65, 0x73, 0x69, 0x7A, 0x65, 0x3D, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x7B, 0x69, + 0x66, 0x28, 0x21, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6F, 0x70, + 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x65, 0x6E, 0x61, 0x62, + 0x6C, 0x65, 0x44, 0x70, 0x69, 0x53, 0x63, 0x61, 0x6C, 0x69, + 0x6E, 0x67, 0x7C, 0x7C, 0x21, 0x77, 0x69, 0x6E, 0x64, 0x6F, + 0x77, 0x7C, 0x7C, 0x77, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x2E, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x50, 0x69, 0x78, 0x65, + 0x6C, 0x52, 0x61, 0x74, 0x69, 0x6F, 0x3D, 0x3D, 0x3D, 0x31, + 0x29, 0x7B, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x7D, 0x76, + 0x61, 0x72, 0x20, 0x64, 0x70, 0x72, 0x3D, 0x77, 0x69, 0x6E, + 0x64, 0x6F, 0x77, 0x2E, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x50, 0x69, 0x78, 0x65, 0x6C, 0x52, 0x61, 0x74, 0x69, 0x6F, + 0x3B, 0x76, 0x61, 0x72, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x3D, 0x70, 0x61, 0x72, 0x73, 0x65, 0x49, 0x6E, 0x74, 0x28, + 0x74, 0x68, 0x69, 0x73, 0x2E, 0x63, 0x61, 0x6E, 0x76, 0x61, + 0x73, 0x2E, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x28, 0x27, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x27, 0x29, 0x29, 0x3B, 0x76, 0x61, 0x72, 0x20, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3D, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x49, 0x6E, 0x74, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x63, 0x61, 0x6E, 0x76, 0x61, 0x73, 0x2E, 0x67, 0x65, 0x74, + 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, + 0x27, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x27, 0x29, 0x29, + 0x3B, 0x69, 0x66, 0x28, 0x21, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x6F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, 0x6C, 0x57, 0x69, + 0x64, 0x74, 0x68, 0x7C, 0x7C, 0x28, 0x4D, 0x61, 0x74, 0x68, + 0x2E, 0x66, 0x6C, 0x6F, 0x6F, 0x72, 0x28, 0x74, 0x68, 0x69, + 0x73, 0x2E, 0x6F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, 0x6C, + 0x57, 0x69, 0x64, 0x74, 0x68, 0x2A, 0x64, 0x70, 0x72, 0x29, + 0x21, 0x3D, 0x3D, 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x29, + 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6F, 0x72, 0x69, 0x67, + 0x69, 0x6E, 0x61, 0x6C, 0x57, 0x69, 0x64, 0x74, 0x68, 0x3D, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x3B, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x63, 0x61, 0x6E, 0x76, 0x61, 0x73, 0x2E, 0x73, 0x65, + 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x28, 0x27, 0x77, 0x69, 0x64, 0x74, 0x68, 0x27, 0x2C, 0x28, + 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x66, 0x6C, 0x6F, 0x6F, 0x72, + 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2A, 0x64, 0x70, 0x72, + 0x29, 0x29, 0x2E, 0x74, 0x6F, 0x53, 0x74, 0x72, 0x69, 0x6E, + 0x67, 0x28, 0x29, 0x29, 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x63, 0x61, 0x6E, 0x76, 0x61, 0x73, 0x2E, 0x73, 0x74, 0x79, + 0x6C, 0x65, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3D, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x2B, 0x27, 0x70, 0x78, 0x27, 0x3B, + 0x74, 0x68, 0x69, 0x73, 0x2E, 0x63, 0x61, 0x6E, 0x76, 0x61, + 0x73, 0x2E, 0x67, 0x65, 0x74, 0x43, 0x6F, 0x6E, 0x74, 0x65, + 0x78, 0x74, 0x28, 0x27, 0x32, 0x64, 0x27, 0x29, 0x2E, 0x73, + 0x63, 0x61, 0x6C, 0x65, 0x28, 0x64, 0x70, 0x72, 0x2C, 0x64, + 0x70, 0x72, 0x29, 0x7D, 0x69, 0x66, 0x28, 0x21, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x6F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, + 0x6C, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x7C, 0x7C, 0x28, + 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x66, 0x6C, 0x6F, 0x6F, 0x72, + 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6F, 0x72, 0x69, 0x67, + 0x69, 0x6E, 0x61, 0x6C, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x2A, 0x64, 0x70, 0x72, 0x29, 0x21, 0x3D, 0x3D, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x29, 0x29, 0x7B, 0x74, 0x68, 0x69, + 0x73, 0x2E, 0x6F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, 0x6C, + 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3D, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x63, + 0x61, 0x6E, 0x76, 0x61, 0x73, 0x2E, 0x73, 0x65, 0x74, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x27, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x27, 0x2C, 0x28, 0x4D, + 0x61, 0x74, 0x68, 0x2E, 0x66, 0x6C, 0x6F, 0x6F, 0x72, 0x28, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2A, 0x64, 0x70, 0x72, + 0x29, 0x29, 0x2E, 0x74, 0x6F, 0x53, 0x74, 0x72, 0x69, 0x6E, + 0x67, 0x28, 0x29, 0x29, 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x63, 0x61, 0x6E, 0x76, 0x61, 0x73, 0x2E, 0x73, 0x74, 0x79, + 0x6C, 0x65, 0x2E, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3D, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2B, 0x27, 0x70, 0x78, + 0x27, 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x63, 0x61, 0x6E, + 0x76, 0x61, 0x73, 0x2E, 0x67, 0x65, 0x74, 0x43, 0x6F, 0x6E, + 0x74, 0x65, 0x78, 0x74, 0x28, 0x27, 0x32, 0x64, 0x27, 0x29, + 0x2E, 0x73, 0x63, 0x61, 0x6C, 0x65, 0x28, 0x64, 0x70, 0x72, + 0x2C, 0x64, 0x70, 0x72, 0x29, 0x7D, 0x7D, 0x3B, 0x53, 0x6D, + 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, 0x61, 0x72, + 0x74, 0x2E, 0x70, 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, + 0x65, 0x2E, 0x73, 0x74, 0x61, 0x72, 0x74, 0x3D, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x7B, 0x69, + 0x66, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x66, 0x72, 0x61, + 0x6D, 0x65, 0x29, 0x7B, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, + 0x7D, 0x76, 0x61, 0x72, 0x20, 0x61, 0x6E, 0x69, 0x6D, 0x61, + 0x74, 0x65, 0x3D, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, + 0x6E, 0x28, 0x29, 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x66, + 0x72, 0x61, 0x6D, 0x65, 0x3D, 0x53, 0x6D, 0x6F, 0x6F, 0x74, + 0x68, 0x69, 0x65, 0x43, 0x68, 0x61, 0x72, 0x74, 0x2E, 0x41, + 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x65, 0x43, 0x6F, 0x6D, 0x70, + 0x61, 0x74, 0x69, 0x62, 0x69, 0x6C, 0x69, 0x74, 0x79, 0x2E, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x6E, 0x69, + 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x46, 0x72, 0x61, 0x6D, + 0x65, 0x28, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, + 0x28, 0x29, 0x7B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x72, 0x65, + 0x6E, 0x64, 0x65, 0x72, 0x28, 0x29, 0x3B, 0x61, 0x6E, 0x69, + 0x6D, 0x61, 0x74, 0x65, 0x28, 0x29, 0x7D, 0x2E, 0x62, 0x69, + 0x6E, 0x64, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x29, 0x7D, + 0x2E, 0x62, 0x69, 0x6E, 0x64, 0x28, 0x74, 0x68, 0x69, 0x73, + 0x29, 0x3B, 0x61, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x65, 0x28, + 0x29, 0x7D, 0x3B, 0x53, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, + 0x65, 0x43, 0x68, 0x61, 0x72, 0x74, 0x2E, 0x70, 0x72, 0x6F, + 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2E, 0x73, 0x74, 0x6F, + 0x70, 0x3D, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, + 0x28, 0x29, 0x7B, 0x69, 0x66, 0x28, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x66, 0x72, 0x61, 0x6D, 0x65, 0x29, 0x7B, 0x53, 0x6D, + 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, 0x61, 0x72, + 0x74, 0x2E, 0x41, 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x65, 0x43, + 0x6F, 0x6D, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6C, 0x69, + 0x74, 0x79, 0x2E, 0x63, 0x61, 0x6E, 0x63, 0x65, 0x6C, 0x41, + 0x6E, 0x69, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x46, 0x72, + 0x61, 0x6D, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x66, + 0x72, 0x61, 0x6D, 0x65, 0x29, 0x3B, 0x64, 0x65, 0x6C, 0x65, + 0x74, 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x66, 0x72, + 0x61, 0x6D, 0x65, 0x7D, 0x7D, 0x3B, 0x53, 0x6D, 0x6F, 0x6F, + 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, 0x61, 0x72, 0x74, 0x2E, + 0x70, 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, 0x2E, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x61, 0x6C, 0x75, + 0x65, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x3D, 0x66, 0x75, 0x6E, + 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x29, 0x7B, 0x76, 0x61, + 0x72, 0x20, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, + 0x69, 0x6F, 0x6E, 0x73, 0x3D, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2C, 0x63, 0x68, + 0x61, 0x72, 0x74, 0x4D, 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, + 0x65, 0x3D, 0x4E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x2E, 0x4E, + 0x61, 0x4E, 0x2C, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x69, + 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x4E, 0x75, 0x6D, + 0x62, 0x65, 0x72, 0x2E, 0x4E, 0x61, 0x4E, 0x3B, 0x66, 0x6F, + 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x64, 0x3D, 0x30, 0x3B, + 0x64, 0x3C, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x73, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x53, 0x65, 0x74, 0x2E, 0x6C, 0x65, 0x6E, + 0x67, 0x74, 0x68, 0x3B, 0x64, 0x2B, 0x3D, 0x31, 0x29, 0x7B, + 0x76, 0x61, 0x72, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x3D, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x53, 0x65, 0x74, 0x5B, + 0x64, 0x5D, 0x2E, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x3B, 0x69, 0x66, 0x28, 0x21, 0x69, 0x73, + 0x4E, 0x61, 0x4E, 0x28, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x2E, 0x6D, 0x61, 0x78, 0x56, 0x61, + 0x6C, 0x75, 0x65, 0x29, 0x29, 0x7B, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x4D, 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3D, + 0x21, 0x69, 0x73, 0x4E, 0x61, 0x4E, 0x28, 0x63, 0x68, 0x61, + 0x72, 0x74, 0x4D, 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, + 0x29, 0x3F, 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x6D, 0x61, 0x78, + 0x28, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x61, 0x78, 0x56, + 0x61, 0x6C, 0x75, 0x65, 0x2C, 0x74, 0x69, 0x6D, 0x65, 0x53, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x6D, 0x61, 0x78, 0x56, + 0x61, 0x6C, 0x75, 0x65, 0x29, 0x3A, 0x74, 0x69, 0x6D, 0x65, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x6D, 0x61, 0x78, + 0x56, 0x61, 0x6C, 0x75, 0x65, 0x7D, 0x69, 0x66, 0x28, 0x21, + 0x69, 0x73, 0x4E, 0x61, 0x4E, 0x28, 0x74, 0x69, 0x6D, 0x65, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x6D, 0x69, 0x6E, + 0x56, 0x61, 0x6C, 0x75, 0x65, 0x29, 0x29, 0x7B, 0x63, 0x68, + 0x61, 0x72, 0x74, 0x4D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, + 0x65, 0x3D, 0x21, 0x69, 0x73, 0x4E, 0x61, 0x4E, 0x28, 0x63, + 0x68, 0x61, 0x72, 0x74, 0x4D, 0x69, 0x6E, 0x56, 0x61, 0x6C, + 0x75, 0x65, 0x29, 0x3F, 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x6D, + 0x69, 0x6E, 0x28, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x69, + 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x2C, 0x74, 0x69, 0x6D, + 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x6D, 0x69, + 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x29, 0x3A, 0x74, 0x69, + 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x6D, + 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x7D, 0x7D, 0x69, + 0x66, 0x28, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, + 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6D, 0x61, 0x78, 0x56, 0x61, + 0x6C, 0x75, 0x65, 0x21, 0x3D, 0x6E, 0x75, 0x6C, 0x6C, 0x29, + 0x7B, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x61, 0x78, 0x56, + 0x61, 0x6C, 0x75, 0x65, 0x3D, 0x63, 0x68, 0x61, 0x72, 0x74, + 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6D, 0x61, + 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x7D, 0x65, 0x6C, 0x73, + 0x65, 0x7B, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x61, 0x78, + 0x56, 0x61, 0x6C, 0x75, 0x65, 0x2A, 0x3D, 0x63, 0x68, 0x61, + 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, + 0x6D, 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x53, 0x63, + 0x61, 0x6C, 0x65, 0x7D, 0x69, 0x66, 0x28, 0x63, 0x68, 0x61, + 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, + 0x6D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x21, 0x3D, + 0x6E, 0x75, 0x6C, 0x6C, 0x29, 0x7B, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x4D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3D, + 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, + 0x6E, 0x73, 0x2E, 0x6D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, + 0x65, 0x7D, 0x65, 0x6C, 0x73, 0x65, 0x7B, 0x63, 0x68, 0x61, + 0x72, 0x74, 0x4D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, + 0x2D, 0x3D, 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x61, 0x62, 0x73, + 0x28, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x69, 0x6E, 0x56, + 0x61, 0x6C, 0x75, 0x65, 0x2A, 0x63, 0x68, 0x61, 0x72, 0x74, + 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6D, 0x69, + 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x53, 0x63, 0x61, 0x6C, + 0x65, 0x2D, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x69, 0x6E, + 0x56, 0x61, 0x6C, 0x75, 0x65, 0x29, 0x7D, 0x69, 0x66, 0x28, + 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6F, 0x70, 0x74, 0x69, 0x6F, + 0x6E, 0x73, 0x2E, 0x79, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x46, + 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x29, 0x7B, 0x76, + 0x61, 0x72, 0x20, 0x72, 0x61, 0x6E, 0x67, 0x65, 0x3D, 0x74, + 0x68, 0x69, 0x73, 0x2E, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x79, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x46, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x7B, 0x6D, 0x69, + 0x6E, 0x3A, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x69, 0x6E, + 0x56, 0x61, 0x6C, 0x75, 0x65, 0x2C, 0x6D, 0x61, 0x78, 0x3A, + 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x61, 0x78, 0x56, 0x61, + 0x6C, 0x75, 0x65, 0x7D, 0x29, 0x3B, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x4D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3D, + 0x72, 0x61, 0x6E, 0x67, 0x65, 0x2E, 0x6D, 0x69, 0x6E, 0x3B, + 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x61, 0x78, 0x56, 0x61, + 0x6C, 0x75, 0x65, 0x3D, 0x72, 0x61, 0x6E, 0x67, 0x65, 0x2E, + 0x6D, 0x61, 0x78, 0x7D, 0x69, 0x66, 0x28, 0x21, 0x69, 0x73, + 0x4E, 0x61, 0x4E, 0x28, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, + 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x29, 0x26, 0x26, + 0x21, 0x69, 0x73, 0x4E, 0x61, 0x4E, 0x28, 0x63, 0x68, 0x61, + 0x72, 0x74, 0x4D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, + 0x29, 0x29, 0x7B, 0x76, 0x61, 0x72, 0x20, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x52, 0x61, + 0x6E, 0x67, 0x65, 0x3D, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, + 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x2D, 0x63, 0x68, + 0x61, 0x72, 0x74, 0x4D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, + 0x65, 0x3B, 0x76, 0x61, 0x72, 0x20, 0x76, 0x61, 0x6C, 0x75, + 0x65, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x44, 0x69, 0x66, 0x66, + 0x3D, 0x28, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x61, + 0x6C, 0x75, 0x65, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x2D, 0x74, + 0x68, 0x69, 0x73, 0x2E, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, + 0x74, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x52, 0x61, 0x6E, 0x67, + 0x65, 0x29, 0x3B, 0x76, 0x61, 0x72, 0x20, 0x6D, 0x69, 0x6E, + 0x56, 0x61, 0x6C, 0x75, 0x65, 0x44, 0x69, 0x66, 0x66, 0x3D, + 0x28, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x69, 0x6E, 0x56, + 0x61, 0x6C, 0x75, 0x65, 0x2D, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x56, 0x69, 0x73, + 0x4D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x29, 0x3B, + 0x74, 0x68, 0x69, 0x73, 0x2E, 0x69, 0x73, 0x41, 0x6E, 0x69, + 0x6D, 0x61, 0x74, 0x69, 0x6E, 0x67, 0x53, 0x63, 0x61, 0x6C, + 0x65, 0x3D, 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x61, 0x62, 0x73, + 0x28, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x52, 0x61, 0x6E, 0x67, + 0x65, 0x44, 0x69, 0x66, 0x66, 0x29, 0x3E, 0x30, 0x2E, 0x31, + 0x7C, 0x7C, 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x61, 0x62, 0x73, + 0x28, 0x6D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x44, + 0x69, 0x66, 0x66, 0x29, 0x3E, 0x30, 0x2E, 0x31, 0x3B, 0x74, + 0x68, 0x69, 0x73, 0x2E, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, + 0x74, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x52, 0x61, 0x6E, 0x67, + 0x65, 0x2B, 0x3D, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, + 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x73, 0x63, 0x61, 0x6C, + 0x65, 0x53, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x6E, 0x67, + 0x2A, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x52, 0x61, 0x6E, 0x67, + 0x65, 0x44, 0x69, 0x66, 0x66, 0x3B, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x56, 0x69, + 0x73, 0x4D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x2B, + 0x3D, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, + 0x6F, 0x6E, 0x73, 0x2E, 0x73, 0x63, 0x61, 0x6C, 0x65, 0x53, + 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x6E, 0x67, 0x2A, 0x6D, + 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x44, 0x69, 0x66, + 0x66, 0x7D, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x76, 0x61, 0x6C, + 0x75, 0x65, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x3D, 0x7B, 0x6D, + 0x69, 0x6E, 0x3A, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x69, + 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x2C, 0x6D, 0x61, 0x78, + 0x3A, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4D, 0x61, 0x78, 0x56, + 0x61, 0x6C, 0x75, 0x65, 0x7D, 0x7D, 0x3B, 0x53, 0x6D, 0x6F, + 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, 0x61, 0x72, 0x74, + 0x2E, 0x70, 0x72, 0x6F, 0x74, 0x6F, 0x74, 0x79, 0x70, 0x65, + 0x2E, 0x72, 0x65, 0x6E, 0x64, 0x65, 0x72, 0x3D, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x63, 0x61, 0x6E, + 0x76, 0x61, 0x73, 0x2C, 0x74, 0x69, 0x6D, 0x65, 0x29, 0x7B, + 0x76, 0x61, 0x72, 0x20, 0x6E, 0x6F, 0x77, 0x4D, 0x69, 0x6C, + 0x6C, 0x69, 0x73, 0x3D, 0x6E, 0x65, 0x77, 0x20, 0x44, 0x61, + 0x74, 0x65, 0x28, 0x29, 0x2E, 0x67, 0x65, 0x74, 0x54, 0x69, + 0x6D, 0x65, 0x28, 0x29, 0x3B, 0x69, 0x66, 0x28, 0x21, 0x74, + 0x68, 0x69, 0x73, 0x2E, 0x69, 0x73, 0x41, 0x6E, 0x69, 0x6D, + 0x61, 0x74, 0x69, 0x6E, 0x67, 0x53, 0x63, 0x61, 0x6C, 0x65, + 0x29, 0x7B, 0x76, 0x61, 0x72, 0x20, 0x6D, 0x61, 0x78, 0x49, + 0x64, 0x6C, 0x65, 0x4D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x3D, + 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x6D, 0x69, 0x6E, 0x28, 0x31, + 0x30, 0x30, 0x30, 0x2F, 0x36, 0x2C, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6D, + 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x50, 0x65, 0x72, 0x50, 0x69, + 0x78, 0x65, 0x6C, 0x29, 0x3B, 0x69, 0x66, 0x28, 0x6E, 0x6F, + 0x77, 0x4D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x2D, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x6C, 0x61, 0x73, 0x74, 0x52, 0x65, 0x6E, + 0x64, 0x65, 0x72, 0x54, 0x69, 0x6D, 0x65, 0x4D, 0x69, 0x6C, + 0x6C, 0x69, 0x73, 0x3C, 0x6D, 0x61, 0x78, 0x49, 0x64, 0x6C, + 0x65, 0x4D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x29, 0x7B, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6E, 0x7D, 0x7D, 0x74, 0x68, 0x69, + 0x73, 0x2E, 0x72, 0x65, 0x73, 0x69, 0x7A, 0x65, 0x28, 0x29, + 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6C, 0x61, 0x73, 0x74, + 0x52, 0x65, 0x6E, 0x64, 0x65, 0x72, 0x54, 0x69, 0x6D, 0x65, + 0x4D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x3D, 0x6E, 0x6F, 0x77, + 0x4D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x3B, 0x63, 0x61, 0x6E, + 0x76, 0x61, 0x73, 0x3D, 0x63, 0x61, 0x6E, 0x76, 0x61, 0x73, + 0x7C, 0x7C, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x63, 0x61, 0x6E, + 0x76, 0x61, 0x73, 0x3B, 0x74, 0x69, 0x6D, 0x65, 0x3D, 0x74, + 0x69, 0x6D, 0x65, 0x7C, 0x7C, 0x6E, 0x6F, 0x77, 0x4D, 0x69, + 0x6C, 0x6C, 0x69, 0x73, 0x2D, 0x28, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x64, 0x65, 0x6C, 0x61, 0x79, 0x7C, 0x7C, 0x30, 0x29, + 0x3B, 0x74, 0x69, 0x6D, 0x65, 0x2D, 0x3D, 0x74, 0x69, 0x6D, + 0x65, 0x25, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x6F, 0x70, 0x74, + 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6D, 0x69, 0x6C, 0x6C, 0x69, + 0x73, 0x50, 0x65, 0x72, 0x50, 0x69, 0x78, 0x65, 0x6C, 0x3B, + 0x76, 0x61, 0x72, 0x20, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, + 0x74, 0x3D, 0x63, 0x61, 0x6E, 0x76, 0x61, 0x73, 0x2E, 0x67, + 0x65, 0x74, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x28, + 0x27, 0x32, 0x64, 0x27, 0x29, 0x2C, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x3D, 0x74, + 0x68, 0x69, 0x73, 0x2E, 0x6F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2C, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, + 0x6E, 0x73, 0x3D, 0x7B, 0x74, 0x6F, 0x70, 0x3A, 0x30, 0x2C, + 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x30, 0x2C, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x3A, 0x63, 0x61, 0x6E, 0x76, 0x61, 0x73, 0x2E, + 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x57, 0x69, 0x64, 0x74, + 0x68, 0x2C, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x63, + 0x61, 0x6E, 0x76, 0x61, 0x73, 0x2E, 0x63, 0x6C, 0x69, 0x65, + 0x6E, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x7D, 0x2C, + 0x6F, 0x6C, 0x64, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6C, 0x69, + 0x64, 0x54, 0x69, 0x6D, 0x65, 0x3D, 0x74, 0x69, 0x6D, 0x65, + 0x2D, 0x28, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, + 0x6E, 0x73, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2A, 0x63, + 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x6D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x50, 0x65, + 0x72, 0x50, 0x69, 0x78, 0x65, 0x6C, 0x29, 0x2C, 0x76, 0x61, + 0x6C, 0x75, 0x65, 0x54, 0x6F, 0x59, 0x50, 0x69, 0x78, 0x65, + 0x6C, 0x3D, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, + 0x28, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x29, 0x7B, 0x76, 0x61, + 0x72, 0x20, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x3D, 0x76, + 0x61, 0x6C, 0x75, 0x65, 0x2D, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x56, 0x69, 0x73, + 0x4D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x3B, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x56, 0x61, + 0x6C, 0x75, 0x65, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x3D, 0x3D, + 0x3D, 0x30, 0x3F, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, + 0x6F, 0x6E, 0x73, 0x2E, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x3A, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2D, 0x28, + 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x72, 0x6F, 0x75, 0x6E, 0x64, + 0x28, 0x28, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2F, 0x74, + 0x68, 0x69, 0x73, 0x2E, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, + 0x74, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x52, 0x61, 0x6E, 0x67, + 0x65, 0x29, 0x2A, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, + 0x6F, 0x6E, 0x73, 0x2E, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x29, 0x29, 0x7D, 0x2E, 0x62, 0x69, 0x6E, 0x64, 0x28, 0x74, + 0x68, 0x69, 0x73, 0x29, 0x2C, 0x74, 0x69, 0x6D, 0x65, 0x54, + 0x6F, 0x58, 0x50, 0x69, 0x78, 0x65, 0x6C, 0x3D, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x74, 0x29, 0x7B, + 0x69, 0x66, 0x28, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, + 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x73, 0x63, 0x72, 0x6F, + 0x6C, 0x6C, 0x42, 0x61, 0x63, 0x6B, 0x77, 0x61, 0x72, 0x64, + 0x73, 0x29, 0x7B, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, + 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x72, 0x6F, 0x75, 0x6E, 0x64, + 0x28, 0x28, 0x74, 0x69, 0x6D, 0x65, 0x2D, 0x74, 0x29, 0x2F, + 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, + 0x6E, 0x73, 0x2E, 0x6D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x50, + 0x65, 0x72, 0x50, 0x69, 0x78, 0x65, 0x6C, 0x29, 0x7D, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x4D, 0x61, 0x74, 0x68, + 0x2E, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x28, 0x64, 0x69, 0x6D, + 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x2D, 0x28, 0x28, 0x74, 0x69, 0x6D, 0x65, + 0x2D, 0x74, 0x29, 0x2F, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, + 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6D, 0x69, 0x6C, + 0x6C, 0x69, 0x73, 0x50, 0x65, 0x72, 0x50, 0x69, 0x78, 0x65, + 0x6C, 0x29, 0x29, 0x7D, 0x3B, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x61, 0x6C, 0x75, + 0x65, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x28, 0x29, 0x3B, 0x63, + 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x66, 0x6F, 0x6E, + 0x74, 0x3D, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, + 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6C, 0x61, 0x62, 0x65, 0x6C, + 0x73, 0x2E, 0x66, 0x6F, 0x6E, 0x74, 0x53, 0x69, 0x7A, 0x65, + 0x2B, 0x27, 0x70, 0x78, 0x20, 0x27, 0x2B, 0x63, 0x68, 0x61, + 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, + 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x73, 0x2E, 0x66, 0x6F, 0x6E, + 0x74, 0x46, 0x61, 0x6D, 0x69, 0x6C, 0x79, 0x3B, 0x63, 0x6F, + 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x73, 0x61, 0x76, 0x65, + 0x28, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, + 0x2E, 0x74, 0x72, 0x61, 0x6E, 0x73, 0x6C, 0x61, 0x74, 0x65, + 0x28, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x6C, 0x65, 0x66, 0x74, 0x2C, 0x64, 0x69, 0x6D, + 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x74, 0x6F, + 0x70, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, + 0x2E, 0x62, 0x65, 0x67, 0x69, 0x6E, 0x50, 0x61, 0x74, 0x68, + 0x28, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, + 0x2E, 0x72, 0x65, 0x63, 0x74, 0x28, 0x30, 0x2C, 0x30, 0x2C, + 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, + 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2C, 0x64, 0x69, 0x6D, + 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, + 0x65, 0x78, 0x74, 0x2E, 0x63, 0x6C, 0x69, 0x70, 0x28, 0x29, + 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x73, + 0x61, 0x76, 0x65, 0x28, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, + 0x65, 0x78, 0x74, 0x2E, 0x66, 0x69, 0x6C, 0x6C, 0x53, 0x74, + 0x79, 0x6C, 0x65, 0x3D, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, + 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x67, 0x72, 0x69, + 0x64, 0x2E, 0x66, 0x69, 0x6C, 0x6C, 0x53, 0x74, 0x79, 0x6C, + 0x65, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, + 0x63, 0x6C, 0x65, 0x61, 0x72, 0x52, 0x65, 0x63, 0x74, 0x28, + 0x30, 0x2C, 0x30, 0x2C, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, + 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x2C, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x29, 0x3B, + 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x66, 0x69, + 0x6C, 0x6C, 0x52, 0x65, 0x63, 0x74, 0x28, 0x30, 0x2C, 0x30, + 0x2C, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2C, 0x64, 0x69, + 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x29, 0x3B, 0x63, 0x6F, 0x6E, + 0x74, 0x65, 0x78, 0x74, 0x2E, 0x72, 0x65, 0x73, 0x74, 0x6F, + 0x72, 0x65, 0x28, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, + 0x78, 0x74, 0x2E, 0x73, 0x61, 0x76, 0x65, 0x28, 0x29, 0x3B, + 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x6C, 0x69, + 0x6E, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x3D, 0x63, 0x68, + 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, + 0x2E, 0x67, 0x72, 0x69, 0x64, 0x2E, 0x6C, 0x69, 0x6E, 0x65, + 0x57, 0x69, 0x64, 0x74, 0x68, 0x3B, 0x63, 0x6F, 0x6E, 0x74, + 0x65, 0x78, 0x74, 0x2E, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, + 0x53, 0x74, 0x79, 0x6C, 0x65, 0x3D, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x67, + 0x72, 0x69, 0x64, 0x2E, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, + 0x53, 0x74, 0x79, 0x6C, 0x65, 0x3B, 0x69, 0x66, 0x28, 0x63, + 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x67, 0x72, 0x69, 0x64, 0x2E, 0x6D, 0x69, 0x6C, + 0x6C, 0x69, 0x73, 0x50, 0x65, 0x72, 0x4C, 0x69, 0x6E, 0x65, + 0x3E, 0x30, 0x29, 0x7B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, + 0x74, 0x2E, 0x62, 0x65, 0x67, 0x69, 0x6E, 0x50, 0x61, 0x74, + 0x68, 0x28, 0x29, 0x3B, 0x66, 0x6F, 0x72, 0x28, 0x76, 0x61, + 0x72, 0x20, 0x74, 0x3D, 0x74, 0x69, 0x6D, 0x65, 0x2D, 0x28, + 0x74, 0x69, 0x6D, 0x65, 0x25, 0x63, 0x68, 0x61, 0x72, 0x74, + 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x67, 0x72, + 0x69, 0x64, 0x2E, 0x6D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x50, + 0x65, 0x72, 0x4C, 0x69, 0x6E, 0x65, 0x29, 0x3B, 0x74, 0x3E, + 0x3D, 0x6F, 0x6C, 0x64, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6C, + 0x69, 0x64, 0x54, 0x69, 0x6D, 0x65, 0x3B, 0x74, 0x2D, 0x3D, + 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, + 0x6E, 0x73, 0x2E, 0x67, 0x72, 0x69, 0x64, 0x2E, 0x6D, 0x69, + 0x6C, 0x6C, 0x69, 0x73, 0x50, 0x65, 0x72, 0x4C, 0x69, 0x6E, + 0x65, 0x29, 0x7B, 0x76, 0x61, 0x72, 0x20, 0x67, 0x78, 0x3D, + 0x74, 0x69, 0x6D, 0x65, 0x54, 0x6F, 0x58, 0x50, 0x69, 0x78, + 0x65, 0x6C, 0x28, 0x74, 0x29, 0x3B, 0x69, 0x66, 0x28, 0x63, + 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x67, 0x72, 0x69, 0x64, 0x2E, 0x73, 0x68, 0x61, + 0x72, 0x70, 0x4C, 0x69, 0x6E, 0x65, 0x73, 0x29, 0x7B, 0x67, + 0x78, 0x2D, 0x3D, 0x30, 0x2E, 0x35, 0x7D, 0x63, 0x6F, 0x6E, + 0x74, 0x65, 0x78, 0x74, 0x2E, 0x6D, 0x6F, 0x76, 0x65, 0x54, + 0x6F, 0x28, 0x67, 0x78, 0x2C, 0x30, 0x29, 0x3B, 0x63, 0x6F, + 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x6C, 0x69, 0x6E, 0x65, + 0x54, 0x6F, 0x28, 0x67, 0x78, 0x2C, 0x64, 0x69, 0x6D, 0x65, + 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x29, 0x7D, 0x63, 0x6F, 0x6E, 0x74, 0x65, + 0x78, 0x74, 0x2E, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x28, + 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, + 0x63, 0x6C, 0x6F, 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x28, + 0x29, 0x7D, 0x66, 0x6F, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, + 0x76, 0x3D, 0x31, 0x3B, 0x76, 0x3C, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x67, + 0x72, 0x69, 0x64, 0x2E, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, + 0x61, 0x6C, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x73, + 0x3B, 0x76, 0x2B, 0x3D, 0x31, 0x29, 0x7B, 0x76, 0x61, 0x72, + 0x20, 0x67, 0x79, 0x3D, 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x72, + 0x6F, 0x75, 0x6E, 0x64, 0x28, 0x76, 0x2A, 0x64, 0x69, 0x6D, + 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x2F, 0x63, 0x68, 0x61, 0x72, 0x74, + 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x67, 0x72, + 0x69, 0x64, 0x2E, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, + 0x6C, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x29, + 0x3B, 0x69, 0x66, 0x28, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, + 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x67, 0x72, 0x69, + 0x64, 0x2E, 0x73, 0x68, 0x61, 0x72, 0x70, 0x4C, 0x69, 0x6E, + 0x65, 0x73, 0x29, 0x7B, 0x67, 0x79, 0x2D, 0x3D, 0x30, 0x2E, + 0x35, 0x7D, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, + 0x62, 0x65, 0x67, 0x69, 0x6E, 0x50, 0x61, 0x74, 0x68, 0x28, + 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, + 0x6D, 0x6F, 0x76, 0x65, 0x54, 0x6F, 0x28, 0x30, 0x2C, 0x67, + 0x79, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, + 0x2E, 0x6C, 0x69, 0x6E, 0x65, 0x54, 0x6F, 0x28, 0x64, 0x69, + 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x2C, 0x67, 0x79, 0x29, 0x3B, 0x63, + 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x73, 0x74, 0x72, + 0x6F, 0x6B, 0x65, 0x28, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, + 0x65, 0x78, 0x74, 0x2E, 0x63, 0x6C, 0x6F, 0x73, 0x65, 0x50, + 0x61, 0x74, 0x68, 0x28, 0x29, 0x7D, 0x69, 0x66, 0x28, 0x63, + 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x67, 0x72, 0x69, 0x64, 0x2E, 0x62, 0x6F, 0x72, + 0x64, 0x65, 0x72, 0x56, 0x69, 0x73, 0x69, 0x62, 0x6C, 0x65, + 0x29, 0x7B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, + 0x62, 0x65, 0x67, 0x69, 0x6E, 0x50, 0x61, 0x74, 0x68, 0x28, + 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, + 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x52, 0x65, 0x63, 0x74, + 0x28, 0x30, 0x2C, 0x30, 0x2C, 0x64, 0x69, 0x6D, 0x65, 0x6E, + 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x2C, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, + 0x6E, 0x73, 0x2E, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x29, + 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x63, + 0x6C, 0x6F, 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x28, 0x29, + 0x7D, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x72, + 0x65, 0x73, 0x74, 0x6F, 0x72, 0x65, 0x28, 0x29, 0x3B, 0x69, + 0x66, 0x28, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, + 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x68, 0x6F, 0x72, 0x69, 0x7A, + 0x6F, 0x6E, 0x74, 0x61, 0x6C, 0x4C, 0x69, 0x6E, 0x65, 0x73, + 0x26, 0x26, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, + 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x68, 0x6F, 0x72, 0x69, 0x7A, + 0x6F, 0x6E, 0x74, 0x61, 0x6C, 0x4C, 0x69, 0x6E, 0x65, 0x73, + 0x2E, 0x6C, 0x65, 0x6E, 0x67, 0x74, 0x68, 0x29, 0x7B, 0x66, + 0x6F, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x68, 0x6C, 0x3D, + 0x30, 0x3B, 0x68, 0x6C, 0x3C, 0x63, 0x68, 0x61, 0x72, 0x74, + 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x68, 0x6F, + 0x72, 0x69, 0x7A, 0x6F, 0x6E, 0x74, 0x61, 0x6C, 0x4C, 0x69, + 0x6E, 0x65, 0x73, 0x2E, 0x6C, 0x65, 0x6E, 0x67, 0x74, 0x68, + 0x3B, 0x68, 0x6C, 0x2B, 0x3D, 0x31, 0x29, 0x7B, 0x76, 0x61, + 0x72, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x3D, 0x63, 0x68, 0x61, + 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, + 0x68, 0x6F, 0x72, 0x69, 0x7A, 0x6F, 0x6E, 0x74, 0x61, 0x6C, + 0x4C, 0x69, 0x6E, 0x65, 0x73, 0x5B, 0x68, 0x6C, 0x5D, 0x2C, + 0x68, 0x6C, 0x79, 0x3D, 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x72, + 0x6F, 0x75, 0x6E, 0x64, 0x28, 0x76, 0x61, 0x6C, 0x75, 0x65, + 0x54, 0x6F, 0x59, 0x50, 0x69, 0x78, 0x65, 0x6C, 0x28, 0x6C, + 0x69, 0x6E, 0x65, 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x29, + 0x29, 0x2D, 0x30, 0x2E, 0x35, 0x3B, 0x63, 0x6F, 0x6E, 0x74, + 0x65, 0x78, 0x74, 0x2E, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, + 0x53, 0x74, 0x79, 0x6C, 0x65, 0x3D, 0x6C, 0x69, 0x6E, 0x65, + 0x2E, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x7C, 0x7C, 0x27, 0x23, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x27, 0x3B, 0x63, 0x6F, + 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x6C, 0x69, 0x6E, 0x65, + 0x57, 0x69, 0x64, 0x74, 0x68, 0x3D, 0x6C, 0x69, 0x6E, 0x65, + 0x2E, 0x6C, 0x69, 0x6E, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, + 0x7C, 0x7C, 0x31, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, + 0x74, 0x2E, 0x62, 0x65, 0x67, 0x69, 0x6E, 0x50, 0x61, 0x74, + 0x68, 0x28, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, + 0x74, 0x2E, 0x6D, 0x6F, 0x76, 0x65, 0x54, 0x6F, 0x28, 0x30, + 0x2C, 0x68, 0x6C, 0x79, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, + 0x65, 0x78, 0x74, 0x2E, 0x6C, 0x69, 0x6E, 0x65, 0x54, 0x6F, + 0x28, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2C, 0x68, 0x6C, + 0x79, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, + 0x2E, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x28, 0x29, 0x3B, + 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x63, 0x6C, + 0x6F, 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x28, 0x29, 0x7D, + 0x7D, 0x66, 0x6F, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x64, + 0x3D, 0x30, 0x3B, 0x64, 0x3C, 0x74, 0x68, 0x69, 0x73, 0x2E, + 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x53, 0x65, 0x74, 0x2E, + 0x6C, 0x65, 0x6E, 0x67, 0x74, 0x68, 0x3B, 0x64, 0x2B, 0x3D, + 0x31, 0x29, 0x7B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, + 0x2E, 0x73, 0x61, 0x76, 0x65, 0x28, 0x29, 0x3B, 0x76, 0x61, + 0x72, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x3D, 0x74, 0x68, 0x69, 0x73, 0x2E, 0x73, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x53, 0x65, 0x74, 0x5B, 0x64, 0x5D, + 0x2E, 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x2C, 0x64, 0x61, 0x74, 0x61, 0x53, 0x65, 0x74, 0x3D, + 0x74, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x2E, 0x64, 0x61, 0x74, 0x61, 0x2C, 0x73, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x3D, + 0x74, 0x68, 0x69, 0x73, 0x2E, 0x73, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x53, 0x65, 0x74, 0x5B, 0x64, 0x5D, 0x2E, 0x6F, 0x70, + 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x3B, 0x74, 0x69, 0x6D, 0x65, + 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2E, 0x64, 0x72, 0x6F, + 0x70, 0x4F, 0x6C, 0x64, 0x44, 0x61, 0x74, 0x61, 0x28, 0x6F, + 0x6C, 0x64, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6C, 0x69, 0x64, + 0x54, 0x69, 0x6D, 0x65, 0x2C, 0x63, 0x68, 0x61, 0x72, 0x74, + 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6D, 0x61, + 0x78, 0x44, 0x61, 0x74, 0x61, 0x53, 0x65, 0x74, 0x4C, 0x65, + 0x6E, 0x67, 0x74, 0x68, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, + 0x65, 0x78, 0x74, 0x2E, 0x6C, 0x69, 0x6E, 0x65, 0x57, 0x69, + 0x64, 0x74, 0x68, 0x3D, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6C, 0x69, + 0x6E, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x3B, 0x63, 0x6F, + 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x73, 0x74, 0x72, 0x6F, + 0x6B, 0x65, 0x53, 0x74, 0x79, 0x6C, 0x65, 0x3D, 0x73, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x53, 0x74, + 0x79, 0x6C, 0x65, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, + 0x74, 0x2E, 0x62, 0x65, 0x67, 0x69, 0x6E, 0x50, 0x61, 0x74, + 0x68, 0x28, 0x29, 0x3B, 0x76, 0x61, 0x72, 0x20, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x58, 0x3D, 0x30, 0x2C, 0x6C, 0x61, 0x73, + 0x74, 0x58, 0x3D, 0x30, 0x2C, 0x6C, 0x61, 0x73, 0x74, 0x59, + 0x3D, 0x30, 0x3B, 0x66, 0x6F, 0x72, 0x28, 0x76, 0x61, 0x72, + 0x20, 0x69, 0x3D, 0x30, 0x3B, 0x69, 0x3C, 0x64, 0x61, 0x74, + 0x61, 0x53, 0x65, 0x74, 0x2E, 0x6C, 0x65, 0x6E, 0x67, 0x74, + 0x68, 0x26, 0x26, 0x64, 0x61, 0x74, 0x61, 0x53, 0x65, 0x74, + 0x2E, 0x6C, 0x65, 0x6E, 0x67, 0x74, 0x68, 0x21, 0x3D, 0x3D, + 0x31, 0x3B, 0x69, 0x2B, 0x3D, 0x31, 0x29, 0x7B, 0x76, 0x61, + 0x72, 0x20, 0x78, 0x3D, 0x74, 0x69, 0x6D, 0x65, 0x54, 0x6F, + 0x58, 0x50, 0x69, 0x78, 0x65, 0x6C, 0x28, 0x64, 0x61, 0x74, + 0x61, 0x53, 0x65, 0x74, 0x5B, 0x69, 0x5D, 0x5B, 0x30, 0x5D, + 0x29, 0x2C, 0x79, 0x3D, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x54, + 0x6F, 0x59, 0x50, 0x69, 0x78, 0x65, 0x6C, 0x28, 0x64, 0x61, + 0x74, 0x61, 0x53, 0x65, 0x74, 0x5B, 0x69, 0x5D, 0x5B, 0x31, + 0x5D, 0x29, 0x3B, 0x69, 0x66, 0x28, 0x69, 0x3D, 0x3D, 0x3D, + 0x30, 0x29, 0x7B, 0x66, 0x69, 0x72, 0x73, 0x74, 0x58, 0x3D, + 0x78, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, + 0x6D, 0x6F, 0x76, 0x65, 0x54, 0x6F, 0x28, 0x78, 0x2C, 0x79, + 0x29, 0x7D, 0x65, 0x6C, 0x73, 0x65, 0x7B, 0x73, 0x77, 0x69, + 0x74, 0x63, 0x68, 0x28, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, + 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x69, 0x6E, 0x74, + 0x65, 0x72, 0x70, 0x6F, 0x6C, 0x61, 0x74, 0x69, 0x6F, 0x6E, + 0x29, 0x7B, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x6C, 0x69, + 0x6E, 0x65, 0x61, 0x72, 0x22, 0x3A, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x22, 0x6C, 0x69, 0x6E, 0x65, 0x22, 0x3A, 0x7B, 0x63, + 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x6C, 0x69, 0x6E, + 0x65, 0x54, 0x6F, 0x28, 0x78, 0x2C, 0x79, 0x29, 0x3B, 0x62, + 0x72, 0x65, 0x61, 0x6B, 0x7D, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x22, 0x62, 0x65, 0x7A, 0x69, 0x65, 0x72, 0x22, 0x3A, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x3A, 0x7B, 0x63, 0x6F, + 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x62, 0x65, 0x7A, 0x69, + 0x65, 0x72, 0x43, 0x75, 0x72, 0x76, 0x65, 0x54, 0x6F, 0x28, + 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x72, 0x6F, 0x75, 0x6E, 0x64, + 0x28, 0x28, 0x6C, 0x61, 0x73, 0x74, 0x58, 0x2B, 0x78, 0x29, + 0x2F, 0x32, 0x29, 0x2C, 0x6C, 0x61, 0x73, 0x74, 0x59, 0x2C, + 0x4D, 0x61, 0x74, 0x68, 0x2E, 0x72, 0x6F, 0x75, 0x6E, 0x64, + 0x28, 0x28, 0x6C, 0x61, 0x73, 0x74, 0x58, 0x2B, 0x78, 0x29, + 0x29, 0x2F, 0x32, 0x2C, 0x79, 0x2C, 0x78, 0x2C, 0x79, 0x29, + 0x3B, 0x62, 0x72, 0x65, 0x61, 0x6B, 0x7D, 0x63, 0x61, 0x73, + 0x65, 0x20, 0x22, 0x73, 0x74, 0x65, 0x70, 0x22, 0x3A, 0x7B, + 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x6C, 0x69, + 0x6E, 0x65, 0x54, 0x6F, 0x28, 0x78, 0x2C, 0x6C, 0x61, 0x73, + 0x74, 0x59, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, + 0x74, 0x2E, 0x6C, 0x69, 0x6E, 0x65, 0x54, 0x6F, 0x28, 0x78, + 0x2C, 0x79, 0x29, 0x3B, 0x62, 0x72, 0x65, 0x61, 0x6B, 0x7D, + 0x7D, 0x7D, 0x6C, 0x61, 0x73, 0x74, 0x58, 0x3D, 0x78, 0x3B, + 0x6C, 0x61, 0x73, 0x74, 0x59, 0x3D, 0x79, 0x7D, 0x69, 0x66, + 0x28, 0x64, 0x61, 0x74, 0x61, 0x53, 0x65, 0x74, 0x2E, 0x6C, + 0x65, 0x6E, 0x67, 0x74, 0x68, 0x3E, 0x31, 0x29, 0x7B, 0x69, + 0x66, 0x28, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x4F, 0x70, + 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x66, 0x69, 0x6C, 0x6C, + 0x53, 0x74, 0x79, 0x6C, 0x65, 0x29, 0x7B, 0x63, 0x6F, 0x6E, + 0x74, 0x65, 0x78, 0x74, 0x2E, 0x6C, 0x69, 0x6E, 0x65, 0x54, + 0x6F, 0x28, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, + 0x6E, 0x73, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2B, 0x73, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x4F, 0x70, 0x74, 0x69, 0x6F, + 0x6E, 0x73, 0x2E, 0x6C, 0x69, 0x6E, 0x65, 0x57, 0x69, 0x64, + 0x74, 0x68, 0x2B, 0x31, 0x2C, 0x6C, 0x61, 0x73, 0x74, 0x59, + 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, + 0x6C, 0x69, 0x6E, 0x65, 0x54, 0x6F, 0x28, 0x64, 0x69, 0x6D, + 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x2B, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6C, 0x69, + 0x6E, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2B, 0x31, 0x2C, + 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, + 0x2E, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2B, 0x73, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x6C, 0x69, 0x6E, 0x65, 0x57, 0x69, 0x64, 0x74, + 0x68, 0x2B, 0x31, 0x29, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, + 0x78, 0x74, 0x2E, 0x6C, 0x69, 0x6E, 0x65, 0x54, 0x6F, 0x28, + 0x66, 0x69, 0x72, 0x73, 0x74, 0x58, 0x2C, 0x64, 0x69, 0x6D, + 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x2B, 0x73, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6C, + 0x69, 0x6E, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x29, 0x3B, + 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x66, 0x69, + 0x6C, 0x6C, 0x53, 0x74, 0x79, 0x6C, 0x65, 0x3D, 0x73, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x66, 0x69, 0x6C, 0x6C, 0x53, 0x74, 0x79, 0x6C, + 0x65, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, + 0x66, 0x69, 0x6C, 0x6C, 0x28, 0x29, 0x7D, 0x69, 0x66, 0x28, + 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x4F, 0x70, 0x74, 0x69, + 0x6F, 0x6E, 0x73, 0x2E, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, + 0x53, 0x74, 0x79, 0x6C, 0x65, 0x26, 0x26, 0x73, 0x65, 0x72, + 0x69, 0x65, 0x73, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, + 0x2E, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x53, 0x74, 0x79, + 0x6C, 0x65, 0x21, 0x3D, 0x3D, 0x27, 0x6E, 0x6F, 0x6E, 0x65, + 0x27, 0x29, 0x7B, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, + 0x2E, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x28, 0x29, 0x7D, + 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x63, 0x6C, + 0x6F, 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x28, 0x29, 0x7D, + 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x72, 0x65, + 0x73, 0x74, 0x6F, 0x72, 0x65, 0x28, 0x29, 0x7D, 0x69, 0x66, + 0x28, 0x21, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, + 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6C, 0x61, 0x62, 0x65, 0x6C, + 0x73, 0x2E, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6C, 0x65, 0x64, + 0x26, 0x26, 0x21, 0x69, 0x73, 0x4E, 0x61, 0x4E, 0x28, 0x74, + 0x68, 0x69, 0x73, 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x52, + 0x61, 0x6E, 0x67, 0x65, 0x2E, 0x6D, 0x69, 0x6E, 0x29, 0x26, + 0x26, 0x21, 0x69, 0x73, 0x4E, 0x61, 0x4E, 0x28, 0x74, 0x68, + 0x69, 0x73, 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x52, 0x61, + 0x6E, 0x67, 0x65, 0x2E, 0x6D, 0x61, 0x78, 0x29, 0x29, 0x7B, + 0x76, 0x61, 0x72, 0x20, 0x6D, 0x61, 0x78, 0x56, 0x61, 0x6C, + 0x75, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x3D, 0x63, + 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x79, 0x4D, 0x61, 0x78, 0x46, 0x6F, 0x72, 0x6D, + 0x61, 0x74, 0x74, 0x65, 0x72, 0x28, 0x74, 0x68, 0x69, 0x73, + 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x52, 0x61, 0x6E, 0x67, + 0x65, 0x2E, 0x6D, 0x61, 0x78, 0x2C, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6C, + 0x61, 0x62, 0x65, 0x6C, 0x73, 0x2E, 0x70, 0x72, 0x65, 0x63, + 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x29, 0x2C, 0x6D, 0x69, 0x6E, + 0x56, 0x61, 0x6C, 0x75, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6E, + 0x67, 0x3D, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, + 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x79, 0x4D, 0x69, 0x6E, 0x46, + 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x74, 0x65, 0x72, 0x28, 0x74, + 0x68, 0x69, 0x73, 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x52, + 0x61, 0x6E, 0x67, 0x65, 0x2E, 0x6D, 0x69, 0x6E, 0x2C, 0x63, + 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x73, 0x2E, 0x70, + 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x29, 0x2C, + 0x6D, 0x61, 0x78, 0x4C, 0x61, 0x62, 0x65, 0x6C, 0x50, 0x6F, + 0x73, 0x3D, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, + 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x73, 0x63, 0x72, 0x6F, 0x6C, + 0x6C, 0x42, 0x61, 0x63, 0x6B, 0x77, 0x61, 0x72, 0x64, 0x73, + 0x3F, 0x30, 0x3A, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, + 0x6F, 0x6E, 0x73, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2D, + 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x6D, 0x65, + 0x61, 0x73, 0x75, 0x72, 0x65, 0x54, 0x65, 0x78, 0x74, 0x28, + 0x6D, 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x53, 0x74, + 0x72, 0x69, 0x6E, 0x67, 0x29, 0x2E, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x2D, 0x32, 0x2C, 0x6D, 0x69, 0x6E, 0x4C, 0x61, 0x62, + 0x65, 0x6C, 0x50, 0x6F, 0x73, 0x3D, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x73, + 0x63, 0x72, 0x6F, 0x6C, 0x6C, 0x42, 0x61, 0x63, 0x6B, 0x77, + 0x61, 0x72, 0x64, 0x73, 0x3F, 0x30, 0x3A, 0x64, 0x69, 0x6D, + 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x2D, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, + 0x74, 0x2E, 0x6D, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x54, + 0x65, 0x78, 0x74, 0x28, 0x6D, 0x69, 0x6E, 0x56, 0x61, 0x6C, + 0x75, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x29, 0x2E, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x2D, 0x32, 0x3B, 0x63, 0x6F, + 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x66, 0x69, 0x6C, 0x6C, + 0x53, 0x74, 0x79, 0x6C, 0x65, 0x3D, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6C, + 0x61, 0x62, 0x65, 0x6C, 0x73, 0x2E, 0x66, 0x69, 0x6C, 0x6C, + 0x53, 0x74, 0x79, 0x6C, 0x65, 0x3B, 0x63, 0x6F, 0x6E, 0x74, + 0x65, 0x78, 0x74, 0x2E, 0x66, 0x69, 0x6C, 0x6C, 0x54, 0x65, + 0x78, 0x74, 0x28, 0x6D, 0x61, 0x78, 0x56, 0x61, 0x6C, 0x75, + 0x65, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x2C, 0x6D, 0x61, + 0x78, 0x4C, 0x61, 0x62, 0x65, 0x6C, 0x50, 0x6F, 0x73, 0x2C, + 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, + 0x6E, 0x73, 0x2E, 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x73, 0x2E, + 0x66, 0x6F, 0x6E, 0x74, 0x53, 0x69, 0x7A, 0x65, 0x29, 0x3B, + 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x66, 0x69, + 0x6C, 0x6C, 0x54, 0x65, 0x78, 0x74, 0x28, 0x6D, 0x69, 0x6E, + 0x56, 0x61, 0x6C, 0x75, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6E, + 0x67, 0x2C, 0x6D, 0x69, 0x6E, 0x4C, 0x61, 0x62, 0x65, 0x6C, + 0x50, 0x6F, 0x73, 0x2C, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, + 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x2D, 0x32, 0x29, 0x7D, 0x69, 0x66, 0x28, 0x63, 0x68, + 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, + 0x2E, 0x74, 0x69, 0x6D, 0x65, 0x73, 0x74, 0x61, 0x6D, 0x70, + 0x46, 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x74, 0x65, 0x72, 0x26, + 0x26, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, + 0x6F, 0x6E, 0x73, 0x2E, 0x67, 0x72, 0x69, 0x64, 0x2E, 0x6D, + 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x50, 0x65, 0x72, 0x4C, 0x69, + 0x6E, 0x65, 0x3E, 0x30, 0x29, 0x7B, 0x76, 0x61, 0x72, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x55, 0x6E, 0x74, 0x69, 0x6C, 0x58, + 0x3D, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, + 0x6F, 0x6E, 0x73, 0x2E, 0x73, 0x63, 0x72, 0x6F, 0x6C, 0x6C, + 0x42, 0x61, 0x63, 0x6B, 0x77, 0x61, 0x72, 0x64, 0x73, 0x3F, + 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x6D, 0x65, + 0x61, 0x73, 0x75, 0x72, 0x65, 0x54, 0x65, 0x78, 0x74, 0x28, + 0x6D, 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x53, 0x74, + 0x72, 0x69, 0x6E, 0x67, 0x29, 0x2E, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x3A, 0x64, 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, + 0x6E, 0x73, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2D, 0x63, + 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x6D, 0x65, 0x61, + 0x73, 0x75, 0x72, 0x65, 0x54, 0x65, 0x78, 0x74, 0x28, 0x6D, + 0x69, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x53, 0x74, 0x72, + 0x69, 0x6E, 0x67, 0x29, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x2B, 0x34, 0x3B, 0x66, 0x6F, 0x72, 0x28, 0x76, 0x61, 0x72, + 0x20, 0x74, 0x3D, 0x74, 0x69, 0x6D, 0x65, 0x2D, 0x28, 0x74, + 0x69, 0x6D, 0x65, 0x25, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, + 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x67, 0x72, 0x69, + 0x64, 0x2E, 0x6D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x50, 0x65, + 0x72, 0x4C, 0x69, 0x6E, 0x65, 0x29, 0x3B, 0x74, 0x3E, 0x3D, + 0x6F, 0x6C, 0x64, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6C, 0x69, + 0x64, 0x54, 0x69, 0x6D, 0x65, 0x3B, 0x74, 0x2D, 0x3D, 0x63, + 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, + 0x73, 0x2E, 0x67, 0x72, 0x69, 0x64, 0x2E, 0x6D, 0x69, 0x6C, + 0x6C, 0x69, 0x73, 0x50, 0x65, 0x72, 0x4C, 0x69, 0x6E, 0x65, + 0x29, 0x7B, 0x76, 0x61, 0x72, 0x20, 0x67, 0x78, 0x3D, 0x74, + 0x69, 0x6D, 0x65, 0x54, 0x6F, 0x58, 0x50, 0x69, 0x78, 0x65, + 0x6C, 0x28, 0x74, 0x29, 0x3B, 0x69, 0x66, 0x28, 0x28, 0x21, + 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, + 0x6E, 0x73, 0x2E, 0x73, 0x63, 0x72, 0x6F, 0x6C, 0x6C, 0x42, + 0x61, 0x63, 0x6B, 0x77, 0x61, 0x72, 0x64, 0x73, 0x26, 0x26, + 0x67, 0x78, 0x3C, 0x74, 0x65, 0x78, 0x74, 0x55, 0x6E, 0x74, + 0x69, 0x6C, 0x58, 0x29, 0x7C, 0x7C, 0x28, 0x63, 0x68, 0x61, + 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, + 0x73, 0x63, 0x72, 0x6F, 0x6C, 0x6C, 0x42, 0x61, 0x63, 0x6B, + 0x77, 0x61, 0x72, 0x64, 0x73, 0x26, 0x26, 0x67, 0x78, 0x3E, + 0x74, 0x65, 0x78, 0x74, 0x55, 0x6E, 0x74, 0x69, 0x6C, 0x58, + 0x29, 0x29, 0x7B, 0x76, 0x61, 0x72, 0x20, 0x74, 0x78, 0x3D, + 0x6E, 0x65, 0x77, 0x20, 0x44, 0x61, 0x74, 0x65, 0x28, 0x74, + 0x29, 0x2C, 0x74, 0x73, 0x3D, 0x63, 0x68, 0x61, 0x72, 0x74, + 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x74, 0x69, + 0x6D, 0x65, 0x73, 0x74, 0x61, 0x6D, 0x70, 0x46, 0x6F, 0x72, + 0x6D, 0x61, 0x74, 0x74, 0x65, 0x72, 0x28, 0x74, 0x78, 0x29, + 0x2C, 0x74, 0x73, 0x57, 0x69, 0x64, 0x74, 0x68, 0x3D, 0x63, + 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x6D, 0x65, 0x61, + 0x73, 0x75, 0x72, 0x65, 0x54, 0x65, 0x78, 0x74, 0x28, 0x74, + 0x73, 0x29, 0x2E, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3B, 0x74, + 0x65, 0x78, 0x74, 0x55, 0x6E, 0x74, 0x69, 0x6C, 0x58, 0x3D, + 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, + 0x6E, 0x73, 0x2E, 0x73, 0x63, 0x72, 0x6F, 0x6C, 0x6C, 0x42, + 0x61, 0x63, 0x6B, 0x77, 0x61, 0x72, 0x64, 0x73, 0x3F, 0x67, + 0x78, 0x2B, 0x74, 0x73, 0x57, 0x69, 0x64, 0x74, 0x68, 0x2B, + 0x32, 0x3A, 0x67, 0x78, 0x2D, 0x74, 0x73, 0x57, 0x69, 0x64, + 0x74, 0x68, 0x2D, 0x32, 0x3B, 0x63, 0x6F, 0x6E, 0x74, 0x65, + 0x78, 0x74, 0x2E, 0x66, 0x69, 0x6C, 0x6C, 0x53, 0x74, 0x79, + 0x6C, 0x65, 0x3D, 0x63, 0x68, 0x61, 0x72, 0x74, 0x4F, 0x70, + 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x6C, 0x61, 0x62, 0x65, + 0x6C, 0x73, 0x2E, 0x66, 0x69, 0x6C, 0x6C, 0x53, 0x74, 0x79, + 0x6C, 0x65, 0x3B, 0x69, 0x66, 0x28, 0x63, 0x68, 0x61, 0x72, + 0x74, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x73, + 0x63, 0x72, 0x6F, 0x6C, 0x6C, 0x42, 0x61, 0x63, 0x6B, 0x77, + 0x61, 0x72, 0x64, 0x73, 0x29, 0x7B, 0x63, 0x6F, 0x6E, 0x74, + 0x65, 0x78, 0x74, 0x2E, 0x66, 0x69, 0x6C, 0x6C, 0x54, 0x65, + 0x78, 0x74, 0x28, 0x74, 0x73, 0x2C, 0x67, 0x78, 0x2C, 0x64, + 0x69, 0x6D, 0x65, 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2D, 0x32, 0x29, 0x7D, + 0x65, 0x6C, 0x73, 0x65, 0x7B, 0x63, 0x6F, 0x6E, 0x74, 0x65, + 0x78, 0x74, 0x2E, 0x66, 0x69, 0x6C, 0x6C, 0x54, 0x65, 0x78, + 0x74, 0x28, 0x74, 0x73, 0x2C, 0x67, 0x78, 0x2D, 0x74, 0x73, + 0x57, 0x69, 0x64, 0x74, 0x68, 0x2C, 0x64, 0x69, 0x6D, 0x65, + 0x6E, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x2E, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x2D, 0x32, 0x29, 0x7D, 0x7D, 0x7D, 0x7D, + 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x2E, 0x72, 0x65, + 0x73, 0x74, 0x6F, 0x72, 0x65, 0x28, 0x29, 0x3B, 0x7D, 0x3B, + 0x53, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, 0x68, + 0x61, 0x72, 0x74, 0x2E, 0x74, 0x69, 0x6D, 0x65, 0x46, 0x6F, + 0x72, 0x6D, 0x61, 0x74, 0x74, 0x65, 0x72, 0x3D, 0x66, 0x75, + 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x28, 0x64, 0x61, 0x74, + 0x65, 0x29, 0x7B, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, + 0x6E, 0x20, 0x70, 0x61, 0x64, 0x32, 0x28, 0x6E, 0x75, 0x6D, + 0x62, 0x65, 0x72, 0x29, 0x7B, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6E, 0x28, 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x3C, 0x31, + 0x30, 0x3F, 0x27, 0x30, 0x27, 0x3A, 0x27, 0x27, 0x29, 0x2B, + 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x7D, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6E, 0x20, 0x70, 0x61, 0x64, 0x32, 0x28, 0x64, + 0x61, 0x74, 0x65, 0x2E, 0x67, 0x65, 0x74, 0x48, 0x6F, 0x75, + 0x72, 0x73, 0x28, 0x29, 0x29, 0x2B, 0x27, 0x3A, 0x27, 0x2B, + 0x70, 0x61, 0x64, 0x32, 0x28, 0x64, 0x61, 0x74, 0x65, 0x2E, + 0x67, 0x65, 0x74, 0x4D, 0x69, 0x6E, 0x75, 0x74, 0x65, 0x73, + 0x28, 0x29, 0x29, 0x2B, 0x27, 0x3A, 0x27, 0x2B, 0x70, 0x61, + 0x64, 0x32, 0x28, 0x64, 0x61, 0x74, 0x65, 0x2E, 0x67, 0x65, + 0x74, 0x53, 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x73, 0x28, 0x29, + 0x29, 0x7D, 0x3B, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, + 0x2E, 0x54, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, + 0x73, 0x3D, 0x54, 0x69, 0x6D, 0x65, 0x53, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x3B, 0x65, 0x78, 0x70, 0x6F, 0x72, 0x74, 0x73, + 0x2E, 0x53, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x69, 0x65, 0x43, + 0x68, 0x61, 0x72, 0x74, 0x3D, 0x53, 0x6D, 0x6F, 0x6F, 0x74, + 0x68, 0x69, 0x65, 0x43, 0x68, 0x61, 0x72, 0x74, 0x7D, 0x29, + 0x28, 0x74, 0x79, 0x70, 0x65, 0x6F, 0x66, 0x20, 0x65, 0x78, + 0x70, 0x6F, 0x72, 0x74, 0x73, 0x3D, 0x3D, 0x3D, 0x27, 0x75, + 0x6E, 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x64, 0x27, 0x3F, + 0x74, 0x68, 0x69, 0x73, 0x3A, 0x65, 0x78, 0x70, 0x6F, 0x72, + 0x74, 0x73, 0x29, 0x3B, }; + +static const unsigned char data_img_favicon_png[] = { + /* /img/favicon.png */ + 0x2F, 0x69, 0x6D, 0x67, 0x2F, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6F, 0x6E, 0x2E, 0x70, 0x6E, 0x67, 0, + 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x20, 0x4F, 0x4B, 0x0D, 0x0A, 0x6C, 0x77, 0x49, + 0x50, 0x2F, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x20, 0x28, 0x68, + 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x61, 0x76, 0x61, + 0x6E, 0x6E, 0x61, 0x68, 0x2E, 0x6E, 0x6F, 0x6E, 0x67, 0x6E, + 0x75, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x70, 0x72, 0x6F, 0x6A, + 0x65, 0x63, 0x74, 0x73, 0x2F, 0x6C, 0x77, 0x69, 0x70, 0x29, + 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, + 0x74, 0x79, 0x70, 0x65, 0x3A, 0x20, 0x69, 0x6D, 0x61, 0x67, + 0x65, 0x2F, 0x70, 0x6E, 0x67, 0x0D, 0x0A, 0x0D, 0x0A, 0x89, + 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, + 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x10, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1F, 0xF3, + 0xFF, 0x61, 0x00, 0x00, 0x02, 0xBF, 0x49, 0x44, 0x41, 0x54, + 0x38, 0xCB, 0xA5, 0x92, 0x4F, 0x6C, 0x14, 0x75, 0x14, 0xC7, + 0x3F, 0x3B, 0x3B, 0x33, 0xFB, 0xBF, 0xBB, 0x74, 0x59, 0xD8, + 0xA5, 0xC5, 0x76, 0x4B, 0x65, 0x6B, 0x49, 0x10, 0x41, 0x41, + 0x20, 0x84, 0xD2, 0x80, 0x6D, 0x6C, 0x62, 0x80, 0xC4, 0x78, + 0xF1, 0x64, 0xD2, 0x93, 0x89, 0x77, 0xF1, 0xE4, 0xD9, 0x83, + 0x37, 0x63, 0x3C, 0xE0, 0xC5, 0x43, 0x2F, 0x46, 0x23, 0x90, + 0x70, 0x30, 0x01, 0xA5, 0x6A, 0x29, 0x2D, 0x65, 0xA1, 0x14, + 0xD3, 0xAD, 0x08, 0xBB, 0x9A, 0xD9, 0xA6, 0xBB, 0x65, 0xF6, + 0x5F, 0x67, 0x77, 0xE7, 0x37, 0xF3, 0xF3, 0x62, 0xD7, 0xA6, + 0xE2, 0xC9, 0x77, 0x79, 0x2F, 0x2F, 0xEF, 0xFB, 0xC9, 0x37, + 0x2F, 0x5F, 0xF8, 0x9F, 0xE5, 0xD9, 0xBE, 0x78, 0x77, 0xF2, + 0x83, 0x54, 0xA2, 0x7F, 0xF8, 0x13, 0xAF, 0x2F, 0xFC, 0xB6, + 0xAB, 0x06, 0xF4, 0xEE, 0xB0, 0x9F, 0xDB, 0xF3, 0xD9, 0x92, + 0x91, 0x5F, 0xF9, 0x72, 0xEE, 0xDA, 0xE5, 0x4B, 0x80, 0xF8, + 0x4F, 0xC0, 0x89, 0x8B, 0xEF, 0xBF, 0x63, 0x05, 0x52, 0x53, + 0x17, 0x26, 0xC6, 0x19, 0x39, 0xD4, 0x47, 0xD0, 0xA7, 0x52, + 0xB1, 0xDA, 0x2C, 0x3D, 0x2D, 0xF3, 0xFD, 0x4F, 0x77, 0x29, + 0xAE, 0x64, 0x6D, 0xBF, 0x65, 0xBC, 0xF2, 0xC3, 0x95, 0xAF, + 0x1E, 0x6E, 0x6A, 0x94, 0x8E, 0x7A, 0xE7, 0xD1, 0xB1, 0xAA, + 0x9E, 0x9A, 0x7A, 0x61, 0xE8, 0x30, 0xAA, 0x3F, 0x84, 0x5F, + 0x53, 0xD8, 0xB7, 0x3B, 0x44, 0x40, 0xF3, 0x52, 0x6D, 0x41, + 0xAA, 0xA7, 0x8F, 0x57, 0x8F, 0x9F, 0xD6, 0x0E, 0x1D, 0x3B, + 0xB5, 0x08, 0xEC, 0xD9, 0x94, 0x79, 0x37, 0x07, 0x2D, 0x73, + 0x26, 0x77, 0x76, 0x74, 0xD4, 0xB3, 0x27, 0xB9, 0x8B, 0xC1, + 0x54, 0x17, 0x89, 0xA8, 0x1F, 0xAB, 0xED, 0x10, 0xF6, 0xAB, + 0xEC, 0x8D, 0x07, 0xD1, 0x75, 0x8D, 0x74, 0x32, 0x8A, 0xA2, + 0xFA, 0x88, 0xEC, 0xDA, 0x7B, 0xEE, 0xD1, 0xDC, 0x8F, 0x9F, + 0x77, 0x1C, 0x4C, 0xBC, 0xF7, 0xE1, 0xE4, 0x81, 0xE1, 0x03, + 0x9E, 0xA6, 0x03, 0x45, 0xB3, 0xC9, 0xFE, 0x54, 0x84, 0xB6, + 0x70, 0x51, 0x14, 0x0F, 0xEB, 0xF5, 0x36, 0x56, 0x5B, 0xD0, + 0xDB, 0x1D, 0xC4, 0xA7, 0xAB, 0x44, 0x23, 0x61, 0x32, 0x99, + 0xCC, 0x41, 0xD8, 0x99, 0xE9, 0x00, 0xA2, 0xB1, 0xEE, 0xB1, + 0x42, 0xA9, 0x8E, 0xEB, 0xBA, 0x9C, 0x3F, 0xDA, 0x83, 0x69, + 0xD9, 0xE4, 0x8C, 0x1A, 0xCB, 0x46, 0x8D, 0xAA, 0x65, 0x53, + 0x6B, 0x0A, 0x5A, 0xB6, 0x4D, 0x2C, 0xA4, 0x23, 0x5C, 0x49, + 0x38, 0x12, 0x63, 0x68, 0x64, 0x74, 0xBC, 0x03, 0x10, 0xAE, + 0x4C, 0xB9, 0xAE, 0x64, 0xF4, 0xE5, 0x5E, 0xBA, 0x02, 0x2A, + 0xAE, 0x10, 0x44, 0x83, 0x1A, 0xB3, 0x2B, 0xEB, 0xAC, 0x18, + 0x35, 0xCC, 0x5A, 0x93, 0xC7, 0x46, 0x85, 0xB6, 0xED, 0x20, + 0xA5, 0x8B, 0x70, 0x1C, 0xF6, 0xA7, 0xFB, 0x77, 0x77, 0x00, + 0xD9, 0xA5, 0x5C, 0xA9, 0x65, 0x35, 0x28, 0x99, 0x75, 0x2A, + 0xB5, 0x06, 0x21, 0x5D, 0xC1, 0x15, 0x36, 0xC7, 0x06, 0xBA, + 0xF0, 0x79, 0x5D, 0x14, 0x1C, 0xE2, 0x61, 0x15, 0x29, 0x1D, + 0x14, 0x24, 0x9A, 0xD7, 0xC3, 0xAD, 0x3B, 0xD9, 0xF5, 0xCE, + 0x13, 0xCB, 0x76, 0x24, 0xB9, 0x6F, 0xA0, 0x6F, 0xBC, 0x58, + 0xB5, 0x69, 0xD8, 0x92, 0x54, 0x3C, 0x4C, 0xAB, 0x2D, 0x10, + 0xC2, 0xE1, 0x59, 0x75, 0x03, 0x9F, 0xEA, 0x21, 0xF7, 0x47, + 0x09, 0x5C, 0x87, 0x46, 0xA3, 0x4E, 0x76, 0x71, 0x89, 0xD9, + 0x6F, 0xAF, 0x5E, 0x82, 0x8A, 0xA1, 0x02, 0xD0, 0x58, 0xFB, + 0xAC, 0x55, 0x2B, 0x7F, 0x3A, 0x3D, 0x6B, 0xA2, 0xA9, 0x5E, + 0xCE, 0x1E, 0xEE, 0x47, 0x91, 0x12, 0xC7, 0x71, 0x09, 0xEA, + 0x1E, 0x96, 0xF3, 0xAB, 0xDC, 0x5B, 0xFE, 0x93, 0x2B, 0x86, + 0x01, 0x96, 0x89, 0x59, 0xFC, 0xBD, 0x00, 0xF9, 0xF9, 0x7F, + 0x72, 0x50, 0x5E, 0x6A, 0x0F, 0xF7, 0x27, 0x26, 0x69, 0x94, + 0xA8, 0x9B, 0x25, 0x56, 0xD7, 0xCA, 0x94, 0xD6, 0x4D, 0x76, + 0x84, 0x34, 0x16, 0x73, 0x79, 0xA6, 0xAE, 0xCF, 0x50, 0x28, + 0x14, 0x58, 0x98, 0xBF, 0xC7, 0x6F, 0xB9, 0x5F, 0xF1, 0x79, + 0xDD, 0x37, 0xFF, 0x95, 0x83, 0xDC, 0xC2, 0xF4, 0xDD, 0x93, + 0x23, 0xE7, 0x64, 0x2C, 0xA4, 0x9F, 0x39, 0xF2, 0x52, 0x1A, + 0x21, 0x04, 0x5F, 0x7C, 0x7D, 0x93, 0xEF, 0x6E, 0xCC, 0xB1, + 0x56, 0x28, 0xF0, 0xC6, 0xEB, 0x83, 0x2C, 0x3E, 0xB8, 0x4F, + 0x44, 0x97, 0xE3, 0xF9, 0x99, 0x6F, 0x7E, 0x79, 0x5E, 0x94, + 0x35, 0x20, 0x46, 0xA0, 0xE7, 0xAD, 0x17, 0x5F, 0x3B, 0xF5, + 0x91, 0xF0, 0x06, 0xD3, 0x89, 0xF8, 0x0E, 0x1E, 0x3D, 0x2E, + 0x30, 0xD8, 0x9B, 0x60, 0x61, 0x7A, 0x3A, 0xCB, 0xC6, 0x93, + 0x8F, 0x69, 0x56, 0x1E, 0x00, 0x06, 0xB0, 0x01, 0xC8, 0xED, + 0x80, 0xF8, 0xDF, 0x31, 0x8D, 0x00, 0xE1, 0x58, 0x32, 0x9D, + 0x1C, 0x1A, 0xE8, 0x95, 0x33, 0x3F, 0xDF, 0x2A, 0x02, 0x36, + 0xD0, 0x00, 0x56, 0x81, 0xE2, 0xF3, 0x00, 0xDB, 0x5D, 0x79, + 0xB6, 0xCC, 0x72, 0x4B, 0x97, 0x5B, 0x8F, 0xFF, 0x02, 0x78, + 0x99, 0x27, 0xDD, 0x60, 0x50, 0xF9, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82, }; + +static const unsigned char data_css_siimple_min_css[] = { + /* /css/siimple.min.css */ + 0x2F, 0x63, 0x73, 0x73, 0x2F, 0x73, 0x69, 0x69, 0x6D, 0x70, 0x6C, 0x65, 0x2E, 0x6D, 0x69, 0x6E, 0x2E, 0x63, 0x73, 0x73, 0, + 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x20, 0x4F, 0x4B, 0x0D, 0x0A, 0x6C, 0x77, 0x49, + 0x50, 0x2F, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x20, 0x28, 0x68, + 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x61, 0x76, 0x61, + 0x6E, 0x6E, 0x61, 0x68, 0x2E, 0x6E, 0x6F, 0x6E, 0x67, 0x6E, + 0x75, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x70, 0x72, 0x6F, 0x6A, + 0x65, 0x63, 0x74, 0x73, 0x2F, 0x6C, 0x77, 0x69, 0x70, 0x29, + 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, + 0x74, 0x79, 0x70, 0x65, 0x3A, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x2F, 0x63, 0x73, 0x73, 0x0D, 0x0A, 0x0D, 0x0A, 0x0D, 0x0A, + 0x2F, 0x2A, 0x2A, 0x0A, 0x20, 0x2A, 0x20, 0x73, 0x69, 0x69, + 0x6D, 0x70, 0x6C, 0x65, 0x20, 0x2D, 0x20, 0x4D, 0x69, 0x6E, + 0x69, 0x6D, 0x61, 0x6C, 0x20, 0x43, 0x53, 0x53, 0x20, 0x66, + 0x72, 0x61, 0x6D, 0x65, 0x77, 0x6F, 0x72, 0x6B, 0x20, 0x66, + 0x6F, 0x72, 0x20, 0x66, 0x6C, 0x61, 0x74, 0x20, 0x61, 0x6E, + 0x64, 0x20, 0x63, 0x6C, 0x65, 0x61, 0x6E, 0x20, 0x64, 0x65, + 0x73, 0x69, 0x67, 0x6E, 0x73, 0x2E, 0x0A, 0x20, 0x2A, 0x20, + 0x40, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x76, + 0x31, 0x2E, 0x33, 0x2E, 0x37, 0x0A, 0x20, 0x2A, 0x20, 0x40, + 0x6C, 0x69, 0x6E, 0x6B, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, + 0x3A, 0x2F, 0x2F, 0x73, 0x69, 0x69, 0x6D, 0x70, 0x6C, 0x65, + 0x2E, 0x6A, 0x75, 0x61, 0x6E, 0x65, 0x73, 0x2E, 0x78, 0x79, + 0x7A, 0x2F, 0x0A, 0x20, 0x2A, 0x20, 0x40, 0x6C, 0x69, 0x63, + 0x65, 0x6E, 0x73, 0x65, 0x20, 0x4D, 0x49, 0x54, 0x0A, 0x20, + 0x2A, 0x2F, 0x0A, 0x0A, 0x40, 0x69, 0x6D, 0x70, 0x6F, 0x72, + 0x74, 0x20, 0x75, 0x72, 0x6C, 0x28, 0x68, 0x74, 0x74, 0x70, + 0x73, 0x3A, 0x2F, 0x2F, 0x66, 0x6F, 0x6E, 0x74, 0x73, 0x2E, + 0x67, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x61, 0x70, 0x69, 0x73, + 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x63, 0x73, 0x73, 0x3F, 0x66, + 0x61, 0x6D, 0x69, 0x6C, 0x79, 0x3D, 0x4F, 0x70, 0x65, 0x6E, + 0x2B, 0x53, 0x61, 0x6E, 0x73, 0x3A, 0x34, 0x30, 0x30, 0x2C, + 0x33, 0x30, 0x30, 0x29, 0x3B, 0x6F, 0x6C, 0x2C, 0x6F, 0x6C, + 0x20, 0x6C, 0x69, 0x2C, 0x70, 0x2C, 0x75, 0x6C, 0x2C, 0x75, + 0x6C, 0x20, 0x6C, 0x69, 0x7B, 0x6C, 0x69, 0x6E, 0x65, 0x2D, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x32, 0x38, 0x70, + 0x78, 0x7D, 0x2E, 0x61, 0x6C, 0x65, 0x72, 0x74, 0x2C, 0x70, + 0x72, 0x65, 0x7B, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x63, + 0x61, 0x6C, 0x63, 0x28, 0x31, 0x30, 0x30, 0x25, 0x20, 0x2D, + 0x20, 0x33, 0x30, 0x70, 0x78, 0x29, 0x7D, 0x2E, 0x61, 0x6C, + 0x65, 0x72, 0x74, 0x2C, 0x2E, 0x62, 0x74, 0x6E, 0x7B, 0x62, + 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x72, 0x61, 0x64, 0x69, + 0x75, 0x73, 0x3A, 0x35, 0x70, 0x78, 0x7D, 0x2E, 0x68, 0x65, + 0x61, 0x72, 0x74, 0x3A, 0x61, 0x66, 0x74, 0x65, 0x72, 0x7B, + 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x3A, 0x22, 0x5C, + 0x32, 0x37, 0x36, 0x34, 0x22, 0x3B, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x3A, 0x23, 0x66, 0x34, 0x35, 0x36, 0x36, 0x30, 0x7D, + 0x62, 0x6F, 0x64, 0x79, 0x2C, 0x68, 0x31, 0x2C, 0x68, 0x32, + 0x2C, 0x68, 0x33, 0x2C, 0x68, 0x34, 0x2C, 0x68, 0x35, 0x2C, + 0x68, 0x36, 0x7B, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, + 0x35, 0x32, 0x36, 0x34, 0x37, 0x35, 0x7D, 0x62, 0x6F, 0x64, + 0x79, 0x7B, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x3A, 0x30, + 0x3B, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x30, + 0x3B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x66, 0x61, 0x6D, 0x69, + 0x6C, 0x79, 0x3A, 0x27, 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x53, + 0x61, 0x6E, 0x73, 0x27, 0x3B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x31, 0x36, 0x70, 0x78, 0x3B, + 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x33, 0x30, 0x30, 0x3B, 0x62, 0x61, 0x63, 0x6B, + 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, + 0x6F, 0x72, 0x3A, 0x23, 0x66, 0x66, 0x66, 0x7D, 0x2E, 0x61, + 0x6C, 0x65, 0x72, 0x74, 0x20, 0x61, 0x2C, 0x61, 0x7B, 0x74, + 0x65, 0x78, 0x74, 0x2D, 0x64, 0x65, 0x63, 0x6F, 0x72, 0x61, + 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x6E, 0x6F, 0x6E, 0x65, 0x3B, + 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x34, 0x30, 0x30, 0x7D, 0x62, 0x6C, 0x6F, 0x63, + 0x6B, 0x71, 0x75, 0x6F, 0x74, 0x65, 0x7B, 0x62, 0x6F, 0x72, + 0x64, 0x65, 0x72, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x34, + 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, 0x23, + 0x36, 0x61, 0x37, 0x65, 0x39, 0x35, 0x3B, 0x70, 0x61, 0x64, + 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x35, 0x70, 0x78, 0x20, 0x35, + 0x70, 0x78, 0x20, 0x35, 0x70, 0x78, 0x20, 0x32, 0x30, 0x70, + 0x78, 0x7D, 0x61, 0x7B, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x23, 0x30, 0x39, 0x61, 0x30, 0x66, 0x36, 0x3B, 0x74, 0x72, + 0x61, 0x6E, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x61, + 0x6C, 0x6C, 0x20, 0x2E, 0x33, 0x73, 0x7D, 0x61, 0x3A, 0x68, + 0x6F, 0x76, 0x65, 0x72, 0x7B, 0x74, 0x65, 0x78, 0x74, 0x2D, + 0x64, 0x65, 0x63, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, + 0x3A, 0x75, 0x6E, 0x64, 0x65, 0x72, 0x6C, 0x69, 0x6E, 0x65, + 0x3B, 0x63, 0x75, 0x72, 0x73, 0x6F, 0x72, 0x3A, 0x70, 0x6F, + 0x69, 0x6E, 0x74, 0x65, 0x72, 0x7D, 0x70, 0x7B, 0x6D, 0x61, + 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, + 0x6D, 0x3A, 0x32, 0x30, 0x70, 0x78, 0x3B, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, 0x30, 0x3B, + 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, 0x62, 0x6C, + 0x6F, 0x63, 0x6B, 0x7D, 0x6F, 0x6C, 0x2C, 0x75, 0x6C, 0x7B, + 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x62, 0x6F, 0x74, + 0x74, 0x6F, 0x6D, 0x3A, 0x31, 0x36, 0x70, 0x78, 0x3B, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, 0x3A, + 0x30, 0x7D, 0x2E, 0x61, 0x6C, 0x65, 0x72, 0x74, 0x2C, 0x68, + 0x31, 0x2C, 0x68, 0x32, 0x2C, 0x68, 0x33, 0x2C, 0x68, 0x34, + 0x2C, 0x68, 0x35, 0x2C, 0x68, 0x36, 0x7B, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x33, + 0x30, 0x30, 0x3B, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, + 0x74, 0x6F, 0x70, 0x3A, 0x30, 0x3B, 0x6D, 0x61, 0x72, 0x67, + 0x69, 0x6E, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x3A, + 0x32, 0x30, 0x70, 0x78, 0x3B, 0x64, 0x69, 0x73, 0x70, 0x6C, + 0x61, 0x79, 0x3A, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x7D, 0x73, + 0x6D, 0x61, 0x6C, 0x6C, 0x7B, 0x63, 0x6F, 0x6C, 0x6F, 0x72, + 0x3A, 0x23, 0x36, 0x61, 0x37, 0x65, 0x39, 0x35, 0x3B, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x31, + 0x34, 0x70, 0x78, 0x7D, 0x68, 0x31, 0x7B, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x33, 0x36, 0x70, + 0x78, 0x3B, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x3A, 0x35, 0x30, 0x70, 0x78, 0x7D, 0x68, + 0x32, 0x7B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x33, 0x32, 0x70, 0x78, 0x3B, 0x6C, 0x69, 0x6E, + 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x34, + 0x36, 0x70, 0x78, 0x7D, 0x68, 0x33, 0x7B, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x32, 0x38, 0x70, + 0x78, 0x3B, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x3A, 0x34, 0x32, 0x70, 0x78, 0x7D, 0x68, + 0x34, 0x7B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x32, 0x34, 0x70, 0x78, 0x3B, 0x6C, 0x69, 0x6E, + 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x33, + 0x38, 0x70, 0x78, 0x7D, 0x68, 0x35, 0x7B, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x32, 0x30, 0x70, + 0x78, 0x3B, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x3A, 0x33, 0x34, 0x70, 0x78, 0x7D, 0x2E, + 0x61, 0x6C, 0x65, 0x72, 0x74, 0x2C, 0x2E, 0x62, 0x74, 0x6E, + 0x2C, 0x68, 0x36, 0x7B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, + 0x69, 0x7A, 0x65, 0x3A, 0x31, 0x36, 0x70, 0x78, 0x7D, 0x68, + 0x36, 0x7B, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x3A, 0x33, 0x30, 0x70, 0x78, 0x7D, 0x2E, + 0x61, 0x6C, 0x65, 0x72, 0x74, 0x7B, 0x74, 0x65, 0x78, 0x74, + 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x6C, 0x65, 0x66, + 0x74, 0x3B, 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x3A, 0x31, 0x70, 0x78, 0x3B, 0x62, + 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x73, 0x74, 0x79, 0x6C, + 0x65, 0x3A, 0x73, 0x6F, 0x6C, 0x69, 0x64, 0x3B, 0x62, 0x61, + 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x45, 0x31, 0x46, 0x35, + 0x46, 0x45, 0x3B, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, + 0x30, 0x33, 0x41, 0x39, 0x46, 0x34, 0x3B, 0x62, 0x6F, 0x72, + 0x64, 0x65, 0x72, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x23, 0x30, 0x33, 0x41, 0x39, 0x46, 0x34, 0x3B, 0x70, 0x61, + 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x31, 0x36, 0x70, 0x78, + 0x20, 0x31, 0x34, 0x70, 0x78, 0x3B, 0x70, 0x61, 0x64, 0x64, + 0x69, 0x6E, 0x67, 0x3A, 0x31, 0x36, 0x70, 0x78, 0x20, 0x31, + 0x34, 0x70, 0x78, 0x3B, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, + 0x67, 0x3A, 0x31, 0x36, 0x70, 0x78, 0x20, 0x31, 0x34, 0x70, + 0x78, 0x7D, 0x2E, 0x62, 0x74, 0x6E, 0x2C, 0x2E, 0x62, 0x74, + 0x6E, 0x2D, 0x6F, 0x75, 0x74, 0x6C, 0x69, 0x6E, 0x65, 0x7B, + 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x66, 0x61, 0x6D, 0x69, 0x6C, + 0x79, 0x3A, 0x27, 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x53, 0x61, + 0x6E, 0x73, 0x27, 0x3B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x33, 0x30, 0x30, 0x3B, + 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, 0x69, 0x6E, + 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x62, 0x6C, 0x6F, 0x63, 0x6B, + 0x3B, 0x74, 0x72, 0x61, 0x6E, 0x73, 0x69, 0x74, 0x69, 0x6F, + 0x6E, 0x3A, 0x61, 0x6C, 0x6C, 0x20, 0x2E, 0x33, 0x73, 0x3B, + 0x2D, 0x77, 0x65, 0x62, 0x6B, 0x69, 0x74, 0x2D, 0x74, 0x6F, + 0x75, 0x63, 0x68, 0x2D, 0x63, 0x61, 0x6C, 0x6C, 0x6F, 0x75, + 0x74, 0x3A, 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x2D, 0x6B, 0x68, + 0x74, 0x6D, 0x6C, 0x2D, 0x75, 0x73, 0x65, 0x72, 0x2D, 0x73, + 0x65, 0x6C, 0x65, 0x63, 0x74, 0x3A, 0x6E, 0x6F, 0x6E, 0x65, + 0x3B, 0x2D, 0x6D, 0x6F, 0x7A, 0x2D, 0x75, 0x73, 0x65, 0x72, + 0x2D, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x3A, 0x6E, 0x6F, + 0x6E, 0x65, 0x3B, 0x2D, 0x6D, 0x73, 0x2D, 0x75, 0x73, 0x65, + 0x72, 0x2D, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x3A, 0x6E, + 0x6F, 0x6E, 0x65, 0x3B, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, + 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x63, 0x65, 0x6E, 0x74, 0x65, + 0x72, 0x3B, 0x63, 0x75, 0x72, 0x73, 0x6F, 0x72, 0x3A, 0x70, + 0x6F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x3B, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x3A, 0x35, 0x70, 0x78, 0x20, 0x35, 0x70, + 0x78, 0x20, 0x32, 0x30, 0x70, 0x78, 0x7D, 0x2E, 0x61, 0x6C, + 0x65, 0x72, 0x74, 0x2D, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x7B, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x44, 0x33, 0x32, + 0x46, 0x32, 0x46, 0x3B, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, + 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, + 0x3A, 0x23, 0x46, 0x46, 0x45, 0x42, 0x45, 0x45, 0x3B, 0x62, + 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x3A, 0x23, 0x46, 0x34, 0x34, 0x33, 0x33, 0x36, 0x7D, + 0x2E, 0x61, 0x6C, 0x65, 0x72, 0x74, 0x2D, 0x77, 0x61, 0x72, + 0x6E, 0x69, 0x6E, 0x67, 0x7B, 0x62, 0x61, 0x63, 0x6B, 0x67, + 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x3A, 0x23, 0x46, 0x46, 0x46, 0x38, 0x45, 0x31, 0x3B, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x46, 0x46, 0x38, + 0x46, 0x30, 0x30, 0x3B, 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, + 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x46, 0x46, + 0x43, 0x31, 0x30, 0x37, 0x7D, 0x2E, 0x61, 0x6C, 0x65, 0x72, + 0x74, 0x2D, 0x64, 0x6F, 0x6E, 0x65, 0x7B, 0x62, 0x61, 0x63, + 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, + 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x45, 0x38, 0x46, 0x35, 0x45, + 0x39, 0x3B, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x33, + 0x38, 0x38, 0x45, 0x33, 0x43, 0x3B, 0x62, 0x6F, 0x72, 0x64, + 0x65, 0x72, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, + 0x34, 0x43, 0x41, 0x46, 0x35, 0x30, 0x7D, 0x2E, 0x62, 0x74, + 0x6E, 0x7B, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x64, 0x65, 0x63, + 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x6E, 0x6F, + 0x6E, 0x65, 0x21, 0x69, 0x6D, 0x70, 0x6F, 0x72, 0x74, 0x61, + 0x6E, 0x74, 0x3B, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x32, 0x38, 0x70, 0x78, 0x3B, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x66, 0x66, 0x66, + 0x3B, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, + 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x30, + 0x39, 0x61, 0x30, 0x66, 0x36, 0x3B, 0x62, 0x6F, 0x72, 0x64, + 0x65, 0x72, 0x3A, 0x30, 0x3B, 0x70, 0x61, 0x64, 0x64, 0x69, + 0x6E, 0x67, 0x3A, 0x35, 0x70, 0x78, 0x20, 0x32, 0x35, 0x70, + 0x78, 0x7D, 0x2E, 0x62, 0x74, 0x6E, 0x3A, 0x68, 0x6F, 0x76, + 0x65, 0x72, 0x7B, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x64, 0x65, + 0x63, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x6E, + 0x6F, 0x6E, 0x65, 0x3B, 0x6F, 0x70, 0x61, 0x63, 0x69, 0x74, + 0x79, 0x3A, 0x2E, 0x38, 0x7D, 0x2E, 0x62, 0x74, 0x6E, 0x2D, + 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x7B, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x31, 0x34, 0x70, 0x78, + 0x21, 0x69, 0x6D, 0x70, 0x6F, 0x72, 0x74, 0x61, 0x6E, 0x74, + 0x3B, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x32, 0x30, 0x70, 0x78, 0x21, 0x69, 0x6D, + 0x70, 0x6F, 0x72, 0x74, 0x61, 0x6E, 0x74, 0x3B, 0x70, 0x61, + 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x34, 0x70, 0x78, 0x20, + 0x31, 0x35, 0x70, 0x78, 0x21, 0x69, 0x6D, 0x70, 0x6F, 0x72, + 0x74, 0x61, 0x6E, 0x74, 0x7D, 0x2E, 0x62, 0x74, 0x6E, 0x2D, + 0x62, 0x69, 0x67, 0x7B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, + 0x69, 0x7A, 0x65, 0x3A, 0x32, 0x32, 0x70, 0x78, 0x21, 0x69, + 0x6D, 0x70, 0x6F, 0x72, 0x74, 0x61, 0x6E, 0x74, 0x3B, 0x6C, + 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x3A, 0x33, 0x34, 0x70, 0x78, 0x21, 0x69, 0x6D, 0x70, 0x6F, + 0x72, 0x74, 0x61, 0x6E, 0x74, 0x3B, 0x70, 0x61, 0x64, 0x64, + 0x69, 0x6E, 0x67, 0x3A, 0x38, 0x70, 0x78, 0x20, 0x33, 0x30, + 0x70, 0x78, 0x21, 0x69, 0x6D, 0x70, 0x6F, 0x72, 0x74, 0x61, + 0x6E, 0x74, 0x7D, 0x2E, 0x62, 0x74, 0x6E, 0x2D, 0x6F, 0x75, + 0x74, 0x6C, 0x69, 0x6E, 0x65, 0x2C, 0x70, 0x72, 0x65, 0x7B, + 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x32, 0x38, 0x70, 0x78, 0x7D, 0x2E, 0x62, 0x74, + 0x6E, 0x2D, 0x6F, 0x75, 0x74, 0x6C, 0x69, 0x6E, 0x65, 0x7B, + 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, + 0x31, 0x36, 0x70, 0x78, 0x3B, 0x74, 0x65, 0x78, 0x74, 0x2D, + 0x64, 0x65, 0x63, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, + 0x3A, 0x6E, 0x6F, 0x6E, 0x65, 0x21, 0x69, 0x6D, 0x70, 0x6F, + 0x72, 0x74, 0x61, 0x6E, 0x74, 0x3B, 0x62, 0x6F, 0x72, 0x64, + 0x65, 0x72, 0x2D, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3A, + 0x35, 0x70, 0x78, 0x3B, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x23, 0x30, 0x39, 0x61, 0x30, 0x66, 0x36, 0x3B, 0x62, 0x61, + 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x74, 0x72, 0x61, 0x6E, 0x73, + 0x70, 0x61, 0x72, 0x65, 0x6E, 0x74, 0x3B, 0x62, 0x6F, 0x72, + 0x64, 0x65, 0x72, 0x3A, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6F, + 0x6C, 0x69, 0x64, 0x20, 0x23, 0x30, 0x39, 0x61, 0x30, 0x66, + 0x36, 0x3B, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, + 0x35, 0x70, 0x78, 0x20, 0x32, 0x35, 0x70, 0x78, 0x7D, 0x2E, + 0x62, 0x74, 0x6E, 0x2D, 0x6F, 0x75, 0x74, 0x6C, 0x69, 0x6E, + 0x65, 0x3A, 0x68, 0x6F, 0x76, 0x65, 0x72, 0x7B, 0x74, 0x65, + 0x78, 0x74, 0x2D, 0x64, 0x65, 0x63, 0x6F, 0x72, 0x61, 0x74, + 0x69, 0x6F, 0x6E, 0x3A, 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x66, 0x66, 0x66, 0x3B, + 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, + 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x30, 0x39, + 0x61, 0x30, 0x66, 0x36, 0x7D, 0x63, 0x6F, 0x64, 0x65, 0x2C, + 0x70, 0x72, 0x65, 0x7B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x66, + 0x61, 0x6D, 0x69, 0x6C, 0x79, 0x3A, 0x27, 0x4F, 0x70, 0x65, + 0x6E, 0x20, 0x53, 0x61, 0x6E, 0x73, 0x27, 0x3B, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x31, 0x36, + 0x70, 0x78, 0x3B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x33, 0x30, 0x30, 0x3B, 0x62, + 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x72, 0x61, 0x64, 0x69, + 0x75, 0x73, 0x3A, 0x35, 0x70, 0x78, 0x3B, 0x62, 0x61, 0x63, + 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, + 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x66, 0x31, 0x66, 0x35, 0x66, + 0x61, 0x7D, 0x63, 0x6F, 0x64, 0x65, 0x7B, 0x63, 0x6F, 0x6C, + 0x6F, 0x72, 0x3A, 0x23, 0x30, 0x39, 0x61, 0x30, 0x66, 0x36, + 0x3B, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2D, 0x6C, + 0x65, 0x66, 0x74, 0x3A, 0x36, 0x70, 0x78, 0x3B, 0x70, 0x61, + 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2D, 0x72, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x36, 0x70, 0x78, 0x7D, 0x70, 0x72, 0x65, 0x7B, + 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, 0x62, 0x6C, + 0x6F, 0x63, 0x6B, 0x3B, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, + 0x67, 0x3A, 0x31, 0x34, 0x70, 0x78, 0x3B, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, + 0x3A, 0x32, 0x30, 0x70, 0x78, 0x3B, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x3A, 0x23, 0x35, 0x32, 0x36, 0x34, 0x37, 0x35, 0x3B, + 0x6F, 0x76, 0x65, 0x72, 0x66, 0x6C, 0x6F, 0x77, 0x2D, 0x78, + 0x3A, 0x61, 0x75, 0x74, 0x6F, 0x7D, 0x2E, 0x66, 0x6F, 0x72, + 0x6D, 0x2D, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6C, 0x65, 0x64, 0x5D, 0x2C, 0x2E, 0x66, + 0x6F, 0x72, 0x6D, 0x2D, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, + 0x74, 0x79, 0x70, 0x65, 0x3D, 0x74, 0x65, 0x78, 0x74, 0x5D, + 0x2C, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, 0x69, 0x6E, 0x70, + 0x75, 0x74, 0x5B, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x70, 0x61, + 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x5D, 0x2C, 0x2E, 0x66, + 0x6F, 0x72, 0x6D, 0x2D, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, + 0x74, 0x79, 0x70, 0x65, 0x3D, 0x6E, 0x75, 0x6D, 0x62, 0x65, + 0x72, 0x5D, 0x2C, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, 0x69, + 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x74, 0x79, 0x70, 0x65, 0x3D, + 0x65, 0x6D, 0x61, 0x69, 0x6C, 0x5D, 0x2C, 0x2E, 0x66, 0x6F, + 0x72, 0x6D, 0x2D, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x74, + 0x79, 0x70, 0x65, 0x3D, 0x64, 0x61, 0x74, 0x65, 0x5D, 0x7B, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x35, 0x32, 0x36, + 0x34, 0x37, 0x35, 0x3B, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, + 0x67, 0x3A, 0x31, 0x30, 0x70, 0x78, 0x3B, 0x6F, 0x75, 0x74, + 0x6C, 0x69, 0x6E, 0x65, 0x3A, 0x30, 0x3B, 0x62, 0x6F, 0x78, + 0x2D, 0x73, 0x69, 0x7A, 0x69, 0x6E, 0x67, 0x3A, 0x62, 0x6F, + 0x72, 0x64, 0x65, 0x72, 0x2D, 0x62, 0x6F, 0x78, 0x3B, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x3A, 0x30, 0x20, 0x35, 0x70, + 0x78, 0x20, 0x32, 0x30, 0x70, 0x78, 0x3B, 0x66, 0x6F, 0x6E, + 0x74, 0x2D, 0x66, 0x61, 0x6D, 0x69, 0x6C, 0x79, 0x3A, 0x27, + 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x53, 0x61, 0x6E, 0x73, 0x27, + 0x3B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, + 0x3A, 0x31, 0x36, 0x70, 0x78, 0x3B, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x33, 0x30, + 0x30, 0x3B, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, + 0x69, 0x6E, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x62, 0x6C, 0x6F, + 0x63, 0x6B, 0x3B, 0x74, 0x72, 0x61, 0x6E, 0x73, 0x69, 0x74, + 0x69, 0x6F, 0x6E, 0x3A, 0x61, 0x6C, 0x6C, 0x20, 0x2E, 0x33, + 0x73, 0x3B, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x34, + 0x30, 0x70, 0x78, 0x7D, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, + 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x74, 0x79, 0x70, 0x65, + 0x3D, 0x74, 0x65, 0x78, 0x74, 0x5D, 0x2C, 0x2E, 0x66, 0x6F, + 0x72, 0x6D, 0x2D, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x74, + 0x79, 0x70, 0x65, 0x3D, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, + 0x72, 0x64, 0x5D, 0x2C, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, + 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x74, 0x79, 0x70, 0x65, + 0x3D, 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x5D, 0x2C, 0x2E, + 0x66, 0x6F, 0x72, 0x6D, 0x2D, 0x69, 0x6E, 0x70, 0x75, 0x74, + 0x5B, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x65, 0x6D, 0x61, 0x69, + 0x6C, 0x5D, 0x7B, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x31, + 0x30, 0x30, 0x25, 0x3B, 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, + 0x3A, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, + 0x20, 0x23, 0x64, 0x31, 0x65, 0x31, 0x65, 0x38, 0x3B, 0x62, + 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x72, 0x61, 0x64, 0x69, + 0x75, 0x73, 0x3A, 0x35, 0x70, 0x78, 0x3B, 0x6C, 0x69, 0x6E, + 0x65, 0x2D, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x34, + 0x30, 0x70, 0x78, 0x7D, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, + 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x74, 0x79, 0x70, 0x65, + 0x3D, 0x74, 0x65, 0x78, 0x74, 0x5D, 0x3A, 0x66, 0x6F, 0x63, + 0x75, 0x73, 0x2C, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, 0x69, + 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x74, 0x79, 0x70, 0x65, 0x3D, + 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x5D, 0x3A, + 0x66, 0x6F, 0x63, 0x75, 0x73, 0x2C, 0x2E, 0x66, 0x6F, 0x72, + 0x6D, 0x2D, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x74, 0x79, + 0x70, 0x65, 0x3D, 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x5D, + 0x3A, 0x66, 0x6F, 0x63, 0x75, 0x73, 0x2C, 0x2E, 0x66, 0x6F, + 0x72, 0x6D, 0x2D, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x74, + 0x79, 0x70, 0x65, 0x3D, 0x65, 0x6D, 0x61, 0x69, 0x6C, 0x5D, + 0x3A, 0x66, 0x6F, 0x63, 0x75, 0x73, 0x7B, 0x62, 0x6F, 0x72, + 0x64, 0x65, 0x72, 0x3A, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6F, + 0x6C, 0x69, 0x64, 0x20, 0x23, 0x30, 0x39, 0x61, 0x30, 0x66, + 0x36, 0x7D, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, 0x69, 0x6E, + 0x70, 0x75, 0x74, 0x5B, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x64, + 0x61, 0x74, 0x65, 0x5D, 0x7B, 0x62, 0x6F, 0x72, 0x64, 0x65, + 0x72, 0x3A, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, + 0x64, 0x20, 0x23, 0x64, 0x31, 0x65, 0x31, 0x65, 0x38, 0x3B, + 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x72, 0x61, 0x64, + 0x69, 0x75, 0x73, 0x3A, 0x35, 0x70, 0x78, 0x3B, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x3A, 0x61, 0x75, 0x74, 0x6F, 0x21, 0x69, + 0x6D, 0x70, 0x6F, 0x72, 0x74, 0x61, 0x6E, 0x74, 0x7D, 0x2E, + 0x66, 0x6F, 0x72, 0x6D, 0x2D, 0x69, 0x6E, 0x70, 0x75, 0x74, + 0x5B, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x64, 0x61, 0x74, 0x65, + 0x5D, 0x3A, 0x66, 0x6F, 0x63, 0x75, 0x73, 0x7B, 0x62, 0x6F, + 0x72, 0x64, 0x65, 0x72, 0x3A, 0x31, 0x70, 0x78, 0x20, 0x73, + 0x6F, 0x6C, 0x69, 0x64, 0x20, 0x23, 0x30, 0x39, 0x61, 0x30, + 0x66, 0x36, 0x7D, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, 0x69, + 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6C, 0x65, 0x64, 0x5D, 0x7B, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x3A, 0x31, 0x30, 0x30, 0x25, 0x3B, 0x62, 0x6F, 0x72, 0x64, + 0x65, 0x72, 0x3A, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, + 0x69, 0x64, 0x20, 0x23, 0x64, 0x31, 0x65, 0x31, 0x65, 0x38, + 0x3B, 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x72, 0x61, + 0x64, 0x69, 0x75, 0x73, 0x3A, 0x35, 0x70, 0x78, 0x3B, 0x63, + 0x75, 0x72, 0x73, 0x6F, 0x72, 0x3A, 0x6E, 0x6F, 0x74, 0x2D, + 0x61, 0x6C, 0x6C, 0x6F, 0x77, 0x65, 0x64, 0x3B, 0x62, 0x61, + 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x64, 0x31, 0x65, 0x31, + 0x65, 0x38, 0x7D, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, 0x69, + 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6C, 0x65, 0x64, 0x5D, 0x3A, 0x66, 0x6F, 0x63, 0x75, 0x73, + 0x7B, 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x3A, 0x31, 0x70, + 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, 0x23, 0x30, + 0x39, 0x61, 0x30, 0x66, 0x36, 0x7D, 0x2E, 0x66, 0x6F, 0x72, + 0x6D, 0x2D, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x74, 0x79, + 0x70, 0x65, 0x3D, 0x73, 0x75, 0x62, 0x6D, 0x69, 0x74, 0x5D, + 0x2C, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, 0x69, 0x6E, 0x70, + 0x75, 0x74, 0x5B, 0x74, 0x79, 0x70, 0x65, 0x3D, 0x62, 0x75, + 0x74, 0x74, 0x6F, 0x6E, 0x5D, 0x7B, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x66, 0x61, 0x6D, 0x69, 0x6C, 0x79, 0x3A, 0x27, 0x4F, + 0x70, 0x65, 0x6E, 0x20, 0x53, 0x61, 0x6E, 0x73, 0x27, 0x3B, + 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, + 0x31, 0x36, 0x70, 0x78, 0x3B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x33, 0x30, 0x30, + 0x3B, 0x2D, 0x77, 0x65, 0x62, 0x6B, 0x69, 0x74, 0x2D, 0x74, + 0x6F, 0x75, 0x63, 0x68, 0x2D, 0x63, 0x61, 0x6C, 0x6C, 0x6F, + 0x75, 0x74, 0x3A, 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x2D, 0x6B, + 0x68, 0x74, 0x6D, 0x6C, 0x2D, 0x75, 0x73, 0x65, 0x72, 0x2D, + 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x3A, 0x6E, 0x6F, 0x6E, + 0x65, 0x3B, 0x2D, 0x6D, 0x6F, 0x7A, 0x2D, 0x75, 0x73, 0x65, + 0x72, 0x2D, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x3A, 0x6E, + 0x6F, 0x6E, 0x65, 0x3B, 0x2D, 0x6D, 0x73, 0x2D, 0x75, 0x73, + 0x65, 0x72, 0x2D, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x3A, + 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x74, 0x65, 0x78, 0x74, 0x2D, + 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x63, 0x65, 0x6E, 0x74, + 0x65, 0x72, 0x3B, 0x74, 0x65, 0x78, 0x74, 0x2D, 0x64, 0x65, + 0x63, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x6E, + 0x6F, 0x6E, 0x65, 0x21, 0x69, 0x6D, 0x70, 0x6F, 0x72, 0x74, + 0x61, 0x6E, 0x74, 0x3B, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x32, 0x38, 0x70, 0x78, + 0x3B, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, 0x69, + 0x6E, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x62, 0x6C, 0x6F, 0x63, + 0x6B, 0x3B, 0x63, 0x75, 0x72, 0x73, 0x6F, 0x72, 0x3A, 0x70, + 0x6F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x3B, 0x62, 0x6F, 0x72, + 0x64, 0x65, 0x72, 0x2D, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, + 0x3A, 0x35, 0x70, 0x78, 0x3B, 0x74, 0x72, 0x61, 0x6E, 0x73, + 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x61, 0x6C, 0x6C, 0x20, + 0x2E, 0x33, 0x73, 0x3B, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x23, 0x66, 0x66, 0x66, 0x3B, 0x62, 0x61, 0x63, 0x6B, 0x67, + 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x3A, 0x23, 0x30, 0x39, 0x61, 0x30, 0x66, 0x36, 0x3B, + 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x3A, 0x30, 0x3B, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x3A, 0x35, 0x70, 0x78, 0x20, + 0x35, 0x70, 0x78, 0x20, 0x32, 0x30, 0x70, 0x78, 0x3B, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x35, 0x70, 0x78, + 0x20, 0x32, 0x35, 0x70, 0x78, 0x7D, 0x2E, 0x66, 0x6F, 0x72, + 0x6D, 0x2D, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x2C, 0x2E, + 0x66, 0x6F, 0x72, 0x6D, 0x2D, 0x74, 0x65, 0x78, 0x74, 0x61, + 0x72, 0x65, 0x61, 0x7B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x66, + 0x61, 0x6D, 0x69, 0x6C, 0x79, 0x3A, 0x27, 0x4F, 0x70, 0x65, + 0x6E, 0x20, 0x53, 0x61, 0x6E, 0x73, 0x27, 0x3B, 0x66, 0x6F, + 0x6E, 0x74, 0x2D, 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x31, 0x36, + 0x70, 0x78, 0x3B, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, + 0x3A, 0x69, 0x6E, 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x62, 0x6C, + 0x6F, 0x63, 0x6B, 0x3B, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, + 0x31, 0x30, 0x30, 0x25, 0x3B, 0x74, 0x72, 0x61, 0x6E, 0x73, + 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x61, 0x6C, 0x6C, 0x20, + 0x2E, 0x33, 0x73, 0x3B, 0x6F, 0x75, 0x74, 0x6C, 0x69, 0x6E, + 0x65, 0x3A, 0x30, 0x3B, 0x62, 0x6F, 0x78, 0x2D, 0x73, 0x69, + 0x7A, 0x69, 0x6E, 0x67, 0x3A, 0x62, 0x6F, 0x72, 0x64, 0x65, + 0x72, 0x2D, 0x62, 0x6F, 0x78, 0x3B, 0x6D, 0x61, 0x72, 0x67, + 0x69, 0x6E, 0x3A, 0x30, 0x20, 0x35, 0x70, 0x78, 0x20, 0x32, + 0x30, 0x70, 0x78, 0x3B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x33, 0x30, 0x30, 0x3B, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x35, 0x32, 0x36, + 0x34, 0x37, 0x35, 0x7D, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, + 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x74, 0x79, 0x70, 0x65, + 0x3D, 0x73, 0x75, 0x62, 0x6D, 0x69, 0x74, 0x5D, 0x3A, 0x68, + 0x6F, 0x76, 0x65, 0x72, 0x2C, 0x2E, 0x66, 0x6F, 0x72, 0x6D, + 0x2D, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5B, 0x74, 0x79, 0x70, + 0x65, 0x3D, 0x62, 0x75, 0x74, 0x74, 0x6F, 0x6E, 0x5D, 0x3A, + 0x68, 0x6F, 0x76, 0x65, 0x72, 0x7B, 0x74, 0x65, 0x78, 0x74, + 0x2D, 0x64, 0x65, 0x63, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, + 0x6E, 0x3A, 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x6F, 0x70, 0x61, + 0x63, 0x69, 0x74, 0x79, 0x3A, 0x2E, 0x38, 0x7D, 0x2E, 0x66, + 0x6F, 0x72, 0x6D, 0x2D, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, + 0x7B, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x36, + 0x70, 0x78, 0x20, 0x31, 0x30, 0x70, 0x78, 0x20, 0x31, 0x30, + 0x70, 0x78, 0x3B, 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x3A, + 0x31, 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, + 0x23, 0x64, 0x31, 0x65, 0x31, 0x65, 0x38, 0x3B, 0x62, 0x6F, + 0x72, 0x64, 0x65, 0x72, 0x2D, 0x72, 0x61, 0x64, 0x69, 0x75, + 0x73, 0x3A, 0x35, 0x70, 0x78, 0x3B, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x34, 0x30, 0x70, 0x78, 0x3B, 0x62, 0x61, + 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x66, 0x66, 0x66, 0x7D, + 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, 0x73, 0x65, 0x6C, 0x65, + 0x63, 0x74, 0x3A, 0x66, 0x6F, 0x63, 0x75, 0x73, 0x7B, 0x62, + 0x6F, 0x72, 0x64, 0x65, 0x72, 0x3A, 0x31, 0x70, 0x78, 0x20, + 0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, 0x23, 0x30, 0x39, 0x61, + 0x30, 0x66, 0x36, 0x7D, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, + 0x74, 0x65, 0x78, 0x74, 0x61, 0x72, 0x65, 0x61, 0x7B, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x31, 0x30, 0x70, + 0x78, 0x3B, 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x3A, 0x31, + 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, 0x23, + 0x64, 0x31, 0x65, 0x31, 0x65, 0x38, 0x3B, 0x62, 0x6F, 0x72, + 0x64, 0x65, 0x72, 0x2D, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, + 0x3A, 0x35, 0x70, 0x78, 0x3B, 0x72, 0x65, 0x73, 0x69, 0x7A, + 0x65, 0x3A, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6C, + 0x7D, 0x2E, 0x66, 0x6F, 0x72, 0x6D, 0x2D, 0x74, 0x65, 0x78, + 0x74, 0x61, 0x72, 0x65, 0x61, 0x3A, 0x66, 0x6F, 0x63, 0x75, + 0x73, 0x7B, 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x3A, 0x31, + 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, 0x23, + 0x30, 0x39, 0x61, 0x30, 0x66, 0x36, 0x7D, 0x2E, 0x66, 0x6F, + 0x72, 0x6D, 0x2D, 0x61, 0x75, 0x74, 0x6F, 0x7B, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x3A, 0x61, 0x75, 0x74, 0x6F, 0x21, 0x69, + 0x6D, 0x70, 0x6F, 0x72, 0x74, 0x61, 0x6E, 0x74, 0x7D, 0x2E, + 0x67, 0x72, 0x69, 0x64, 0x7B, 0x64, 0x69, 0x73, 0x70, 0x6C, + 0x61, 0x79, 0x3A, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x3B, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x3A, 0x39, 0x36, 0x30, 0x70, 0x78, + 0x3B, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, + 0x66, 0x74, 0x3A, 0x61, 0x75, 0x74, 0x6F, 0x3B, 0x6D, 0x61, + 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x72, 0x69, 0x67, 0x68, 0x74, + 0x3A, 0x61, 0x75, 0x74, 0x6F, 0x3B, 0x6D, 0x69, 0x6E, 0x2D, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x34, 0x30, 0x70, + 0x78, 0x7D, 0x40, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x20, 0x28, + 0x6D, 0x61, 0x78, 0x2D, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, + 0x39, 0x36, 0x30, 0x70, 0x78, 0x29, 0x7B, 0x2E, 0x67, 0x72, + 0x69, 0x64, 0x7B, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x39, + 0x34, 0x25, 0x7D, 0x7D, 0x2E, 0x67, 0x72, 0x69, 0x64, 0x2D, + 0x66, 0x6C, 0x75, 0x69, 0x64, 0x2C, 0x2E, 0x72, 0x6F, 0x77, + 0x7B, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x31, 0x30, 0x30, + 0x25, 0x7D, 0x2E, 0x72, 0x6F, 0x77, 0x7B, 0x64, 0x69, 0x73, + 0x70, 0x6C, 0x61, 0x79, 0x3A, 0x69, 0x6E, 0x6C, 0x69, 0x6E, + 0x65, 0x2D, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x3B, 0x6D, 0x61, + 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, + 0x30, 0x3B, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x72, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x30, 0x7D, 0x2E, 0x72, 0x6F, + 0x77, 0x3A, 0x61, 0x66, 0x74, 0x65, 0x72, 0x7B, 0x63, 0x6F, + 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x3A, 0x22, 0x20, 0x22, 0x3B, + 0x63, 0x6C, 0x65, 0x61, 0x72, 0x3A, 0x62, 0x6F, 0x74, 0x68, + 0x3B, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, 0x74, + 0x61, 0x62, 0x6C, 0x65, 0x3B, 0x6C, 0x69, 0x6E, 0x65, 0x2D, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x30, 0x7D, 0x2E, + 0x63, 0x6F, 0x6C, 0x2D, 0x31, 0x2C, 0x2E, 0x63, 0x6F, 0x6C, + 0x2D, 0x31, 0x30, 0x2C, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x31, + 0x31, 0x2C, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x31, 0x32, 0x2C, + 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x32, 0x2C, 0x2E, 0x63, 0x6F, + 0x6C, 0x2D, 0x33, 0x2C, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x34, + 0x2C, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x35, 0x2C, 0x2E, 0x63, + 0x6F, 0x6C, 0x2D, 0x37, 0x2C, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, + 0x38, 0x2C, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x39, 0x7B, 0x64, + 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, 0x69, 0x6E, 0x6C, + 0x69, 0x6E, 0x65, 0x2D, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x3B, + 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6C, 0x2D, 0x61, + 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x74, 0x6F, 0x70, 0x3B, 0x66, + 0x6C, 0x6F, 0x61, 0x74, 0x3A, 0x6C, 0x65, 0x66, 0x74, 0x3B, + 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x31, 0x25, + 0x7D, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x31, 0x7B, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x3A, 0x36, 0x2E, 0x33, 0x33, 0x25, 0x7D, + 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x32, 0x7B, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x3A, 0x31, 0x34, 0x2E, 0x36, 0x36, 0x25, 0x7D, + 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x33, 0x7B, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x3A, 0x32, 0x32, 0x2E, 0x39, 0x39, 0x25, 0x7D, + 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x34, 0x7B, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x3A, 0x33, 0x31, 0x2E, 0x33, 0x33, 0x25, 0x7D, + 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x35, 0x7B, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x3A, 0x33, 0x39, 0x2E, 0x36, 0x36, 0x25, 0x7D, + 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x36, 0x7B, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x3A, 0x34, 0x37, 0x2E, 0x39, 0x39, 0x25, 0x3B, + 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, 0x69, 0x6E, + 0x6C, 0x69, 0x6E, 0x65, 0x2D, 0x62, 0x6C, 0x6F, 0x63, 0x6B, + 0x3B, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6C, 0x2D, + 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x74, 0x6F, 0x70, 0x3B, + 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x3A, 0x6C, 0x65, 0x66, 0x74, + 0x3B, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x31, + 0x25, 0x7D, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x37, 0x7B, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x3A, 0x35, 0x36, 0x2E, 0x33, 0x33, + 0x25, 0x7D, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x38, 0x7B, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x3A, 0x36, 0x34, 0x2E, 0x36, 0x36, + 0x25, 0x7D, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x39, 0x7B, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x3A, 0x37, 0x32, 0x2E, 0x39, 0x39, + 0x25, 0x7D, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x31, 0x30, 0x7B, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x38, 0x31, 0x2E, 0x33, + 0x33, 0x25, 0x7D, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x31, 0x31, + 0x7B, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x38, 0x39, 0x2E, + 0x36, 0x36, 0x25, 0x7D, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x31, + 0x32, 0x7B, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x39, 0x37, + 0x2E, 0x39, 0x39, 0x25, 0x7D, 0x40, 0x6D, 0x65, 0x64, 0x69, + 0x61, 0x20, 0x28, 0x6D, 0x61, 0x78, 0x2D, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x3A, 0x34, 0x30, 0x30, 0x70, 0x78, 0x29, 0x7B, + 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x31, 0x2C, 0x2E, 0x63, 0x6F, + 0x6C, 0x2D, 0x31, 0x30, 0x2C, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, + 0x31, 0x31, 0x2C, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x31, 0x32, + 0x2C, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x32, 0x2C, 0x2E, 0x63, + 0x6F, 0x6C, 0x2D, 0x33, 0x2C, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, + 0x34, 0x2C, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x35, 0x2C, 0x2E, + 0x63, 0x6F, 0x6C, 0x2D, 0x36, 0x2C, 0x2E, 0x63, 0x6F, 0x6C, + 0x2D, 0x37, 0x2C, 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x38, 0x2C, + 0x2E, 0x63, 0x6F, 0x6C, 0x2D, 0x39, 0x7B, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x3A, 0x39, 0x38, 0x25, 0x7D, 0x7D, 0x2E, 0x74, + 0x61, 0x62, 0x6C, 0x65, 0x7B, 0x64, 0x69, 0x73, 0x70, 0x6C, + 0x61, 0x79, 0x3A, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x3B, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x3A, 0x31, 0x30, 0x30, 0x25, 0x3B, + 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x3A, 0x30, 0x3B, 0x62, 0x6F, 0x72, 0x64, 0x65, + 0x72, 0x2D, 0x63, 0x6F, 0x6C, 0x6C, 0x61, 0x70, 0x73, 0x65, + 0x3A, 0x63, 0x6F, 0x6C, 0x6C, 0x61, 0x70, 0x73, 0x65, 0x3B, + 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x3A, 0x33, 0x30, 0x30, 0x3B, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x3A, 0x23, 0x35, 0x32, 0x36, 0x34, 0x37, 0x35, 0x3B, + 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, 0x74, 0x6F, 0x70, + 0x3A, 0x30, 0x3B, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, 0x2D, + 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x3A, 0x32, 0x30, 0x70, + 0x78, 0x7D, 0x2E, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x61, 0x64, 0x20, 0x74, 0x72, 0x20, 0x74, 0x64, + 0x7B, 0x66, 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x3A, 0x34, 0x30, 0x30, 0x3B, 0x62, 0x6F, 0x72, + 0x64, 0x65, 0x72, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, + 0x3A, 0x32, 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, + 0x20, 0x23, 0x64, 0x31, 0x65, 0x31, 0x65, 0x38, 0x3B, 0x62, + 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x2D, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x23, 0x66, 0x36, 0x66, + 0x38, 0x66, 0x61, 0x7D, 0x2E, 0x74, 0x61, 0x62, 0x6C, 0x65, + 0x20, 0x74, 0x72, 0x20, 0x74, 0x64, 0x7B, 0x62, 0x6F, 0x72, + 0x64, 0x65, 0x72, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, + 0x3A, 0x31, 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, + 0x20, 0x23, 0x64, 0x31, 0x65, 0x31, 0x65, 0x38, 0x3B, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2D, 0x74, 0x6F, 0x70, + 0x3A, 0x31, 0x30, 0x70, 0x78, 0x3B, 0x70, 0x61, 0x64, 0x64, + 0x69, 0x6E, 0x67, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, + 0x3A, 0x31, 0x30, 0x70, 0x78, 0x3B, 0x70, 0x61, 0x64, 0x64, + 0x69, 0x6E, 0x67, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x31, + 0x30, 0x70, 0x78, 0x7D, }; + +static const unsigned char data_css_style_css[] = { + /* /css/style.css */ + 0x2F, 0x63, 0x73, 0x73, 0x2F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x2E, 0x63, 0x73, 0x73, 0, + 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30, 0x20, 0x32, + 0x30, 0x30, 0x20, 0x4F, 0x4B, 0x0D, 0x0A, 0x6C, 0x77, 0x49, + 0x50, 0x2F, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x20, 0x28, 0x68, + 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x61, 0x76, 0x61, + 0x6E, 0x6E, 0x61, 0x68, 0x2E, 0x6E, 0x6F, 0x6E, 0x67, 0x6E, + 0x75, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x70, 0x72, 0x6F, 0x6A, + 0x65, 0x63, 0x74, 0x73, 0x2F, 0x6C, 0x77, 0x69, 0x70, 0x29, + 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, + 0x74, 0x79, 0x70, 0x65, 0x3A, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x2F, 0x63, 0x73, 0x73, 0x0D, 0x0A, 0x0D, 0x0A, 0x0D, 0x0A, + 0x75, 0x6C, 0x2E, 0x6E, 0x61, 0x76, 0x62, 0x61, 0x72, 0x20, + 0x7B, 0x0A, 0x09, 0x6C, 0x69, 0x73, 0x74, 0x2D, 0x73, 0x74, + 0x79, 0x6C, 0x65, 0x2D, 0x74, 0x79, 0x70, 0x65, 0x3A, 0x20, + 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x0A, 0x09, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x2D, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, + 0x3A, 0x20, 0x33, 0x32, 0x70, 0x78, 0x3B, 0x0A, 0x09, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x30, 0x3B, + 0x0A, 0x09, 0x6F, 0x76, 0x65, 0x72, 0x66, 0x6C, 0x6F, 0x77, + 0x3A, 0x20, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6E, 0x3B, 0x0A, + 0x09, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, + 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, + 0x33, 0x33, 0x33, 0x3B, 0x0A, 0x7D, 0x0A, 0x75, 0x6C, 0x2E, + 0x6E, 0x61, 0x76, 0x62, 0x61, 0x72, 0x20, 0x6C, 0x69, 0x20, + 0x7B, 0x0A, 0x09, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x3A, 0x20, + 0x6C, 0x65, 0x66, 0x74, 0x3B, 0x0A, 0x7D, 0x0A, 0x75, 0x6C, + 0x2E, 0x6E, 0x61, 0x76, 0x62, 0x61, 0x72, 0x20, 0x6C, 0x69, + 0x20, 0x61, 0x20, 0x7B, 0x0A, 0x09, 0x64, 0x69, 0x73, 0x70, + 0x6C, 0x61, 0x79, 0x3A, 0x20, 0x62, 0x6C, 0x6F, 0x63, 0x6B, + 0x3B, 0x0A, 0x09, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, + 0x77, 0x68, 0x69, 0x74, 0x65, 0x3B, 0x0A, 0x09, 0x74, 0x65, + 0x78, 0x74, 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x20, + 0x63, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x3B, 0x0A, 0x09, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0x31, 0x34, + 0x70, 0x78, 0x20, 0x31, 0x36, 0x70, 0x78, 0x3B, 0x0A, 0x09, + 0x74, 0x65, 0x78, 0x74, 0x2D, 0x64, 0x65, 0x63, 0x6F, 0x72, + 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x6E, 0x6F, 0x6E, + 0x65, 0x3B, 0x0A, 0x7D, 0x0A, 0x75, 0x6C, 0x2E, 0x6E, 0x61, + 0x76, 0x62, 0x61, 0x72, 0x20, 0x6C, 0x69, 0x20, 0x61, 0x3A, + 0x68, 0x6F, 0x76, 0x65, 0x72, 0x3A, 0x6E, 0x6F, 0x74, 0x28, + 0x2E, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x29, 0x20, 0x7B, + 0x0A, 0x09, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, + 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, + 0x23, 0x31, 0x31, 0x31, 0x3B, 0x0A, 0x7D, 0x0A, 0x75, 0x6C, + 0x2E, 0x6E, 0x61, 0x76, 0x62, 0x61, 0x72, 0x20, 0x6C, 0x69, + 0x20, 0x61, 0x2E, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, + 0x7B, 0x0A, 0x09, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, + 0x75, 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, + 0x20, 0x23, 0x30, 0x39, 0x61, 0x30, 0x66, 0x36, 0x3B, 0x0A, + 0x7D, 0x0A, 0x40, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x20, 0x73, + 0x63, 0x72, 0x65, 0x65, 0x6E, 0x20, 0x61, 0x6E, 0x64, 0x20, + 0x28, 0x6D, 0x61, 0x78, 0x2D, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x3A, 0x20, 0x36, 0x30, 0x30, 0x70, 0x78, 0x29, 0x7B, 0x0A, + 0x09, 0x75, 0x6C, 0x2E, 0x6E, 0x61, 0x76, 0x62, 0x61, 0x72, + 0x20, 0x6C, 0x69, 0x2E, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2C, + 0x20, 0x0A, 0x09, 0x75, 0x6C, 0x2E, 0x6E, 0x61, 0x76, 0x62, + 0x61, 0x72, 0x20, 0x6C, 0x69, 0x20, 0x7B, 0x66, 0x6C, 0x6F, + 0x61, 0x74, 0x3A, 0x20, 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x7D, + 0x0A, 0x7D, 0x0A, 0x2E, 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, + 0x77, 0x69, 0x74, 0x63, 0x68, 0x20, 0x7B, 0x0A, 0x09, 0x70, + 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x72, + 0x65, 0x6C, 0x61, 0x74, 0x69, 0x76, 0x65, 0x3B, 0x20, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x3A, 0x20, 0x39, 0x30, 0x70, 0x78, + 0x3B, 0x0A, 0x09, 0x2D, 0x77, 0x65, 0x62, 0x6B, 0x69, 0x74, + 0x2D, 0x75, 0x73, 0x65, 0x72, 0x2D, 0x73, 0x65, 0x6C, 0x65, + 0x63, 0x74, 0x3A, 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x20, 0x2D, + 0x6D, 0x6F, 0x7A, 0x2D, 0x75, 0x73, 0x65, 0x72, 0x2D, 0x73, + 0x65, 0x6C, 0x65, 0x63, 0x74, 0x3A, 0x6E, 0x6F, 0x6E, 0x65, + 0x3B, 0x20, 0x2D, 0x6D, 0x73, 0x2D, 0x75, 0x73, 0x65, 0x72, + 0x2D, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x3A, 0x20, 0x6E, + 0x6F, 0x6E, 0x65, 0x3B, 0x0A, 0x7D, 0x0A, 0x2E, 0x6F, 0x6E, + 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, + 0x63, 0x68, 0x65, 0x63, 0x6B, 0x62, 0x6F, 0x78, 0x20, 0x7B, + 0x0A, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, + 0x20, 0x6E, 0x6F, 0x6E, 0x65, 0x3B, 0x0A, 0x7D, 0x0A, 0x2E, + 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, + 0x68, 0x2D, 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x20, 0x7B, 0x0A, + 0x09, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, 0x20, + 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x3B, 0x20, 0x6F, 0x76, 0x65, + 0x72, 0x66, 0x6C, 0x6F, 0x77, 0x3A, 0x20, 0x68, 0x69, 0x64, + 0x64, 0x65, 0x6E, 0x3B, 0x20, 0x63, 0x75, 0x72, 0x73, 0x6F, + 0x72, 0x3A, 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x65, 0x72, + 0x3B, 0x0A, 0x09, 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x3A, + 0x20, 0x32, 0x70, 0x78, 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, + 0x20, 0x23, 0x30, 0x33, 0x41, 0x39, 0x46, 0x34, 0x3B, 0x20, + 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x72, 0x61, 0x64, + 0x69, 0x75, 0x73, 0x3A, 0x20, 0x32, 0x30, 0x70, 0x78, 0x3B, + 0x0A, 0x7D, 0x0A, 0x2E, 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, + 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, 0x69, 0x6E, 0x6E, 0x65, + 0x72, 0x20, 0x7B, 0x0A, 0x09, 0x64, 0x69, 0x73, 0x70, 0x6C, + 0x61, 0x79, 0x3A, 0x20, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x3B, + 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3A, 0x20, 0x32, 0x30, + 0x30, 0x25, 0x3B, 0x20, 0x6D, 0x61, 0x72, 0x67, 0x69, 0x6E, + 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x20, 0x2D, 0x31, 0x30, + 0x30, 0x25, 0x3B, 0x0A, 0x09, 0x74, 0x72, 0x61, 0x6E, 0x73, + 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x20, 0x30, 0x2E, 0x33, 0x73, 0x20, 0x65, + 0x61, 0x73, 0x65, 0x2D, 0x69, 0x6E, 0x20, 0x30, 0x73, 0x3B, + 0x0A, 0x7D, 0x0A, 0x2E, 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, + 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, 0x69, 0x6E, 0x6E, 0x65, + 0x72, 0x3A, 0x62, 0x65, 0x66, 0x6F, 0x72, 0x65, 0x2C, 0x20, + 0x2E, 0x6F, 0x6E, 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, + 0x63, 0x68, 0x2D, 0x69, 0x6E, 0x6E, 0x65, 0x72, 0x3A, 0x61, + 0x66, 0x74, 0x65, 0x72, 0x20, 0x7B, 0x0A, 0x09, 0x64, 0x69, + 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, 0x20, 0x62, 0x6C, 0x6F, + 0x63, 0x6B, 0x3B, 0x20, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x3A, + 0x20, 0x6C, 0x65, 0x66, 0x74, 0x3B, 0x20, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x3A, 0x20, 0x35, 0x30, 0x25, 0x3B, 0x20, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x33, 0x30, 0x70, + 0x78, 0x3B, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, + 0x3A, 0x20, 0x30, 0x3B, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x2D, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x33, 0x30, + 0x70, 0x78, 0x3B, 0x0A, 0x09, 0x66, 0x6F, 0x6E, 0x74, 0x2D, + 0x73, 0x69, 0x7A, 0x65, 0x3A, 0x20, 0x31, 0x34, 0x70, 0x78, + 0x3B, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x77, + 0x68, 0x69, 0x74, 0x65, 0x3B, 0x20, 0x66, 0x6F, 0x6E, 0x74, + 0x2D, 0x66, 0x61, 0x6D, 0x69, 0x6C, 0x79, 0x3A, 0x20, 0x54, + 0x72, 0x65, 0x62, 0x75, 0x63, 0x68, 0x65, 0x74, 0x2C, 0x20, + 0x41, 0x72, 0x69, 0x61, 0x6C, 0x2C, 0x20, 0x73, 0x61, 0x6E, + 0x73, 0x2D, 0x73, 0x65, 0x72, 0x69, 0x66, 0x3B, 0x20, 0x66, + 0x6F, 0x6E, 0x74, 0x2D, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x3A, 0x20, 0x62, 0x6F, 0x6C, 0x64, 0x3B, 0x0A, 0x09, 0x62, + 0x6F, 0x78, 0x2D, 0x73, 0x69, 0x7A, 0x69, 0x6E, 0x67, 0x3A, + 0x20, 0x62, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x62, 0x6F, + 0x78, 0x3B, 0x0A, 0x7D, 0x0A, 0x2E, 0x6F, 0x6E, 0x6F, 0x66, + 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, 0x69, 0x6E, + 0x6E, 0x65, 0x72, 0x3A, 0x62, 0x65, 0x66, 0x6F, 0x72, 0x65, + 0x20, 0x7B, 0x0A, 0x09, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x6E, + 0x74, 0x3A, 0x20, 0x22, 0x4F, 0x4E, 0x22, 0x3B, 0x0A, 0x09, + 0x74, 0x65, 0x78, 0x74, 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, + 0x3A, 0x20, 0x6C, 0x65, 0x66, 0x74, 0x3B, 0x0A, 0x09, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2D, 0x6C, 0x65, 0x66, + 0x74, 0x3A, 0x20, 0x31, 0x34, 0x70, 0x78, 0x3B, 0x0A, 0x09, + 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, + 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x45, + 0x31, 0x46, 0x35, 0x46, 0x45, 0x3B, 0x20, 0x63, 0x6F, 0x6C, + 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x30, 0x33, 0x41, 0x39, 0x46, + 0x34, 0x3B, 0x0A, 0x7D, 0x0A, 0x2E, 0x6F, 0x6E, 0x6F, 0x66, + 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, 0x69, 0x6E, + 0x6E, 0x65, 0x72, 0x3A, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, + 0x7B, 0x0A, 0x09, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, + 0x3A, 0x20, 0x22, 0x4F, 0x46, 0x46, 0x22, 0x3B, 0x0A, 0x09, + 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2D, 0x72, 0x69, + 0x67, 0x68, 0x74, 0x3A, 0x20, 0x31, 0x34, 0x70, 0x78, 0x3B, + 0x0A, 0x09, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, + 0x6E, 0x64, 0x2D, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, + 0x23, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x3B, 0x20, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x3A, 0x20, 0x23, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x3B, 0x0A, 0x09, 0x74, 0x65, 0x78, 0x74, + 0x2D, 0x61, 0x6C, 0x69, 0x67, 0x6E, 0x3A, 0x20, 0x72, 0x69, + 0x67, 0x68, 0x74, 0x3B, 0x0A, 0x7D, 0x0A, 0x2E, 0x6F, 0x6E, + 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, + 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x20, 0x7B, 0x0A, 0x09, + 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x3A, 0x20, 0x62, + 0x6C, 0x6F, 0x63, 0x6B, 0x3B, 0x20, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x3A, 0x20, 0x31, 0x38, 0x70, 0x78, 0x3B, 0x20, 0x6D, + 0x61, 0x72, 0x67, 0x69, 0x6E, 0x3A, 0x20, 0x36, 0x70, 0x78, + 0x3B, 0x0A, 0x09, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, + 0x75, 0x6E, 0x64, 0x3A, 0x20, 0x23, 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x3B, 0x0A, 0x09, 0x70, 0x6F, 0x73, 0x69, 0x74, + 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x61, 0x62, 0x73, 0x6F, 0x6C, + 0x75, 0x74, 0x65, 0x3B, 0x20, 0x74, 0x6F, 0x70, 0x3A, 0x20, + 0x30, 0x3B, 0x20, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x3A, + 0x20, 0x30, 0x3B, 0x0A, 0x09, 0x72, 0x69, 0x67, 0x68, 0x74, + 0x3A, 0x20, 0x35, 0x36, 0x70, 0x78, 0x3B, 0x0A, 0x09, 0x62, + 0x6F, 0x72, 0x64, 0x65, 0x72, 0x3A, 0x20, 0x32, 0x70, 0x78, + 0x20, 0x73, 0x6F, 0x6C, 0x69, 0x64, 0x20, 0x23, 0x30, 0x33, + 0x41, 0x39, 0x46, 0x34, 0x3B, 0x20, 0x62, 0x6F, 0x72, 0x64, + 0x65, 0x72, 0x2D, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3A, + 0x20, 0x32, 0x30, 0x70, 0x78, 0x3B, 0x0A, 0x09, 0x74, 0x72, + 0x61, 0x6E, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, + 0x61, 0x6C, 0x6C, 0x20, 0x30, 0x2E, 0x33, 0x73, 0x20, 0x65, + 0x61, 0x73, 0x65, 0x2D, 0x69, 0x6E, 0x20, 0x30, 0x73, 0x3B, + 0x20, 0x0A, 0x7D, 0x0A, 0x2E, 0x6F, 0x6E, 0x6F, 0x66, 0x66, + 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, 0x63, 0x68, 0x65, + 0x63, 0x6B, 0x62, 0x6F, 0x78, 0x3A, 0x63, 0x68, 0x65, 0x63, + 0x6B, 0x65, 0x64, 0x20, 0x2B, 0x20, 0x2E, 0x6F, 0x6E, 0x6F, + 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, 0x6C, + 0x61, 0x62, 0x65, 0x6C, 0x20, 0x2E, 0x6F, 0x6E, 0x6F, 0x66, + 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, 0x69, 0x6E, + 0x6E, 0x65, 0x72, 0x20, 0x7B, 0x0A, 0x09, 0x6D, 0x61, 0x72, + 0x67, 0x69, 0x6E, 0x2D, 0x6C, 0x65, 0x66, 0x74, 0x3A, 0x20, + 0x30, 0x3B, 0x0A, 0x7D, 0x0A, 0x2E, 0x6F, 0x6E, 0x6F, 0x66, + 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, 0x63, 0x68, + 0x65, 0x63, 0x6B, 0x62, 0x6F, 0x78, 0x3A, 0x63, 0x68, 0x65, + 0x63, 0x6B, 0x65, 0x64, 0x20, 0x2B, 0x20, 0x2E, 0x6F, 0x6E, + 0x6F, 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, + 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x20, 0x2E, 0x6F, 0x6E, 0x6F, + 0x66, 0x66, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x2D, 0x73, + 0x77, 0x69, 0x74, 0x63, 0x68, 0x20, 0x7B, 0x0A, 0x09, 0x72, + 0x69, 0x67, 0x68, 0x74, 0x3A, 0x20, 0x30, 0x70, 0x78, 0x3B, + 0x20, 0x0A, 0x7D, }; + +const struct fsdata_file file_index_ssi[] = {{ +NULL, +data_index_ssi, data_index_ssi + 11, +sizeof(data_index_ssi) - 11, +1 +}}; + +const struct fsdata_file file_404_html[] = {{ +file_index_ssi, +data_404_html, data_404_html + 10, +sizeof(data_404_html) - 10, +1 +}}; + +const struct fsdata_file file_websockets_html[] = {{ +file_404_html, +data_websockets_html, data_websockets_html + 17, +sizeof(data_websockets_html) - 17, +1 +}}; + +const struct fsdata_file file_about_html[] = {{ +file_websockets_html, +data_about_html, data_about_html + 12, +sizeof(data_about_html) - 12, +1 +}}; + +const struct fsdata_file file_js_smoothie_min_js[] = {{ +file_about_html, +data_js_smoothie_min_js, data_js_smoothie_min_js + 20, +sizeof(data_js_smoothie_min_js) - 20, +1 +}}; + +const struct fsdata_file file_img_favicon_png[] = {{ +file_js_smoothie_min_js, +data_img_favicon_png, data_img_favicon_png + 17, +sizeof(data_img_favicon_png) - 17, +1 +}}; + +const struct fsdata_file file_css_siimple_min_css[] = {{ +file_img_favicon_png, +data_css_siimple_min_css, data_css_siimple_min_css + 21, +sizeof(data_css_siimple_min_css) - 21, +1 +}}; + +const struct fsdata_file file_css_style_css[] = {{ +file_css_siimple_min_css, +data_css_style_css, data_css_style_css + 15, +sizeof(data_css_style_css) - 15, +1 +}}; + +#define FS_ROOT file_css_style_css + +#define FS_NUMFILES 8 diff --git a/examples/http_server/fsdata/makefsdata b/examples/http_server/fsdata/makefsdata new file mode 100755 index 0000000..5361370 --- /dev/null +++ b/examples/http_server/fsdata/makefsdata @@ -0,0 +1,114 @@ +#!/usr/bin/perl + +$incHttpHeader = 1; + +open(OUTPUT, "> fsdata.c"); +print(OUTPUT "#include \"httpd/fsdata.h\"\n\n"); + +chdir("fs"); +open(FILES, "find . -type f |"); + +while($file = ) { + + # Do not include files in CVS directories nor backup files. + if($file =~ /(CVS|~)/) { + next; + } + + chop($file); + + if($incHttpHeader == 1) { + open(HEADER, "> /tmp/header") || die $!; + if($file =~ /404/) { + print(HEADER "HTTP/1.0 404 File not found\r\n"); + } else { + print(HEADER "HTTP/1.0 200 OK\r\n"); + } + print(HEADER "lwIP/1.4.1 (http://savannah.nongnu.org/projects/lwip)\r\n"); + if($file =~ /\.html$/ || $file =~ /\.htm$/ || $file =~ /\.shtml$/ || $file =~ /\.shtm$/ || $file =~ /\.ssi$/) { + print(HEADER "Content-type: text/html\r\n"); + } elsif($file =~ /\.js$/) { + print(HEADER "Content-type: application/x-javascript\r\n\r\n"); + } elsif($file =~ /\.css$/) { + print(HEADER "Content-type: text/css\r\n\r\n"); + } elsif($file =~ /\.ico$/) { + print(HEADER "Content-type: image/x-icon\r\n\r\n"); + } elsif($file =~ /\.gif$/) { + print(HEADER "Content-type: image/gif\r\n"); + } elsif($file =~ /\.png$/) { + print(HEADER "Content-type: image/png\r\n"); + } elsif($file =~ /\.jpg$/) { + print(HEADER "Content-type: image/jpeg\r\n"); + } elsif($file =~ /\.bmp$/) { + print(HEADER "Content-type: image/bmp\r\n\r\n"); + } elsif($file =~ /\.class$/) { + print(HEADER "Content-type: application/octet-stream\r\n"); + } elsif($file =~ /\.ram$/) { + print(HEADER "Content-type: audio/x-pn-realaudio\r\n"); + } else { + print(HEADER "Content-type: text/plain\r\n"); + } + print(HEADER "\r\n"); + close(HEADER); + + unless($file =~ /\.plain$/ || $file =~ /cgi/) { + system("cat /tmp/header $file > /tmp/file"); + } else { + system("cp $file /tmp/file"); + } + } else { + system("cp $file /tmp/file"); + } + + open(FILE, "/tmp/file"); + unlink("/tmp/file"); + unlink("/tmp/header"); + + $file =~ s/\.//; + $fvar = $file; + $fvar =~ s-/-_-g; + $fvar =~ s-\.-_-g; + + print(OUTPUT "static const unsigned char data".$fvar."[] = {\n"); + print(OUTPUT "\t/* $file */\n\t"); + for($j = 0; $j < length($file); $j++) { + printf(OUTPUT "0x%02X, ", unpack("C", substr($file, $j, 1))); + } + printf(OUTPUT "0,\n"); + + + $i = 0; + while(read(FILE, $data, 1)) { + if($i == 0) { + print(OUTPUT "\t"); + } + printf(OUTPUT "0x%02X, ", unpack("C", $data)); + $i++; + if($i == 10) { + print(OUTPUT "\n"); + $i = 0; + } + } + print(OUTPUT "};\n\n"); + close(FILE); + push(@fvars, $fvar); + push(@files, $file); +} + +for($i = 0; $i < @fvars; $i++) { + $file = $files[$i]; + $fvar = $fvars[$i]; + + if($i == 0) { + $prevfile = "NULL"; + } else { + $prevfile = "file" . $fvars[$i - 1]; + } + print(OUTPUT "const struct fsdata_file file".$fvar."[] = {{\n$prevfile,\ndata$fvar, "); + print(OUTPUT "data$fvar + ". (length($file) + 1) .",\n"); + print(OUTPUT "sizeof(data$fvar) - ". (length($file) + 1) .",\n"); + print(OUTPUT $incHttpHeader."\n}};\n\n"); +} + +print(OUTPUT "#define FS_ROOT file$fvars[$i - 1]\n\n"); +print(OUTPUT "#define FS_NUMFILES $i\n"); diff --git a/examples/http_server/fsdata/readme.txt b/examples/http_server/fsdata/readme.txt new file mode 100644 index 0000000..a1db0c7 --- /dev/null +++ b/examples/http_server/fsdata/readme.txt @@ -0,0 +1,2 @@ +This directory contains a script ('makefsdata') to create C code suitable for +httpd for given html pages (or other files) in a directory. diff --git a/examples/http_server/http_server.c b/examples/http_server/http_server.c new file mode 100644 index 0000000..985732e --- /dev/null +++ b/examples/http_server/http_server.c @@ -0,0 +1,202 @@ +/* + * HTTP server example. + * + * This sample code is in the public domain. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LED_PIN 2 + +enum { + SSI_UPTIME, + SSI_FREE_HEAP, + SSI_LED_STATE +}; + +int32_t ssi_handler(int32_t iIndex, char *pcInsert, int32_t iInsertLen) +{ + switch (iIndex) { + case SSI_UPTIME: + snprintf(pcInsert, iInsertLen, "%d", + xTaskGetTickCount() * portTICK_PERIOD_MS / 1000); + break; + case SSI_FREE_HEAP: + snprintf(pcInsert, iInsertLen, "%d", (int) xPortGetFreeHeapSize()); + break; + case SSI_LED_STATE: + snprintf(pcInsert, iInsertLen, (GPIO.OUT & BIT(LED_PIN)) ? "Off" : "On"); + break; + default: + snprintf(pcInsert, iInsertLen, "N/A"); + break; + } + + /* Tell the server how many characters to insert */ + return (strlen(pcInsert)); +} + +char *gpio_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) +{ + for (int i = 0; i < iNumParams; i++) { + if (strcmp(pcParam[i], "on") == 0) { + uint8_t gpio_num = atoi(pcValue[i]); + gpio_enable(gpio_num, GPIO_OUTPUT); + gpio_write(gpio_num, true); + } else if (strcmp(pcParam[i], "off") == 0) { + uint8_t gpio_num = atoi(pcValue[i]); + gpio_enable(gpio_num, GPIO_OUTPUT); + gpio_write(gpio_num, false); + } else if (strcmp(pcParam[i], "toggle") == 0) { + uint8_t gpio_num = atoi(pcValue[i]); + gpio_enable(gpio_num, GPIO_OUTPUT); + gpio_toggle(gpio_num); + } + } + return "/index.ssi"; +} + +char *about_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) +{ + return "/about.html"; +} + +char *websocket_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) +{ + return "/websockets.html"; +} + +void websocket_task(void *pvParameter) +{ + struct tcp_pcb *pcb = (struct tcp_pcb *) pvParameter; + + for (;;) { + if (pcb == NULL || pcb->state != ESTABLISHED) { + printf("Connection closed, deleting task\n"); + break; + } + + int uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000; + int heap = (int) xPortGetFreeHeapSize(); + int led = !gpio_read(LED_PIN); + + /* Generate response in JSON format */ + char response[64]; + int len = snprintf(response, sizeof (response), + "{\"uptime\" : \"%d\"," + " \"heap\" : \"%d\"," + " \"led\" : \"%d\"}", uptime, heap, led); + if (len < sizeof (response)) + websocket_write(pcb, (unsigned char *) response, len, WS_TEXT_MODE); + + vTaskDelay(2000 / portTICK_PERIOD_MS); + } + + vTaskDelete(NULL); +} + +/** + * This function is called when websocket frame is received. + * + * Note: this function is executed on TCP thread and should return as soon + * as possible. + */ +void websocket_cb(struct tcp_pcb *pcb, uint8_t *data, u16_t data_len, uint8_t mode) +{ + printf("[websocket_callback]:\n%.*s\n", (int) data_len, (char*) data); + + uint8_t response[2]; + uint16_t val; + + switch (data[0]) { + case 'A': // ADC + /* This should be done on a separate thread in 'real' applications */ + val = sdk_system_adc_read(); + break; + case 'D': // Disable LED + gpio_write(LED_PIN, true); + val = 0xDEAD; + break; + case 'E': // Enable LED + gpio_write(LED_PIN, false); + val = 0xBEEF; + break; + default: + printf("Unknown command\n"); + val = 0; + break; + } + + response[1] = (uint8_t) val; + response[0] = val >> 8; + + websocket_write(pcb, response, 2, WS_BIN_MODE); +} + +/** + * This function is called when new websocket is open and + * creates a new websocket_task if requested URI equals '/stream'. + */ +void websocket_open_cb(struct tcp_pcb *pcb, const char *uri) +{ + printf("WS URI: %s\n", uri); + if (!strcmp(uri, "/stream")) { + printf("request for streaming\n"); + xTaskCreate(&websocket_task, "websocket_task", 256, (void *) pcb, 2, NULL); + } +} + +void httpd_task(void *pvParameters) +{ + tCGI pCGIs[] = { + {"/gpio", (tCGIHandler) gpio_cgi_handler}, + {"/about", (tCGIHandler) about_cgi_handler}, + {"/websockets", (tCGIHandler) websocket_cgi_handler}, + }; + + const char *pcConfigSSITags[] = { + "uptime", // SSI_UPTIME + "heap", // SSI_FREE_HEAP + "led" // SSI_LED_STATE + }; + + /* register handlers and start the server */ + http_set_cgi_handlers(pCGIs, sizeof (pCGIs) / sizeof (pCGIs[0])); + http_set_ssi_handler((tSSIHandler) ssi_handler, pcConfigSSITags, + sizeof (pcConfigSSITags) / sizeof (pcConfigSSITags[0])); + websocket_register_callbacks((tWsOpenHandler) websocket_open_cb, + (tWsHandler) websocket_cb); + httpd_init(); + + for (;;); +} + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + struct sdk_station_config config = { + .ssid = WIFI_SSID, + .password = WIFI_PASS, + }; + + /* required to call wifi_set_opmode before station_set_config */ + sdk_wifi_set_opmode(STATION_MODE); + sdk_wifi_station_set_config(&config); + sdk_wifi_station_connect(); + + /* turn off LED */ + gpio_enable(LED_PIN, GPIO_OUTPUT); + gpio_write(LED_PIN, true); + + /* initialize tasks */ + xTaskCreate(&httpd_task, "HTTP Daemon", 128, NULL, 2, NULL); +} diff --git a/examples/max7219_7seg/Makefile b/examples/max7219_7seg/Makefile new file mode 100644 index 0000000..fc55387 --- /dev/null +++ b/examples/max7219_7seg/Makefile @@ -0,0 +1,3 @@ +PROGRAM = max7219_7seg +EXTRA_COMPONENTS = extras/max7219 +include ../../common.mk diff --git a/examples/max7219_7seg/main.c b/examples/max7219_7seg/main.c new file mode 100644 index 0000000..d90d01b --- /dev/null +++ b/examples/max7219_7seg/main.c @@ -0,0 +1,52 @@ +/* + * Example of using MAX7219 driver with 7 segment displays + * + * Part of esp-open-rtos + * Copyright (C) 2017 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define CS_PIN 5 +#define DELAY 2000 + +static max7219_display_t disp = { + .cs_pin = CS_PIN, + .digits = 8, + .cascade_size = 1, + .mirrored = true +}; + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + max7219_init(&disp); + //max7219_set_decode_mode(&disp, true); + + char buf[9]; + while (true) + { + max7219_clear(&disp); + max7219_draw_text(&disp, 0, "7219LEDS"); + vTaskDelay(DELAY / portTICK_PERIOD_MS); + + max7219_clear(&disp); + sprintf(buf, "%2.4f A", 34.6782); + max7219_draw_text(&disp, 0, buf); + vTaskDelay(DELAY / portTICK_PERIOD_MS); + + max7219_clear(&disp); + sprintf(buf, "%08x", hwrand()); + max7219_draw_text(&disp, 0, buf); + vTaskDelay(DELAY / portTICK_PERIOD_MS); + } +} diff --git a/examples/tls_server_bearssl/Makefile b/examples/tls_server_bearssl/Makefile new file mode 100644 index 0000000..79c0e38 --- /dev/null +++ b/examples/tls_server_bearssl/Makefile @@ -0,0 +1,4 @@ +PROGRAM=tls_server_bearssl +EXTRA_COMPONENTS = extras/bearssl + +include ../../common.mk diff --git a/examples/tls_server_bearssl/certificate.h b/examples/tls_server_bearssl/certificate.h new file mode 100644 index 0000000..bafd713 --- /dev/null +++ b/examples/tls_server_bearssl/certificate.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "bearssl.h" + +/* + * A sample server certificate chain with a single intermediate CA. + * Certificate key type: RSA + * Signing algorithm for both certificates: RSA + */ + +static const unsigned char CERT0[] = { + 0x30, 0x82, 0x03, 0x3C, 0x30, 0x82, 0x02, 0x24, 0xA0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x14, 0x58, 0xDA, 0xBA, 0x36, 0xCD, 0xED, 0xA0, 0xDA, 0x5C, + 0x10, 0x33, 0x73, 0x8E, 0x0C, 0x64, 0x92, 0x79, 0x92, 0xAF, 0x03, 0x30, + 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, + 0x05, 0x00, 0x30, 0x27, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x0F, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6D, 0x65, 0x64, + 0x69, 0x61, 0x74, 0x65, 0x20, 0x43, 0x41, 0x30, 0x1E, 0x17, 0x0D, 0x31, + 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, + 0x17, 0x0D, 0x33, 0x37, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, + 0x35, 0x39, 0x5A, 0x30, 0x21, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x09, 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x68, 0x6F, + 0x73, 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, + 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xD4, + 0x7A, 0x1D, 0x27, 0xBA, 0x2B, 0x3A, 0x67, 0xB2, 0x91, 0x6A, 0xFB, 0xE7, + 0x83, 0x44, 0xCA, 0xED, 0x1C, 0x75, 0xAD, 0xDD, 0x4D, 0x83, 0x62, 0xD6, + 0xAA, 0x68, 0x95, 0xB2, 0x24, 0x21, 0x7B, 0x15, 0xAE, 0x2A, 0x99, 0x68, + 0x15, 0xED, 0x66, 0xF0, 0xB8, 0x58, 0xE7, 0xD3, 0xF5, 0x2E, 0xC6, 0xD9, + 0x2A, 0x5E, 0xE7, 0x0E, 0x2E, 0xE7, 0xFC, 0x67, 0x59, 0xC0, 0xC8, 0x61, + 0x7D, 0x4B, 0xA4, 0x6F, 0xDD, 0x9F, 0xD9, 0xC8, 0x85, 0x87, 0x64, 0xC7, + 0xBA, 0x1A, 0x0F, 0x29, 0xD4, 0x96, 0xA8, 0x78, 0x9A, 0x6B, 0x62, 0x20, + 0xA9, 0x32, 0xD0, 0xEE, 0xA9, 0x8C, 0x28, 0x61, 0x47, 0xA2, 0x50, 0x2A, + 0x63, 0xF6, 0x21, 0xDE, 0xDA, 0xD8, 0xD5, 0xF0, 0x7F, 0xC5, 0x00, 0x82, + 0x70, 0xE6, 0xA3, 0xBF, 0x5C, 0x89, 0x27, 0x4F, 0x51, 0x92, 0x77, 0x03, + 0xC3, 0xB0, 0xCC, 0x2E, 0x3B, 0xEC, 0x23, 0xF2, 0x2F, 0x53, 0x41, 0xAF, + 0x89, 0x93, 0xFF, 0xD2, 0x80, 0xB1, 0x43, 0x97, 0xDE, 0xD6, 0x19, 0xA0, + 0x92, 0x12, 0x7A, 0x3D, 0x66, 0x79, 0xE1, 0xC1, 0xBC, 0xE1, 0x77, 0x70, + 0xA2, 0x8B, 0x3D, 0x46, 0x84, 0x53, 0x3F, 0xE4, 0x4E, 0x42, 0x41, 0x37, + 0x92, 0x1E, 0x1F, 0xFD, 0x38, 0xB3, 0xF7, 0xEF, 0x87, 0x39, 0x80, 0xD3, + 0x56, 0xCF, 0xF4, 0xE0, 0x13, 0xDE, 0x64, 0xB0, 0x72, 0xA4, 0x03, 0x84, + 0xC4, 0x41, 0xED, 0x6F, 0xFA, 0x3E, 0xE2, 0xCA, 0x04, 0x20, 0xD2, 0xD7, + 0xDC, 0x2C, 0x82, 0x2B, 0x7A, 0xE2, 0x6D, 0xA1, 0x1C, 0x48, 0xDB, 0xCF, + 0x89, 0x4F, 0x34, 0x97, 0x3D, 0x28, 0xA8, 0x53, 0xDA, 0xE7, 0xC1, 0xE1, + 0x73, 0x15, 0xA3, 0x30, 0x76, 0x7F, 0x8F, 0x23, 0x42, 0x14, 0x3D, 0x51, + 0x34, 0xD2, 0x5A, 0xAD, 0x3C, 0x9B, 0xCB, 0xC8, 0xFE, 0x7F, 0x6E, 0x8E, + 0x40, 0xF3, 0xBD, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x66, 0x30, 0x64, + 0x30, 0x1F, 0x06, 0x03, 0x55, 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, + 0x14, 0xC5, 0x01, 0xAD, 0x7C, 0xE6, 0x63, 0xC9, 0x9C, 0x17, 0x5F, 0xC5, + 0xA2, 0xA7, 0xE1, 0x50, 0x5F, 0xE8, 0xF5, 0xA8, 0xC6, 0x30, 0x1D, 0x06, + 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0xC5, 0x01, 0xAD, 0x7C, + 0xE6, 0x63, 0xC9, 0x9C, 0x17, 0x5F, 0xC5, 0xA2, 0xA7, 0xE1, 0x50, 0x5F, + 0xE8, 0xF5, 0xA8, 0xC6, 0x30, 0x0C, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, + 0x01, 0xFF, 0x04, 0x02, 0x30, 0x00, 0x30, 0x14, 0x06, 0x03, 0x55, 0x1D, + 0x11, 0x04, 0x0D, 0x30, 0x0B, 0x82, 0x09, 0x6C, 0x6F, 0x63, 0x61, 0x6C, + 0x68, 0x6F, 0x73, 0x74, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, + 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x71, 0xB3, 0x5D, 0x21, 0xC2, 0x0E, 0xD7, 0xD0, 0xC6, 0xF8, 0x4A, 0x73, + 0x87, 0x4D, 0x22, 0x02, 0x27, 0xD0, 0xC7, 0xF8, 0xFD, 0x1A, 0x6D, 0x62, + 0x58, 0x82, 0x08, 0x76, 0x20, 0x07, 0xC0, 0x48, 0x92, 0xF3, 0xE9, 0x98, + 0x5A, 0xEB, 0x0D, 0x4E, 0x56, 0x95, 0x63, 0x68, 0x4D, 0xAB, 0xD4, 0x00, + 0x76, 0xE0, 0x37, 0xCF, 0x0B, 0x11, 0x61, 0x59, 0x5C, 0xDE, 0xF6, 0xA5, + 0x14, 0x59, 0xD9, 0x25, 0x9F, 0x59, 0xD9, 0xD3, 0x5E, 0x86, 0xAC, 0x1D, + 0xA6, 0x2C, 0x32, 0x42, 0x19, 0x32, 0x13, 0x40, 0x0B, 0x54, 0xCD, 0x67, + 0x26, 0xB6, 0xBD, 0xB4, 0x96, 0xA4, 0xCA, 0x1F, 0x7F, 0x37, 0xD6, 0xA8, + 0x75, 0xEB, 0x3A, 0x81, 0x51, 0x30, 0xB9, 0xF9, 0x4A, 0x01, 0x6F, 0xD1, + 0xD6, 0xED, 0x4F, 0xDF, 0x3F, 0x30, 0x60, 0x06, 0x67, 0x92, 0x8E, 0x61, + 0x85, 0x5F, 0x1A, 0xB5, 0x8C, 0xB3, 0x0F, 0x61, 0xA9, 0xFA, 0xDF, 0x5D, + 0xC4, 0x64, 0x00, 0xEA, 0x87, 0xB1, 0x33, 0x5F, 0x7D, 0xCB, 0xA3, 0x85, + 0x24, 0x6E, 0x2C, 0x65, 0x3B, 0xEA, 0x73, 0x3F, 0x86, 0xD3, 0xFD, 0xE8, + 0xE4, 0x02, 0xC6, 0x61, 0x6A, 0x26, 0x17, 0x52, 0x01, 0x25, 0x5C, 0x7F, + 0xA8, 0xCE, 0x69, 0x1F, 0xAC, 0x61, 0x2C, 0xB7, 0x04, 0xAF, 0xFD, 0xA4, + 0x7A, 0x99, 0xCB, 0x26, 0x15, 0x4C, 0xFF, 0x74, 0xD4, 0x73, 0x0C, 0x57, + 0x0C, 0x26, 0xEB, 0xD7, 0x2A, 0xDC, 0x5C, 0xC3, 0x41, 0xBB, 0xC2, 0xF3, + 0xCE, 0x16, 0xBD, 0x8E, 0x7B, 0xFB, 0xE9, 0xDF, 0xAB, 0x21, 0x0D, 0x91, + 0xB3, 0x8D, 0xD8, 0xCF, 0xC8, 0xF4, 0x49, 0xB9, 0xD0, 0xE3, 0x16, 0x70, + 0x3F, 0xB3, 0xDE, 0xD4, 0x07, 0x25, 0xFA, 0x46, 0x44, 0x52, 0x89, 0x4D, + 0x89, 0xC8, 0xE2, 0xCA, 0xB5, 0x33, 0x7F, 0xC7, 0x21, 0xBD, 0x28, 0xEF, + 0xFE, 0x66, 0x74, 0x03 +}; + +static const unsigned char CERT1[] = { + 0x30, 0x82, 0x03, 0x34, 0x30, 0x82, 0x02, 0x1C, 0xA0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x14, 0x70, 0x0F, 0x60, 0xEE, 0xF0, 0x07, 0x9A, 0x9C, 0x69, + 0xAC, 0x97, 0x62, 0x26, 0x4F, 0x5D, 0x05, 0xB6, 0xF9, 0x8F, 0x48, 0x30, + 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, + 0x05, 0x00, 0x30, 0x1C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x04, 0x52, 0x6F, 0x6F, 0x74, 0x30, 0x1E, 0x17, 0x0D, + 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x5A, 0x17, 0x0D, 0x33, 0x37, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, + 0x39, 0x35, 0x39, 0x5A, 0x30, 0x27, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x41, 0x31, 0x18, 0x30, 0x16, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x0F, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6D, + 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, + 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, + 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xB3, 0xE8, 0x6B, 0xAF, 0x9C, 0x16, + 0x52, 0xE3, 0x81, 0x0C, 0x50, 0xAB, 0x25, 0xCE, 0xCC, 0x0D, 0xC7, 0xF2, + 0x1F, 0x7F, 0x50, 0xDF, 0x2C, 0x5C, 0x35, 0xD6, 0x62, 0x2E, 0x63, 0x27, + 0x41, 0xA7, 0xE4, 0x53, 0xA8, 0x4B, 0x27, 0xFA, 0x13, 0x91, 0xA3, 0xFA, + 0x09, 0x4A, 0x2F, 0x3B, 0x5E, 0xCF, 0x77, 0xB3, 0x8A, 0xC1, 0xCD, 0x49, + 0x95, 0x9C, 0x75, 0x0D, 0x64, 0x74, 0xEF, 0xE4, 0xD7, 0x4B, 0xB9, 0xA1, + 0x9B, 0x68, 0xD2, 0x30, 0x71, 0x48, 0xEA, 0xF7, 0x4B, 0x14, 0xDF, 0x3F, + 0x47, 0xA9, 0xD8, 0xBB, 0xEC, 0x8F, 0x28, 0xCC, 0xFA, 0xDF, 0xB4, 0x1F, + 0x94, 0x7C, 0x96, 0xFC, 0x08, 0x05, 0x28, 0xF9, 0xE8, 0xF4, 0x2F, 0x2F, + 0xEE, 0x62, 0x9C, 0x8A, 0x3A, 0xE0, 0x85, 0x58, 0x60, 0xB6, 0x0F, 0x2D, + 0x30, 0xB4, 0xC0, 0x41, 0x54, 0x91, 0x4C, 0x1F, 0x5F, 0xAD, 0xF1, 0x19, + 0xF0, 0xC0, 0x22, 0xA6, 0x7D, 0xD8, 0x3F, 0x79, 0x34, 0x59, 0x42, 0x7B, + 0x5B, 0xB5, 0x41, 0xC4, 0x64, 0x7F, 0x52, 0xCF, 0x3C, 0x37, 0x22, 0xA1, + 0x2F, 0x79, 0x25, 0x94, 0x24, 0x41, 0xC2, 0x3F, 0xFA, 0xC7, 0x75, 0xFB, + 0x48, 0xB5, 0x0D, 0x18, 0xA7, 0xF4, 0x54, 0xF3, 0x2E, 0x6E, 0xD8, 0x43, + 0x58, 0xC4, 0xAB, 0x50, 0xE8, 0x05, 0xAD, 0x91, 0xB6, 0x1E, 0x01, 0x75, + 0xB3, 0x54, 0x9C, 0xDE, 0xA0, 0x99, 0x15, 0xFB, 0xAC, 0xF1, 0x5C, 0x97, + 0x49, 0x51, 0xCC, 0xEF, 0x58, 0x12, 0x6F, 0x73, 0x6B, 0xB3, 0x34, 0x14, + 0x01, 0x0F, 0x5A, 0x9D, 0xFA, 0xAA, 0xD6, 0x93, 0xD3, 0xE2, 0xEA, 0xC3, + 0xAB, 0xBC, 0x4E, 0xED, 0xCC, 0x51, 0xA1, 0xB8, 0xF8, 0x94, 0xB6, 0xB4, + 0x2C, 0xA8, 0x86, 0x2B, 0x1F, 0xF6, 0x51, 0x43, 0x29, 0x52, 0x5E, 0x13, + 0x89, 0xB3, 0x6A, 0x78, 0x60, 0x4E, 0x4E, 0xC0, 0x1B, 0xA5, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xA3, 0x63, 0x30, 0x61, 0x30, 0x1F, 0x06, 0x03, 0x55, + 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xC3, 0x0A, 0xBC, 0xB4, + 0x90, 0x63, 0x96, 0x92, 0x45, 0xBC, 0x27, 0xA0, 0xF0, 0x58, 0x89, 0x2A, + 0xD5, 0x78, 0x00, 0x12, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, + 0x16, 0x04, 0x14, 0x7C, 0xCF, 0xA3, 0xC6, 0x29, 0xF7, 0xF3, 0xC5, 0xAA, + 0x19, 0xD0, 0xC0, 0x16, 0xEB, 0xE0, 0x40, 0x0F, 0xCE, 0x44, 0xA7, 0x30, + 0x0E, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03, + 0x02, 0x00, 0x86, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, 0x01, + 0xFF, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xFF, 0x30, 0x0D, 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x54, 0x3B, 0xF4, 0xEA, 0xC6, 0xE4, 0xE8, 0xC0, + 0x5B, 0x12, 0xB2, 0xAD, 0xFA, 0xBD, 0xA9, 0xF4, 0x62, 0x03, 0xAD, 0x57, + 0x4C, 0x6F, 0x07, 0xA7, 0x47, 0x35, 0x75, 0x3E, 0x48, 0xA2, 0x5B, 0x71, + 0x10, 0x01, 0x8F, 0x93, 0x99, 0x9D, 0xA7, 0x47, 0xE3, 0x8E, 0x3E, 0xAF, + 0x2D, 0xBA, 0x06, 0xFE, 0xD4, 0xE4, 0x97, 0xBC, 0x10, 0x8D, 0xA6, 0xA5, + 0x3A, 0x3C, 0xE6, 0x21, 0xCB, 0x3B, 0xDA, 0x13, 0xE3, 0x4A, 0x23, 0xEB, + 0xBC, 0xBA, 0xA5, 0x90, 0x91, 0xAD, 0x55, 0x6E, 0xD5, 0xDD, 0x85, 0x4B, + 0x6B, 0x27, 0xD2, 0x39, 0x76, 0x06, 0x2E, 0xD4, 0x23, 0x5B, 0xBB, 0x55, + 0xB5, 0x49, 0x3F, 0x7D, 0x2F, 0x21, 0xCB, 0x96, 0x75, 0x19, 0x99, 0xA9, + 0x99, 0xF1, 0x0B, 0xBB, 0x3E, 0xC7, 0x10, 0xA7, 0x8B, 0x7A, 0x52, 0xB9, + 0x49, 0xE4, 0x14, 0x08, 0x78, 0xE7, 0x3D, 0xD9, 0x98, 0x57, 0xDA, 0xFF, + 0xDA, 0xB9, 0x8D, 0x66, 0x75, 0x80, 0xB5, 0x5D, 0xB9, 0xDF, 0x69, 0x65, + 0xDB, 0x16, 0xE7, 0x1E, 0xC4, 0x40, 0xE4, 0x46, 0x8A, 0x1E, 0x76, 0x32, + 0x16, 0xE7, 0x83, 0x67, 0xDA, 0x78, 0x56, 0xAB, 0x0F, 0x4C, 0x5C, 0xF1, + 0x70, 0xDA, 0x66, 0x20, 0xF4, 0xF8, 0x0F, 0xF6, 0xDC, 0x2F, 0x72, 0x97, + 0x6B, 0x78, 0x40, 0x79, 0x9D, 0x5B, 0x6D, 0xBE, 0x03, 0xCE, 0x70, 0x42, + 0xA8, 0x2A, 0x71, 0x11, 0xDF, 0x2E, 0x81, 0x01, 0xEA, 0x86, 0x12, 0x91, + 0x4F, 0xD0, 0x18, 0xED, 0x84, 0xA4, 0x48, 0x30, 0xB9, 0xB9, 0x7B, 0x56, + 0x3E, 0xEF, 0x8D, 0xE1, 0x1F, 0xFD, 0x90, 0xB2, 0x44, 0x85, 0x58, 0x6F, + 0xA7, 0x30, 0x74, 0x7C, 0xF1, 0xAE, 0x8C, 0x99, 0x3F, 0xF1, 0x4D, 0x57, + 0xC5, 0x95, 0x68, 0xC2, 0x36, 0xB2, 0xEC, 0xC2, 0x68, 0x7A, 0xC0, 0x81, + 0xA9, 0x55, 0x05, 0xE7, 0xCF, 0xAF, 0x50, 0xAC +}; + +static const br_x509_certificate SERVER_CERTIFICATE_CHAIN[] = { + { (unsigned char *)CERT0, sizeof CERT0 }, + { (unsigned char *)CERT1, sizeof CERT1 } +}; + +#define SERVER_CERTIFICATE_CHAIN_LEN 2 diff --git a/examples/tls_server_bearssl/key.h b/examples/tls_server_bearssl/key.h new file mode 100644 index 0000000..41c6b28 --- /dev/null +++ b/examples/tls_server_bearssl/key.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "bearssl.h" + +/* + * The private key for the server certificate (RSA). + */ + +static const unsigned char RSA_P[] = { + 0xF9, 0xF6, 0x32, 0x9C, 0x51, 0xD7, 0x71, 0xB2, 0x62, 0xED, 0x81, 0x24, + 0xDA, 0xF6, 0xA9, 0x2C, 0xC3, 0x2D, 0x55, 0x94, 0x95, 0x51, 0xE5, 0x36, + 0x12, 0x9E, 0x2E, 0x1F, 0x8E, 0xD8, 0x22, 0x22, 0x64, 0x5E, 0xA3, 0xFC, + 0x60, 0xF0, 0x14, 0xDC, 0x10, 0x48, 0x6F, 0x4F, 0x1A, 0x7D, 0xCC, 0x37, + 0x23, 0x27, 0x76, 0xB6, 0x31, 0xAA, 0xDE, 0x70, 0xC9, 0xEC, 0xAB, 0x0A, + 0x50, 0xF1, 0xEA, 0x79, 0x79, 0x74, 0xBE, 0x3B, 0xAB, 0x2E, 0x63, 0x97, + 0xA2, 0x6F, 0x0A, 0x45, 0x7B, 0xD7, 0x6C, 0xD2, 0x6D, 0x1B, 0x70, 0x31, + 0x03, 0xAF, 0x26, 0x8B, 0x28, 0xC5, 0xF8, 0x49, 0x67, 0x45, 0x41, 0xF1, + 0xD3, 0x35, 0x05, 0x27, 0x2A, 0x79, 0x21, 0xDA, 0x06, 0xB6, 0x3D, 0xA2, + 0xE8, 0x3C, 0x40, 0xFA, 0x6F, 0xC4, 0xFC, 0xF5, 0xC4, 0x05, 0xEB, 0x9A, + 0x31, 0xAA, 0x40, 0x7E, 0x5E, 0xEE, 0x22, 0x5B +}; + +static const unsigned char RSA_Q[] = { + 0xD9, 0x9C, 0x1A, 0xDC, 0x94, 0x51, 0xF6, 0xC1, 0x96, 0xDA, 0x0E, 0x11, + 0x3F, 0x37, 0x28, 0x7A, 0x33, 0xB0, 0xC4, 0xBE, 0xDF, 0x68, 0x2B, 0xD3, + 0x1C, 0x65, 0x67, 0x7A, 0x3C, 0xAF, 0x59, 0x50, 0xAB, 0x29, 0x36, 0x17, + 0x39, 0x15, 0x71, 0x97, 0x62, 0xDD, 0xCD, 0x25, 0xEE, 0x8D, 0xFF, 0x26, + 0x8F, 0x71, 0x21, 0xBE, 0x8F, 0x9C, 0x85, 0x4E, 0x3A, 0xE1, 0x24, 0xC7, + 0xE1, 0x4A, 0xDF, 0xD0, 0x60, 0xB3, 0xC4, 0x44, 0x2E, 0xAC, 0x73, 0x16, + 0x5F, 0x07, 0xC9, 0x3A, 0x73, 0x5E, 0xDA, 0x9B, 0xEE, 0xE2, 0xB4, 0xA9, + 0x3D, 0x33, 0x14, 0x7B, 0xEE, 0xA7, 0xD4, 0xAC, 0xF7, 0x53, 0xE6, 0x3E, + 0xF0, 0x85, 0x57, 0x4C, 0x8B, 0x96, 0x1B, 0xDD, 0xD7, 0x36, 0xFC, 0x89, + 0x37, 0x59, 0x75, 0x96, 0x75, 0x8B, 0x2E, 0xF7, 0x04, 0x2D, 0x29, 0x89, + 0xD9, 0xB7, 0x9F, 0x71, 0x3B, 0xE2, 0xED, 0xC7 +}; + +static const unsigned char RSA_DP[] = { + 0xE4, 0xFC, 0x02, 0x2F, 0x2B, 0xD6, 0x47, 0x04, 0xD9, 0x15, 0xA4, 0x5A, + 0x23, 0xF1, 0x14, 0xD7, 0xB9, 0xD1, 0x1C, 0xF6, 0x29, 0xB8, 0x45, 0x57, + 0x3C, 0xC5, 0x05, 0x91, 0xC9, 0x64, 0xFC, 0x18, 0x2F, 0x84, 0x77, 0x8B, + 0x6B, 0x2E, 0x64, 0x9D, 0x98, 0x99, 0x12, 0xC7, 0x0F, 0x88, 0xB0, 0x7C, + 0xCE, 0x4A, 0x87, 0xB8, 0xAE, 0x55, 0x4E, 0xC3, 0x5A, 0x67, 0xE1, 0xE4, + 0x68, 0x74, 0xC5, 0x8D, 0x14, 0x93, 0xBA, 0xF5, 0xA4, 0x82, 0xB1, 0x9F, + 0xA6, 0xA1, 0x3C, 0x72, 0x9C, 0xD9, 0xA3, 0x8A, 0x3D, 0x83, 0x86, 0x4A, + 0x90, 0x8A, 0x72, 0xAF, 0xC6, 0xE1, 0x5C, 0xEB, 0xB9, 0x9C, 0x3B, 0xA6, + 0x12, 0x0B, 0x1F, 0x36, 0x5A, 0xF5, 0x6E, 0xEA, 0x71, 0x7D, 0x9F, 0x87, + 0x4E, 0x62, 0x6C, 0x50, 0x3F, 0xF5, 0xE0, 0x9A, 0x30, 0x42, 0x10, 0x2C, + 0x48, 0x55, 0x24, 0x11, 0xE0, 0x5B, 0x1C, 0xC3 +}; + +static const unsigned char RSA_DQ[] = { + 0x8D, 0xAC, 0xE0, 0xA0, 0x33, 0xC0, 0x99, 0x52, 0xB8, 0x90, 0x07, 0x10, + 0x9B, 0x83, 0xA1, 0xCA, 0xCD, 0xD4, 0x8C, 0x83, 0x68, 0x98, 0x3D, 0xD0, + 0x18, 0x70, 0xBC, 0xCA, 0x0C, 0xB0, 0x6D, 0x09, 0xE4, 0x25, 0xD4, 0x9D, + 0x92, 0x00, 0xB0, 0x0F, 0xCB, 0xC2, 0x74, 0x49, 0xF9, 0xE2, 0x60, 0xF8, + 0x0D, 0xF3, 0xAD, 0xF0, 0x8F, 0x37, 0x6C, 0x62, 0xDE, 0x5A, 0xAE, 0xC3, + 0xA3, 0x9E, 0x47, 0xD1, 0x36, 0xE4, 0x53, 0x27, 0xC0, 0xEB, 0x6D, 0x92, + 0x67, 0x14, 0x7E, 0xA2, 0x9B, 0x72, 0x6A, 0x09, 0x93, 0xA1, 0xED, 0xD5, + 0x31, 0x8F, 0x0C, 0x0B, 0x13, 0xFA, 0x18, 0xB0, 0xF3, 0xE5, 0x9F, 0xC5, + 0xE2, 0x7A, 0x2D, 0xB8, 0x1C, 0x39, 0x02, 0xB3, 0x8F, 0xE6, 0xB0, 0xCB, + 0xF5, 0x49, 0x3D, 0x11, 0x54, 0x3D, 0xE5, 0xB9, 0xD4, 0xF2, 0x42, 0x55, + 0x09, 0x76, 0x4F, 0x4C, 0x3D, 0x9D, 0x25, 0x09 +}; + +static const unsigned char RSA_IQ[] = { + 0x72, 0x0C, 0xA7, 0xCF, 0x06, 0x95, 0x69, 0xF4, 0x75, 0x20, 0x34, 0x03, + 0xE0, 0xCF, 0x9A, 0x51, 0x93, 0xF5, 0x42, 0x2E, 0xF2, 0x85, 0xBE, 0xCE, + 0x4F, 0x38, 0xB5, 0x8C, 0xA2, 0x99, 0x42, 0xF3, 0xBD, 0x65, 0x38, 0xE2, + 0x34, 0x3F, 0x21, 0x9D, 0xF5, 0xBD, 0xB3, 0xBF, 0x73, 0x3C, 0x18, 0xDE, + 0xF6, 0xF0, 0x7F, 0xA1, 0xC2, 0x55, 0xF2, 0x38, 0xE9, 0x0E, 0x1E, 0x31, + 0xE7, 0xDB, 0x51, 0xC5, 0x71, 0x8D, 0x67, 0x71, 0x3A, 0x9F, 0x55, 0x52, + 0x60, 0xEE, 0x45, 0xF6, 0x08, 0x98, 0x81, 0xB7, 0x7B, 0x2F, 0xF2, 0x96, + 0x7D, 0x73, 0xD0, 0xA6, 0xAB, 0xAA, 0x83, 0x49, 0x41, 0x35, 0xA9, 0x90, + 0x67, 0xCE, 0xD3, 0xB9, 0x73, 0x54, 0xAA, 0x84, 0x00, 0x88, 0x88, 0x90, + 0x1D, 0x86, 0x9B, 0xE5, 0xB8, 0xCE, 0x89, 0x0A, 0x1B, 0x47, 0x62, 0x31, + 0xC2, 0x3F, 0xC3, 0x8C, 0x86, 0x09, 0x3C, 0x86 +}; + +static const br_rsa_private_key SERVER_PRIVATE_KEY = { + 2048, + (unsigned char *)RSA_P, sizeof RSA_P, + (unsigned char *)RSA_Q, sizeof RSA_Q, + (unsigned char *)RSA_DP, sizeof RSA_DP, + (unsigned char *)RSA_DQ, sizeof RSA_DQ, + (unsigned char *)RSA_IQ, sizeof RSA_IQ +}; \ No newline at end of file diff --git a/examples/tls_server_bearssl/tls_server_bearssl.c b/examples/tls_server_bearssl/tls_server_bearssl.c new file mode 100644 index 0000000..09632ae --- /dev/null +++ b/examples/tls_server_bearssl/tls_server_bearssl.c @@ -0,0 +1,188 @@ +/* tls_server_bearssl - simple BearSSL TLS server that outputs some statistics to a connecting client, + * then closes the socket. + * + * Uses code from the server_basic example in BearSSL and the tls_server example. + * + * To test this program, connect to the ESP using openssl command line like this: + * + * openssl s_client -connect 192.168.66.209:800 + * + * The openssl command line client will print some information for the (self-signed) server certificate, + * then after a couple of seconds (validation) there will be a few lines of text output sent from the ESP. + * + * See the certificate.h and key.h files for private key & certificate, plus information for generation. + * + * Original Copyright (c) 2016 Thomas Pornin , MIT License. + * Additions Copyright (C) 2016 Stefan Schake, MIT License. + */ +#include "espressif/esp_common.h" +#include "esp/uart.h" +#include "esp/hwrand.h" + +#include + +#include "FreeRTOS.h" +#include "task.h" + +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include "lwip/netdb.h" +#include "lwip/dns.h" +#include "lwip/api.h" + +#include "ssid_config.h" + +/* Server cert & key */ +#include "certificate.h" +#include "key.h" + +#include "bearssl.h" + +#define PORT 800 + +/* + * Low-level data read callback for the simplified SSL I/O API. + */ +static int +sock_read(void *ctx, unsigned char *buf, size_t len) +{ + for (;;) { + ssize_t rlen; + + rlen = read(*(int *)ctx, buf, len); + if (rlen <= 0) { + if (rlen < 0 && errno == EINTR) { + continue; + } + return -1; + } + return (int)rlen; + } +} + +/* + * Low-level data write callback for the simplified SSL I/O API. + */ +static int +sock_write(void *ctx, const unsigned char *buf, size_t len) +{ + for (;;) { + ssize_t wlen; + + wlen = write(*(int *)ctx, buf, len); + if (wlen <= 0) { + if (wlen < 0 && errno == EINTR) { + continue; + } + return -1; + } + return (int)wlen; + } +} + +/* + * BearSSL IO buffer and server state + */ +static unsigned char iobuf[BR_SSL_BUFSIZE_MONO]; +br_ssl_server_context sc; +br_sslio_context ioc; + +void tls_server_task(void *pvParameters) +{ + int successes = 0, failures = 0; + printf("TLS server task starting...\n"); + + /* Prepare server socket */ + int sfd = socket(PF_INET, SOCK_STREAM, 0); + if (sfd < 0) { + printf("Failed to allocate socket.\r\n"); + return; + } + struct sockaddr_in addr; + bzero(&addr, sizeof(addr)); + addr.sin_port = htons(PORT); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + if (bind(sfd, (const struct sockaddr*) &addr, sizeof(addr)) < 0) { + close(sfd); + printf("Failed to bind socket\r\n"); + return; + } + if (listen(sfd, 0) < 0) { + close(sfd); + printf("Failed to listen\r\n"); + return; + } + + /* Initialize engine */ + br_ssl_server_init_full_rsa(&sc, SERVER_CERTIFICATE_CHAIN, SERVER_CERTIFICATE_CHAIN_LEN, &SERVER_PRIVATE_KEY); + br_ssl_engine_set_buffer(&sc.eng, iobuf, sizeof iobuf, 0); + + /* + * Inject some entropy from the ESP hardware RNG + * This is necessary because we don't support any of the BearSSL methods + */ + for (int i = 0; i < 10; i++) { + int rand = hwrand(); + br_ssl_engine_inject_entropy(&sc.eng, &rand, 4); + } + + while(1) { + printf("Top of the loop, free heap = %u\r\n", xPortGetFreeHeapSize()); + + /* Accept a new client */ + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + int cfd = accept(sfd, (struct sockaddr*)&sa, &sa_len); + if (cfd < 0) { + continue; + } + + /* Prepare for a new handshake */ + br_ssl_server_reset(&sc); + /* Initialize the simplified IO wrapper */ + br_sslio_init(&ioc, &sc.eng, sock_read, &cfd, sock_write, &cfd); + + /* Prepare a message to the client */ + unsigned char buf[100]; + int len = sprintf((char *) buf, "O hai, client %d.%d.%d.%d:%d\r\nFree heap size is %d bytes\r\n", + ip4_addr1(&sa.sin_addr), ip4_addr2(&sa.sin_addr), + ip4_addr3(&sa.sin_addr), ip4_addr4(&sa.sin_addr), + ntohs(sa.sin_port), xPortGetFreeHeapSize()); + + /* Send the message and close the connection */ + br_sslio_write_all(&ioc, buf, len); + br_sslio_close(&ioc); + + /* Check if something bad happened */ + if (br_ssl_engine_last_error(&sc.eng) != BR_ERR_OK) { + close(cfd); + printf("failure, error = %d\r\n", br_ssl_engine_last_error(&sc.eng)); + failures++; + continue; + } + + close(cfd); + successes++; + printf("successes = %d failures = %d\r\n", successes, failures); + printf("Waiting for next client...\r\n"); + } +} + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + struct sdk_station_config config = { + .ssid = WIFI_SSID, + .password = WIFI_PASS, + }; + + /* required to call wifi_set_opmode before station_set_config */ + sdk_wifi_set_opmode(STATION_MODE); + sdk_wifi_station_set_config(&config); + + xTaskCreate(&tls_server_task, "server_task", 2048, NULL, 2, NULL); +} diff --git a/extras/bearssl/BearSSL b/extras/bearssl/BearSSL new file mode 160000 index 0000000..f0c0046 --- /dev/null +++ b/extras/bearssl/BearSSL @@ -0,0 +1 @@ +Subproject commit f0c00466018e4bcdaa2d965ac723d53f015cde9a diff --git a/extras/bearssl/component.mk b/extras/bearssl/component.mk new file mode 100644 index 0000000..4e99cb6 --- /dev/null +++ b/extras/bearssl/component.mk @@ -0,0 +1,14 @@ +# Component makefile for BearSSL + +BEARSSL_DIR = $(bearssl_ROOT)BearSSL/ +INC_DIRS += $(BEARSSL_DIR)inc + +# args for passing into compile rule generation +bearssl_INC_DIR = $(BEARSSL_DIR)inc $(BEARSSL_DIR)src +bearssl_SRC_DIR = $(BEARSSL_DIR)src $(sort $(dir $(wildcard $(BEARSSL_DIR)src/*/))) + +$(eval $(call component_compile_rules,bearssl)) + +# Helpful error if git submodule not initialised +$(BEARSSL_DIR): + $(error "bearssl git submodule not installed. Please run 'git submodule update --init'") diff --git a/extras/crc_generic/crc_lib b/extras/crc_generic/crc_lib index e013b43..a97013c 160000 --- a/extras/crc_generic/crc_lib +++ b/extras/crc_generic/crc_lib @@ -1 +1 @@ -Subproject commit e013b431f58d916b68138b838d2a1c4584304de0 +Subproject commit a97013c72f686735889b7a0908bbbf15a104f7d0 diff --git a/extras/httpd/component.mk b/extras/httpd/component.mk new file mode 100644 index 0000000..c93f8f9 --- /dev/null +++ b/extras/httpd/component.mk @@ -0,0 +1,9 @@ +# Component makefile for extras/httpd + +# expected anyone using httpd includes it as 'httpd/httpd.h' +INC_DIRS += $(httpd_ROOT).. + +# args for passing into compile rule generation +httpd_SRC_DIR = $(httpd_ROOT) + +$(eval $(call component_compile_rules,httpd)) diff --git a/extras/httpd/fs.c b/extras/httpd/fs.c new file mode 100644 index 0000000..6faa628 --- /dev/null +++ b/extras/httpd/fs.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#include "lwip/opt.h" +#include "lwip/def.h" +#include "fs.h" +#include "fsdata.h" +#include + +/** Set this to 1 to include "fsdata_custom.c" instead of "fsdata.c" for the + * file system (to prevent changing the file included in CVS) */ +#ifndef HTTPD_USE_CUSTOM_FSDATA +#define HTTPD_USE_CUSTOM_FSDATA 0 +#endif + +#if HTTPD_USE_CUSTOM_FSDATA +#include "fsdata_custom.c" +#else /* HTTPD_USE_CUSTOM_FSDATA */ +#include "fsdata.c" +#endif /* HTTPD_USE_CUSTOM_FSDATA */ + +/*-----------------------------------------------------------------------------------*/ + +#if LWIP_HTTPD_CUSTOM_FILES +int fs_open_custom(struct fs_file *file, const char *name); +void fs_close_custom(struct fs_file *file); +#if LWIP_HTTPD_FS_ASYNC_READ +u8_t fs_canread_custom(struct fs_file *file); +u8_t fs_wait_read_custom(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + +/*-----------------------------------------------------------------------------------*/ +err_t +fs_open(struct fs_file *file, const char *name) +{ + const struct fsdata_file *f; + + if ((file == NULL) || (name == NULL)) { + return ERR_ARG; + } + +#if LWIP_HTTPD_CUSTOM_FILES + if (fs_open_custom(file, name)) { + file->is_custom_file = 1; + return ERR_OK; + } + file->is_custom_file = 0; +#endif /* LWIP_HTTPD_CUSTOM_FILES */ + + for (f = FS_ROOT; f != NULL; f = f->next) { + if (!strcmp(name, (char *)f->name)) { + file->data = (const char *)f->data; + file->len = f->len; + file->index = f->len; + file->pextension = NULL; + file->http_header_included = f->http_header_included; +#if HTTPD_PRECALCULATED_CHECKSUM + file->chksum_count = f->chksum_count; + file->chksum = f->chksum; +#endif /* HTTPD_PRECALCULATED_CHECKSUM */ +#if LWIP_HTTPD_FILE_STATE + file->state = fs_state_init(file, name); +#endif /* #if LWIP_HTTPD_FILE_STATE */ + return ERR_OK; + } + } + /* file not found */ + return ERR_VAL; +} + +/*-----------------------------------------------------------------------------------*/ +void +fs_close(struct fs_file *file) +{ +#if LWIP_HTTPD_CUSTOM_FILES + if (file->is_custom_file) { + fs_close_custom(file); + } +#endif /* LWIP_HTTPD_CUSTOM_FILES */ +#if LWIP_HTTPD_FILE_STATE + fs_state_free(file, file->state); +#endif /* #if LWIP_HTTPD_FILE_STATE */ + LWIP_UNUSED_ARG(file); +} +/*-----------------------------------------------------------------------------------*/ +#if LWIP_HTTPD_DYNAMIC_FILE_READ +#if LWIP_HTTPD_FS_ASYNC_READ +int +fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg) +#else /* LWIP_HTTPD_FS_ASYNC_READ */ +int +fs_read(struct fs_file *file, char *buffer, int count) +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +{ + int read; + + if(file->index == file->len) { + return FS_READ_EOF; + } +#if LWIP_HTTPD_FS_ASYNC_READ +#if LWIP_HTTPD_CUSTOM_FILES + if (!fs_canread_custom(file)) { + if (fs_wait_read_custom(file, callback_fn, callback_arg)) { + return FS_READ_DELAYED; + } + } +#else /* LWIP_HTTPD_CUSTOM_FILES */ + LWIP_UNUSED_ARG(callback_fn); + LWIP_UNUSED_ARG(callback_arg); +#endif /* LWIP_HTTPD_CUSTOM_FILES */ +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + + read = file->len - file->index; + if(read > count) { + read = count; + } + + MEMCPY(buffer, (file->data + file->index), read); + file->index += read; + + return(read); +} +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ +/*-----------------------------------------------------------------------------------*/ +#if LWIP_HTTPD_FS_ASYNC_READ +int +fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg) +{ + if (file != NULL) { +#if LWIP_HTTPD_FS_ASYNC_READ +#if LWIP_HTTPD_CUSTOM_FILES + if (!fs_canread_custom(file)) { + if (fs_wait_read_custom(file, callback_fn, callback_arg)) { + return 0; + } + } +#else /* LWIP_HTTPD_CUSTOM_FILES */ + LWIP_UNUSED_ARG(callback_fn); + LWIP_UNUSED_ARG(callback_arg); +#endif /* LWIP_HTTPD_CUSTOM_FILES */ +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + } + return 1; +} +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +/*-----------------------------------------------------------------------------------*/ +int +fs_bytes_left(struct fs_file *file) +{ + return file->len - file->index; +} diff --git a/extras/httpd/fs.h b/extras/httpd/fs.h new file mode 100644 index 0000000..e8286a7 --- /dev/null +++ b/extras/httpd/fs.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __FS_H__ +#define __FS_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" + +/** Set this to 1 and provide the functions: + * - "int fs_open_custom(struct fs_file *file, const char *name)" + * Called first for every opened file to allow opening files + * that are not included in fsdata(_custom).c + * - "void fs_close_custom(struct fs_file *file)" + * Called to free resources allocated by fs_open_custom(). + */ +#ifndef LWIP_HTTPD_CUSTOM_FILES +#define LWIP_HTTPD_CUSTOM_FILES 0 +#endif + +/** Set this to 1 to support fs_read() to dynamically read file data. + * Without this (default=off), only one-block files are supported, + * and the contents must be ready after fs_open(). + */ +#ifndef LWIP_HTTPD_DYNAMIC_FILE_READ +#define LWIP_HTTPD_DYNAMIC_FILE_READ 0 +#endif + +/** Set this to 1 to include an application state argument per file + * that is opened. This allows to keep a state per connection/file. + */ +#ifndef LWIP_HTTPD_FILE_STATE +#define LWIP_HTTPD_FILE_STATE 0 +#endif + +/** HTTPD_PRECALCULATED_CHECKSUM==1: include precompiled checksums for + * predefined (MSS-sized) chunks of the files to prevent having to calculate + * the checksums at runtime. */ +#ifndef HTTPD_PRECALCULATED_CHECKSUM +#define HTTPD_PRECALCULATED_CHECKSUM 0 +#endif + +/** LWIP_HTTPD_FS_ASYNC_READ==1: support asynchronous read operations + * (fs_read_async returns FS_READ_DELAYED and calls a callback when finished). + */ +#ifndef LWIP_HTTPD_FS_ASYNC_READ +#define LWIP_HTTPD_FS_ASYNC_READ 0 +#endif + +#define FS_READ_EOF -1 +#define FS_READ_DELAYED -2 + +#if HTTPD_PRECALCULATED_CHECKSUM +struct fsdata_chksum { + u32_t offset; + u16_t chksum; + u16_t len; +}; +#endif /* HTTPD_PRECALCULATED_CHECKSUM */ + +struct fs_file { + const char *data; + int len; + int index; + void *pextension; +#if HTTPD_PRECALCULATED_CHECKSUM + const struct fsdata_chksum *chksum; + u16_t chksum_count; +#endif /* HTTPD_PRECALCULATED_CHECKSUM */ + u8_t http_header_included; +#if LWIP_HTTPD_CUSTOM_FILES + u8_t is_custom_file; +#endif /* LWIP_HTTPD_CUSTOM_FILES */ +#if LWIP_HTTPD_FILE_STATE + void *state; +#endif /* LWIP_HTTPD_FILE_STATE */ +}; + +#if LWIP_HTTPD_FS_ASYNC_READ +typedef void (*fs_wait_cb)(void *arg); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +err_t fs_open(struct fs_file *file, const char *name); +void fs_close(struct fs_file *file); +#if LWIP_HTTPD_DYNAMIC_FILE_READ +#if LWIP_HTTPD_FS_ASYNC_READ +int fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg); +#else /* LWIP_HTTPD_FS_ASYNC_READ */ +int fs_read(struct fs_file *file, char *buffer, int count); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ +#if LWIP_HTTPD_FS_ASYNC_READ +int fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ +int fs_bytes_left(struct fs_file *file); + +#if LWIP_HTTPD_FILE_STATE +/** This user-defined function is called when a file is opened. */ +void *fs_state_init(struct fs_file *file, const char *name); +/** This user-defined function is called when a file is closed. */ +void fs_state_free(struct fs_file *file, void *state); +#endif /* #if LWIP_HTTPD_FILE_STATE */ + +#endif /* __FS_H__ */ diff --git a/extras/httpd/fsdata.h b/extras/httpd/fsdata.h new file mode 100644 index 0000000..de1d259 --- /dev/null +++ b/extras/httpd/fsdata.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __FSDATA_H__ +#define __FSDATA_H__ + +#include "lwip/opt.h" +#include "fs.h" + +struct fsdata_file { + const struct fsdata_file *next; + const unsigned char *name; + const unsigned char *data; + int len; + u8_t http_header_included; +#if HTTPD_PRECALCULATED_CHECKSUM + u16_t chksum_count; + const struct fsdata_chksum *chksum; +#endif /* HTTPD_PRECALCULATED_CHECKSUM */ +}; + +#endif /* __FSDATA_H__ */ diff --git a/extras/httpd/httpd.c b/extras/httpd/httpd.c new file mode 100644 index 0000000..f647ca6 --- /dev/null +++ b/extras/httpd/httpd.c @@ -0,0 +1,2754 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +/* This httpd supports for a + * rudimentary server-side-include facility which will replace tags of the form + * in any file whose extension is .shtml, .shtm or .ssi with + * strings provided by an include handler whose pointer is provided to the + * module via function http_set_ssi_handler(). + * Additionally, a simple common + * gateway interface (CGI) handling mechanism has been added to allow clients + * to hook functions to particular request URIs. + * + * To enable SSI support, define label LWIP_HTTPD_SSI in lwipopts.h. + * To enable CGI support, define label LWIP_HTTPD_CGI in lwipopts.h. + * + * By default, the server assumes that HTTP headers are already present in + * each file stored in the file system. By defining LWIP_HTTPD_DYNAMIC_HEADERS in + * lwipopts.h, this behavior can be changed such that the server inserts the + * headers automatically based on the extension of the file being served. If + * this mode is used, be careful to ensure that the file system image used + * does not already contain the header information. + * + * File system images without headers can be created using the makefsfile + * tool with the -h command line option. + * + * + * Notes about valid SSI tags + * -------------------------- + * + * The following assumptions are made about tags used in SSI markers: + * + * 1. No tag may contain '-' or whitespace characters within the tag name. + * 2. Whitespace is allowed between the tag leadin "". + * 3. The maximum tag name length is LWIP_HTTPD_MAX_TAG_NAME_LEN, currently 8 characters. + * + * Notes on CGI usage + * ------------------ + * + * The simple CGI support offered here works with GET method requests only + * and can handle up to 16 parameters encoded into the URI. The handler + * function may not write directly to the HTTP output but must return a + * filename that the HTTP server will send to the browser as a response to + * the incoming CGI request. + * + * + * + * The list of supported file types is quite short, so if makefsdata complains + * about an unknown extension, make sure to add it (and its doctype) to + * the 'g_psHTTPHeaders' list. + */ +#include "httpd.h" +#include "lwip/debug.h" +#include "lwip/stats.h" +#include "httpd_structs.h" +#include "lwip/tcp.h" +#include "fs.h" + +#include +#include + +#if LWIP_TCP + +#ifndef HTTPD_DEBUG +#define HTTPD_DEBUG LWIP_DBG_OFF +#endif + +/** Set this to 1 and add the next line to lwippools.h to use a memp pool + * for allocating struct http_state instead of the heap: + * + * LWIP_MEMPOOL(HTTPD_STATE, 20, 100, "HTTPD_STATE") + */ +#ifndef HTTPD_USE_MEM_POOL +#define HTTPD_USE_MEM_POOL 0 +#endif + +/** The server port for HTTPD to use */ +#ifndef HTTPD_SERVER_PORT +#define HTTPD_SERVER_PORT 80 +#endif + +/** Maximum retries before the connection is aborted/closed. + * - number of times pcb->poll is called -> default is 4*500ms = 2s; + * - reset when pcb->sent is called + */ +#ifndef HTTPD_MAX_RETRIES +#define HTTPD_MAX_RETRIES 4 +#endif + +/** The poll delay is X*500ms */ +#ifndef HTTPD_POLL_INTERVAL +#define HTTPD_POLL_INTERVAL 4 +#endif + +/** Priority for tcp pcbs created by HTTPD (very low by default). + * Lower priorities get killed first when running out of memroy. + */ +#ifndef HTTPD_TCP_PRIO +#define HTTPD_TCP_PRIO TCP_PRIO_MIN +#endif + +/** Set this to 1 to enabled timing each file sent */ +#ifndef LWIP_HTTPD_TIMING +#define LWIP_HTTPD_TIMING 0 +#endif +#ifndef HTTPD_DEBUG_TIMING +#define HTTPD_DEBUG_TIMING LWIP_DBG_OFF +#endif + +/** Set this to 1 on platforms where strnstr is not available */ +#ifndef LWIP_HTTPD_STRNSTR_PRIVATE +#define LWIP_HTTPD_STRNSTR_PRIVATE 1 +#endif + +/** Set this to one to show error pages when parsing a request fails instead + of simply closing the connection. */ +#ifndef LWIP_HTTPD_SUPPORT_EXTSTATUS +#define LWIP_HTTPD_SUPPORT_EXTSTATUS 0 +#endif + +/** Set this to 0 to drop support for HTTP/0.9 clients (to save some bytes) */ +#ifndef LWIP_HTTPD_SUPPORT_V09 +#define LWIP_HTTPD_SUPPORT_V09 1 +#endif + +/** Set this to 1 to enable HTTP/1.1 persistent connections. + * ATTENTION: If the generated file system includes HTTP headers, these must + * include the "Connection: keep-alive" header (pass argument "-11" to makefsdata). + */ +#ifndef LWIP_HTTPD_SUPPORT_11_KEEPALIVE +#define LWIP_HTTPD_SUPPORT_11_KEEPALIVE 0 +#endif + +/** Set this to 1 to support HTTP request coming in in multiple packets/pbufs */ +#ifndef LWIP_HTTPD_SUPPORT_REQUESTLIST +#define LWIP_HTTPD_SUPPORT_REQUESTLIST 1 +#endif + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST +/** Number of rx pbufs to enqueue to parse an incoming request (up to the first + newline) */ +#ifndef LWIP_HTTPD_REQ_QUEUELEN +#define LWIP_HTTPD_REQ_QUEUELEN 5 +#endif + +/** Number of (TCP payload-) bytes (in pbufs) to enqueue to parse and incoming + request (up to the first double-newline) */ +#ifndef LWIP_HTTPD_REQ_BUFSIZE +#define LWIP_HTTPD_REQ_BUFSIZE LWIP_HTTPD_MAX_REQ_LENGTH +#endif + +/** Defines the maximum length of a HTTP request line (up to the first CRLF, + copied from pbuf into this a global buffer when pbuf- or packet-queues + are received - otherwise the input pbuf is used directly) */ +#ifndef LWIP_HTTPD_MAX_REQ_LENGTH +#define LWIP_HTTPD_MAX_REQ_LENGTH LWIP_MIN(1023, (LWIP_HTTPD_REQ_QUEUELEN * PBUF_POOL_BUFSIZE)) +#endif +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + +/** Maximum length of the filename to send as response to a POST request, + * filled in by the application when a POST is finished. + */ +#ifndef LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN +#define LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN 63 +#endif + +/** Set this to 0 to not send the SSI tag (default is on, so the tag will + * be sent in the HTML page */ +#ifndef LWIP_HTTPD_SSI_INCLUDE_TAG +#define LWIP_HTTPD_SSI_INCLUDE_TAG 1 +#endif + +/** Set this to 1 to call tcp_abort when tcp_close fails with memory error. + * This can be used to prevent consuming all memory in situations where the + * HTTP server has low priority compared to other communication. */ +#ifndef LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR +#define LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR 0 +#endif + +/** Set this to 1 to kill the oldest connection when running out of + * memory for 'struct http_state' or 'struct http_ssi_state'. + * ATTENTION: This puts all connections on a linked list, so may be kind of slow. + */ +#ifndef LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED +#define LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 0 +#endif + +/** Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes */ +#define MIN_REQ_LEN 7 + +#define CRLF "\r\n" +#define HTTP11_CONNECTIONKEEPALIVE "Connection: keep-alive" + +#if LWIP_HTTPD_SSI +#define LWIP_HTTPD_IS_SSI(hs) ((hs)->ssi) +#else /* LWIP_HTTPD_SSI */ +#define LWIP_HTTPD_IS_SSI(hs) 0 +#endif /* LWIP_HTTPD_SSI */ + +/** These defines check whether tcp_write has to copy data or not */ + +/** This was TI's check whether to let TCP copy data or not +#define HTTP_IS_DATA_VOLATILE(hs) ((hs->file < (char *)0x20000000) ? 0 : TCP_WRITE_FLAG_COPY)*/ +#ifndef HTTP_IS_DATA_VOLATILE +#if LWIP_HTTPD_SSI +/* Copy for SSI files, no copy for non-SSI files */ +#define HTTP_IS_DATA_VOLATILE(hs) ((hs)->ssi ? TCP_WRITE_FLAG_COPY : 0) +#else /* LWIP_HTTPD_SSI */ +/** Default: don't copy if the data is sent from file-system directly */ +#define HTTP_IS_DATA_VOLATILE(hs) (((hs->file != NULL) && (hs->handle != NULL) && (hs->file == \ + (char*)hs->handle->data + hs->handle->len - hs->left)) \ + ? 0 : TCP_WRITE_FLAG_COPY) +#endif /* LWIP_HTTPD_SSI */ +#endif + +/** Default: headers are sent from ROM */ +#ifndef HTTP_IS_HDR_VOLATILE +#define HTTP_IS_HDR_VOLATILE(hs, ptr) 0 +#endif + +#if LWIP_HTTPD_SSI +/** Default: Tags are sent from struct http_state and are therefore volatile */ +#ifndef HTTP_IS_TAG_VOLATILE +#define HTTP_IS_TAG_VOLATILE(ptr) TCP_WRITE_FLAG_COPY +#endif +#endif /* LWIP_HTTPD_SSI */ + +/* Return values for http_send_*() */ +#define HTTP_DATA_TO_SEND_BREAK 2 +#define HTTP_DATA_TO_SEND_CONTINUE 1 +#define HTTP_NO_DATA_TO_SEND 0 + +#if HTTPD_USE_MEM_POOL +#define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)memp_malloc(MEMP_HTTPD_SSI_STATE) +#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)memp_malloc(MEMP_HTTPD_STATE) +#else /* HTTPD_USE_MEM_POOL */ +#define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)mem_malloc(sizeof(struct http_ssi_state)) +#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state)) +#endif /* HTTPD_USE_MEM_POOL */ + +#include +#include +#include "strcasestr.h" + +static const char WS_HEADER[] = "Upgrade: websocket\r\n"; +static const char WS_GUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; +static const char WS_RSP[] = "HTTP/1.1 101 Switching Protocols\r\n" \ + "Upgrade: websocket\r\n" \ + "Connection: Upgrade\r\n" \ + "Sec-WebSocket-Accept: "; + +/* Response buffer length (30 = base64 encoded key max length) */ +#define WS_BUF_LEN (sizeof(WS_RSP) + sizeof(CRLF CRLF) + 30 - 2) + +/* WebSocket timeout: X*(HTTPD_POLL_INTERVAL), default is 10*4*500ms = 20s */ +#ifndef WS_TIMEOUT +#define WS_TIMEOUT 10 +#endif + +/* Callback functions */ +static tWsHandler websocket_cb = NULL; +static tWsOpenHandler websocket_open_cb = NULL; + +typedef struct +{ + const char *name; + u8_t shtml; +} default_filename; + +const default_filename g_psDefaultFilenames[] = { + {"/index.shtml", 1 }, + {"/index.ssi", 1 }, + {"/index.shtm", 1 }, + {"/index.html", 0 }, + {"/index.htm", 0 } +}; + +#define NUM_DEFAULT_FILENAMES (sizeof(g_psDefaultFilenames) / \ + sizeof(default_filename)) + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST +/** HTTP request is copied here from pbufs for simple parsing */ +static char httpd_req_buf[LWIP_HTTPD_MAX_REQ_LENGTH+1]; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + +#if LWIP_HTTPD_SUPPORT_POST +/** Filename for response file to send when POST is finished */ +static char http_post_response_filename[LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN+1]; +#endif /* LWIP_HTTPD_SUPPORT_POST */ + +#if LWIP_HTTPD_DYNAMIC_HEADERS +/* The number of individual strings that comprise the headers sent before each + * requested file. + */ +#define NUM_FILE_HDR_STRINGS 3 +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + +#if LWIP_HTTPD_SSI + +#define HTTPD_LAST_TAG_PART 0xFFFF + +enum tag_check_state { + TAG_NONE, /* Not processing an SSI tag */ + TAG_LEADIN, /* Tag lead in "" being processed */ + TAG_SENDING /* Sending tag replacement string */ +}; + +struct http_ssi_state { + const char *parsed; /* Pointer to the first unparsed byte in buf. */ +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + const char *tag_started;/* Pointer to the first opening '<' of the tag. */ +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */ + const char *tag_end; /* Pointer to char after the closing '>' of the tag. */ + u32_t parse_left; /* Number of unparsed bytes in buf. */ + u16_t tag_index; /* Counter used by tag parsing state machine */ + u16_t tag_insert_len; /* Length of insert in string tag_insert */ +#if LWIP_HTTPD_SSI_MULTIPART + u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */ +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + u8_t tag_name_len; /* Length of the tag name in string tag_name */ + char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */ + char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */ + enum tag_check_state tag_state; /* State of the tag processor */ +}; +#endif /* LWIP_HTTPD_SSI */ + +struct http_state { +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + struct http_state *next; +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + struct fs_file file_handle; + struct fs_file *handle; + char *file; /* Pointer to first unsent byte in buf. */ + + u8_t is_websocket; + + struct tcp_pcb *pcb; +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + struct pbuf *req; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + +#if LWIP_HTTPD_DYNAMIC_FILE_READ + char *buf; /* File read buffer. */ + int buf_len; /* Size of file read buffer, buf. */ +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ + u32_t left; /* Number of unsent bytes in buf. */ + u8_t retries; +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + u8_t keepalive; +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ +#if LWIP_HTTPD_SSI + struct http_ssi_state *ssi; +#endif /* LWIP_HTTPD_SSI */ +#if LWIP_HTTPD_CGI + char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */ + char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */ +#endif /* LWIP_HTTPD_CGI */ +#if LWIP_HTTPD_DYNAMIC_HEADERS + const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */ + u16_t hdr_pos; /* The position of the first unsent header byte in the + current string */ + u16_t hdr_index; /* The index of the hdr string currently being sent. */ +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ +#if LWIP_HTTPD_TIMING + u32_t time_started; +#endif /* LWIP_HTTPD_TIMING */ +#if LWIP_HTTPD_SUPPORT_POST + u32_t post_content_len_left; +#if LWIP_HTTPD_POST_MANUAL_WND + u32_t unrecved_bytes; + u8_t no_auto_wnd; + u8_t post_finished; +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ +#endif /* LWIP_HTTPD_SUPPORT_POST*/ +}; + +static err_t http_close_conn(struct tcp_pcb *pcb, struct http_state *hs); +static err_t http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn); +static err_t http_find_file(struct http_state *hs, const char *uri, int is_09); +static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check); +static err_t http_poll(void *arg, struct tcp_pcb *pcb); + +static err_t websocket_send_close(struct tcp_pcb *pcb); + +#if LWIP_HTTPD_FS_ASYNC_READ +static void http_continue(void *connection); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +#if LWIP_HTTPD_SSI +/* SSI insert handler function pointer. */ +tSSIHandler g_pfnSSIHandler = NULL; +int g_iNumTags = 0; +const char **g_ppcTags = NULL; + +#define LEN_TAG_LEAD_IN 5 +const char * const g_pcTagLeadIn = ""; +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_CGI +/* CGI handler information */ +const tCGI *g_pCGIs; +int g_iNumCGIs; +#endif /* LWIP_HTTPD_CGI */ + +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED +/** global list of active HTTP connections, use to kill the oldest when + running out of memory */ +static struct http_state *http_connections; +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + +#if LWIP_HTTPD_STRNSTR_PRIVATE +/** Like strstr but does not need 'buffer' to be NULL-terminated */ +static char* +strnstr(const char* buffer, const char* token, size_t n) +{ + const char* p; + int tokenlen = (int)strlen(token); + if (tokenlen == 0) { + return (char *)buffer; + } + for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) { + if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) { + return (char *)p; + } + } + return NULL; +} +#endif /* LWIP_HTTPD_STRNSTR_PRIVATE */ + +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED +static void +http_kill_oldest_connection(u8_t ssi_required) +{ + struct http_state *hs = http_connections; + struct http_state *hs_free_next = NULL; + while(hs && hs->next) { + if (ssi_required) { + if (hs->next->ssi != NULL) { + hs_free_next = hs; + } + } else { + hs_free_next = hs; + } + hs = hs->next; + } + if (hs_free_next != NULL) { + LWIP_ASSERT("hs_free_next->next != NULL", hs_free_next->next != NULL); + LWIP_ASSERT("hs_free_next->next->pcb != NULL", hs_free_next->next->pcb != NULL); + /* send RST when killing a connection because of memory shortage */ + http_close_or_abort_conn(hs_free_next->next->pcb, hs_free_next->next, 1); /* this also unlinks the http_state from the list */ + } +} +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + +#if LWIP_HTTPD_SSI +/** Allocate as struct http_ssi_state. */ +static struct http_ssi_state* +http_ssi_state_alloc(void) +{ + struct http_ssi_state *ret = HTTP_ALLOC_SSI_STATE(); +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + if (ret == NULL) { + http_kill_oldest_connection(1); + ret = HTTP_ALLOC_SSI_STATE(); + } +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + if (ret != NULL) { + memset(ret, 0, sizeof(struct http_ssi_state)); + } + return ret; +} + +/** Free a struct http_ssi_state. */ +static void +http_ssi_state_free(struct http_ssi_state *ssi) +{ + if (ssi != NULL) { +#if HTTPD_USE_MEM_POOL + memp_free(MEMP_HTTPD_SSI_STATE, ssi); +#else /* HTTPD_USE_MEM_POOL */ + mem_free(ssi); +#endif /* HTTPD_USE_MEM_POOL */ + } +} +#endif /* LWIP_HTTPD_SSI */ + +/** Initialize a struct http_state. + */ +static void +http_state_init(struct http_state* hs) +{ + /* Initialize the structure. */ + memset(hs, 0, sizeof(struct http_state)); +#if LWIP_HTTPD_DYNAMIC_HEADERS + /* Indicate that the headers are not yet valid */ + hs->hdr_index = NUM_FILE_HDR_STRINGS; +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ +} + +/** Allocate a struct http_state. */ +static struct http_state* +http_state_alloc(void) +{ + struct http_state *ret = HTTP_ALLOC_HTTP_STATE(); +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + if (ret == NULL) { + http_kill_oldest_connection(0); + ret = HTTP_ALLOC_HTTP_STATE(); + } +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + if (ret != NULL) { + http_state_init(ret); +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + /* add the connection to the list */ + if (http_connections == NULL) { + http_connections = ret; + } else { + struct http_state *last; + for(last = http_connections; last->next != NULL; last = last->next); + LWIP_ASSERT("last != NULL", last != NULL); + last->next = ret; + } +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ + } + return ret; +} + +/** Free a struct http_state. + * Also frees the file data if dynamic. + */ +static void +http_state_eof(struct http_state *hs) +{ + if(hs->handle) { +#if LWIP_HTTPD_TIMING + u32_t ms_needed = sys_now() - hs->time_started; + u32_t needed = LWIP_MAX(1, (ms_needed/100)); + LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n", + ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed))); +#endif /* LWIP_HTTPD_TIMING */ + fs_close(hs->handle); + hs->handle = NULL; + } +#if LWIP_HTTPD_DYNAMIC_FILE_READ + if (hs->buf != NULL) { + mem_free(hs->buf); + hs->buf = NULL; + } +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ +#if LWIP_HTTPD_SSI + if (hs->ssi) { + http_ssi_state_free(hs->ssi); + hs->ssi = NULL; + } +#endif /* LWIP_HTTPD_SSI */ +} + +/** Free a struct http_state. + * Also frees the file data if dynamic. + */ +static void +http_state_free(struct http_state *hs) +{ + if (hs != NULL) { + http_state_eof(hs); +#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED + /* take the connection off the list */ + if (http_connections) { + if (http_connections == hs) { + http_connections = hs->next; + } else { + struct http_state *last; + for(last = http_connections; last->next != NULL; last = last->next) { + if (last->next == hs) { + last->next = hs->next; + break; + } + } + } + } +#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ +#if HTTPD_USE_MEM_POOL + memp_free(MEMP_HTTPD_STATE, hs); +#else /* HTTPD_USE_MEM_POOL */ + mem_free(hs); +#endif /* HTTPD_USE_MEM_POOL */ + } +} + +/** Call tcp_write() in a loop trying smaller and smaller length + * + * @param pcb tcp_pcb to send + * @param ptr Data to send + * @param length Length of data to send (in/out: on return, contains the + * amount of data sent) + * @param apiflags directly passed to tcp_write + * @return the return value of tcp_write + */ +static err_t +http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags) +{ + u16_t len; + err_t err; + LWIP_ASSERT("length != NULL", length != NULL); + len = *length; + if (len == 0) { + return ERR_OK; + } + do { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying to send %d bytes\n", len)); + err = tcp_write(pcb, ptr, len, apiflags); + if (err == ERR_MEM) { + if ((tcp_sndbuf(pcb) == 0) || + (tcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) { + /* no need to try smaller sizes */ + len = 1; + } else { + len /= 2; + } + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, + ("Send failed, trying less (%d bytes)\n", len)); + } + } while ((err == ERR_MEM) && (len > 1)); + + if (err == ERR_OK) { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len)); + } else { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err))); + } + + *length = len; + return err; +} + +/** + * The connection shall be actively closed (using RST to close from fault states). + * Reset the sent- and recv-callbacks. + * + * @param pcb the tcp pcb to reset callbacks + * @param hs connection state to free + */ +static err_t +http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn) +{ + err_t err; + LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void*)pcb)); + +#if LWIP_HTTPD_SUPPORT_POST + if (hs != NULL) { + if ((hs->post_content_len_left != 0) +#if LWIP_HTTPD_POST_MANUAL_WND + || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0)) +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + ) { + /* make sure the post code knows that the connection is closed */ + http_post_response_filename[0] = 0; + httpd_post_finished(hs, http_post_response_filename, LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN); + } + } +#endif /* LWIP_HTTPD_SUPPORT_POST*/ + + if (hs != NULL) { + if (hs->is_websocket) + websocket_send_close(pcb); + + if (hs->req != NULL) { + /* this should not happen */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Freeing buffer (malformed request?)\n")); + pbuf_free(hs->req); + hs->req = NULL; + } + } + + tcp_arg(pcb, NULL); + tcp_recv(pcb, NULL); + tcp_err(pcb, NULL); + tcp_poll(pcb, NULL, 0); + tcp_sent(pcb, NULL); + if (hs != NULL) { + http_state_free(hs); + } + + if (abort_conn) { + tcp_abort(pcb); + return ERR_OK; + } + err = tcp_close(pcb); + if (err != ERR_OK) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void*)pcb)); + /* error closing, try again later in poll */ + tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL); + } + return err; +} + +/** + * The connection shall be actively closed. + * Reset the sent- and recv-callbacks. + * + * @param pcb the tcp pcb to reset callbacks + * @param hs connection state to free + */ +static err_t +http_close_conn(struct tcp_pcb *pcb, struct http_state *hs) +{ + return http_close_or_abort_conn(pcb, hs, 0); +} + +/** End of file: either close the connection (Connection: close) or + * close the file (Connection: keep-alive) + */ +static void +http_eof(struct tcp_pcb *pcb, struct http_state *hs) +{ + /* HTTP/1.1 persistent connection? (Not supported for SSI) */ +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (hs->keepalive && !LWIP_HTTPD_IS_SSI(hs)) { + http_state_eof(hs); + http_state_init(hs); + hs->keepalive = 1; + } else +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + if (hs->is_websocket) { + http_state_eof(hs); + http_state_init(hs); + hs->is_websocket = 1; + } else { + http_close_conn(pcb, hs); + } +} + +#if LWIP_HTTPD_CGI +/** + * Extract URI parameters from the parameter-part of an URI in the form + * "test.cgi?x=y" @todo: better explanation! + * Pointers to the parameters are stored in hs->param_vals. + * + * @param hs http connection state + * @param params pointer to the NULL-terminated parameter string from the URI + * @return number of parameters extracted + */ +static int +extract_uri_parameters(struct http_state *hs, char *params) +{ + char *pair; + char *equals; + int loop; + + /* If we have no parameters at all, return immediately. */ + if(!params || (params[0] == '\0')) { + return(0); + } + + /* Get a pointer to our first parameter */ + pair = params; + + /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the + * remainder (if any) */ + for(loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) { + + /* Save the name of the parameter */ + hs->params[loop] = pair; + + /* Remember the start of this name=value pair */ + equals = pair; + + /* Find the start of the next name=value pair and replace the delimiter + * with a 0 to terminate the previous pair string. */ + pair = strchr(pair, '&'); + if(pair) { + *pair = '\0'; + pair++; + } else { + /* We didn't find a new parameter so find the end of the URI and + * replace the space with a '\0' */ + pair = strchr(equals, ' '); + if(pair) { + *pair = '\0'; + } + + /* Revert to NULL so that we exit the loop as expected. */ + pair = NULL; + } + + /* Now find the '=' in the previous pair, replace it with '\0' and save + * the parameter value string. */ + equals = strchr(equals, '='); + if(equals) { + *equals = '\0'; + hs->param_vals[loop] = equals + 1; + } else { + hs->param_vals[loop] = NULL; + } + } + + return loop; +} +#endif /* LWIP_HTTPD_CGI */ + +#if LWIP_HTTPD_SSI +/** + * Insert a tag (found in an shtml in the form of "" into the file. + * The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement + * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN). + * The amount of data written is stored to ssi->tag_insert_len. + * + * @todo: return tag_insert_len - maybe it can be removed from struct http_state? + * + * @param hs http connection state + */ +static void +get_tag_insert(struct http_state *hs) +{ + int loop; + size_t len; + struct http_ssi_state *ssi; + LWIP_ASSERT("hs != NULL", hs != NULL); + ssi = hs->ssi; + LWIP_ASSERT("ssi != NULL", ssi != NULL); +#if LWIP_HTTPD_SSI_MULTIPART + u16_t current_tag_part = ssi->tag_part; + ssi->tag_part = HTTPD_LAST_TAG_PART; +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + + if(g_pfnSSIHandler && g_ppcTags && g_iNumTags) { + + /* Find this tag in the list we have been provided. */ + for(loop = 0; loop < g_iNumTags; loop++) { + if(strcmp(ssi->tag_name, g_ppcTags[loop]) == 0) { + ssi->tag_insert_len = g_pfnSSIHandler(loop, ssi->tag_insert, + LWIP_HTTPD_MAX_TAG_INSERT_LEN +#if LWIP_HTTPD_SSI_MULTIPART + , current_tag_part, &ssi->tag_part +#endif /* LWIP_HTTPD_SSI_MULTIPART */ +#if LWIP_HTTPD_FILE_STATE + , hs->handle->state +#endif /* LWIP_HTTPD_FILE_STATE */ + ); + return; + } + } + } + + /* If we drop out, we were asked to serve a page which contains tags that + * we don't have a handler for. Merely echo back the tags with an error + * marker. */ +#define UNKNOWN_TAG1_TEXT "***UNKNOWN TAG " +#define UNKNOWN_TAG1_LEN 18 +#define UNKNOWN_TAG2_TEXT "***" +#define UNKNOWN_TAG2_LEN 7 + len = LWIP_MIN(strlen(ssi->tag_name), + LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN)); + MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN); + MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len); + MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN); + ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0; + + len = strlen(ssi->tag_insert); + LWIP_ASSERT("len <= 0xffff", len <= 0xffff); + ssi->tag_insert_len = (u16_t)len; +} +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_DYNAMIC_HEADERS +/** + * Generate the relevant HTTP headers for the given filename and write + * them into the supplied buffer. + */ +static void +get_http_headers(struct http_state *pState, char *pszURI) +{ + unsigned int iLoop; + char *pszWork; + char *pszExt; + char *pszVars; + + /* Ensure that we initialize the loop counter. */ + iLoop = 0; + + /* In all cases, the second header we send is the server identification + so set it here. */ + pState->hdrs[1] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER]; + + /* Is this a normal file or the special case we use to send back the + default "404: Page not found" response? */ + if (pszURI == NULL) { + pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND]; + pState->hdrs[2] = g_psHTTPHeaderStrings[DEFAULT_404_HTML]; + + /* Set up to send the first header string. */ + pState->hdr_index = 0; + pState->hdr_pos = 0; + return; + } else { + /* We are dealing with a particular filename. Look for one other + special case. We assume that any filename with "404" in it must be + indicative of a 404 server error whereas all other files require + the 200 OK header. */ + if (strstr(pszURI, "404")) { + pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND]; + } else if (strstr(pszURI, "400")) { + pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST]; + } else if (strstr(pszURI, "501")) { + pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL]; + } else { + pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_OK]; + } + + /* Determine if the URI has any variables and, if so, temporarily remove + them. */ + pszVars = strchr(pszURI, '?'); + if(pszVars) { + *pszVars = '\0'; + } + + /* Get a pointer to the file extension. We find this by looking for the + last occurrence of "." in the filename passed. */ + pszExt = NULL; + pszWork = strchr(pszURI, '.'); + while(pszWork) { + pszExt = pszWork + 1; + pszWork = strchr(pszExt, '.'); + } + + /* Now determine the content type and add the relevant header for that. */ + for(iLoop = 0; (iLoop < NUM_HTTP_HEADERS) && pszExt; iLoop++) { + /* Have we found a matching extension? */ + if(!strcmp(g_psHTTPHeaders[iLoop].extension, pszExt)) { + pState->hdrs[2] = + g_psHTTPHeaderStrings[g_psHTTPHeaders[iLoop].headerIndex]; + break; + } + } + + /* Reinstate the parameter marker if there was one in the original URI. */ + if(pszVars) { + *pszVars = '?'; + } + } + + /* Does the URL passed have any file extension? If not, we assume it + is a special-case URL used for control state notification and we do + not send any HTTP headers with the response. */ + if(!pszExt) { + /* Force the header index to a value indicating that all headers + have already been sent. */ + pState->hdr_index = NUM_FILE_HDR_STRINGS; + } else { + /* Did we find a matching extension? */ + if(iLoop == NUM_HTTP_HEADERS) { + /* No - use the default, plain text file type. */ + pState->hdrs[2] = g_psHTTPHeaderStrings[HTTP_HDR_DEFAULT_TYPE]; + } + + /* Set up to send the first header string. */ + pState->hdr_index = 0; + pState->hdr_pos = 0; + } +} + +/** Sub-function of http_send(): send dynamic headers + * + * @returns: - HTTP_NO_DATA_TO_SEND: no new data has been enqueued + * - HTTP_DATA_TO_SEND_CONTINUE: continue with sending HTTP body + * - HTTP_DATA_TO_SEND_BREAK: data has been enqueued, headers pending, + * so don't send HTTP body yet + */ +static u8_t +http_send_headers(struct tcp_pcb *pcb, struct http_state *hs) +{ + err_t err; + u16_t len; + u8_t data_to_send = HTTP_NO_DATA_TO_SEND; + u16_t hdrlen, sendlen; + + /* How much data can we send? */ + len = tcp_sndbuf(pcb); + sendlen = len; + + while(len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) { + const void *ptr; + u16_t old_sendlen; + /* How much do we have to send from the current header? */ + hdrlen = (u16_t)strlen(hs->hdrs[hs->hdr_index]); + + /* How much of this can we send? */ + sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos); + + /* Send this amount of data or as much as we can given memory + * constraints. */ + ptr = (const void *)(hs->hdrs[hs->hdr_index] + hs->hdr_pos); + old_sendlen = sendlen; + err = http_write(pcb, ptr, &sendlen, HTTP_IS_HDR_VOLATILE(hs, ptr)); + if ((err == ERR_OK) && (old_sendlen != sendlen)) { + /* Remember that we added some more data to be transmitted. */ + data_to_send = HTTP_DATA_TO_SEND_CONTINUE; + } else if (err != ERR_OK) { + /* special case: http_write does not try to send 1 byte */ + sendlen = 0; + } + + /* Fix up the header position for the next time round. */ + hs->hdr_pos += sendlen; + len -= sendlen; + + /* Have we finished sending this string? */ + if(hs->hdr_pos == hdrlen) { + /* Yes - move on to the next one */ + hs->hdr_index++; + hs->hdr_pos = 0; + } + } + /* If we get here and there are still header bytes to send, we send + * the header information we just wrote immediately. If there are no + * more headers to send, but we do have file data to send, drop through + * to try to send some file data too. */ + if((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) { + LWIP_DEBUGF(HTTPD_DEBUG, ("tcp_output\n")); + return HTTP_DATA_TO_SEND_BREAK; + } + return data_to_send; +} +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + +/** Sub-function of http_send(): end-of-file (or block) is reached, + * either close the file or read the next block (if supported). + * + * @returns: 0 if the file is finished or no data has been read + * 1 if the file is not finished and data has been read + */ +static u8_t +http_check_eof(struct tcp_pcb *pcb, struct http_state *hs) +{ +#if LWIP_HTTPD_DYNAMIC_FILE_READ + int count; +#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ + + /* Do we have a valid file handle? */ + if (hs->handle == NULL) { + /* No - close the connection. */ + http_eof(pcb, hs); + return 0; + } + if (fs_bytes_left(hs->handle) <= 0) { + /* We reached the end of the file so this request is done. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); + http_eof(pcb, hs); + return 0; + } +#if LWIP_HTTPD_DYNAMIC_FILE_READ + /* Do we already have a send buffer allocated? */ + if(hs->buf) { + /* Yes - get the length of the buffer */ + count = hs->buf_len; + } else { + /* We don't have a send buffer so allocate one up to 2mss bytes long. */ + count = 2 * tcp_mss(pcb); + do { + hs->buf = (char*)mem_malloc((mem_size_t)count); + if (hs->buf != NULL) { + hs->buf_len = count; + break; + } + count = count / 2; + } while (count > 100); + + /* Did we get a send buffer? If not, return immediately. */ + if (hs->buf == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("No buff\n")); + return 0; + } + } + + /* Read a block of data from the file. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Trying to read %d bytes.\n", count)); + +#if LWIP_HTTPD_FS_ASYNC_READ + count = fs_read_async(hs->handle, hs->buf, count, http_continue, hs); +#else /* LWIP_HTTPD_FS_ASYNC_READ */ + count = fs_read(hs->handle, hs->buf, count); +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + if (count < 0) { + if (count == FS_READ_DELAYED) { + /* Delayed read, wait for FS to unblock us */ + return 0; + } + /* We reached the end of the file so this request is done. + * @todo: don't close here for HTTP/1.1? */ + LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); + http_eof(pcb, hs); + return 0; + } + + /* Set up to send the block of data we just read */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Read %d bytes.\n", count)); + hs->left = count; + hs->file = hs->buf; +#if LWIP_HTTPD_SSI + if (hs->ssi) { + hs->ssi->parse_left = count; + hs->ssi->parsed = hs->buf; + } +#endif /* LWIP_HTTPD_SSI */ +#else /* LWIP_HTTPD_DYNAMIC_FILE_READ */ + LWIP_ASSERT("SSI and DYNAMIC_HEADERS turned off but eof not reached", 0); +#endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */ + return 1; +} + +/** Sub-function of http_send(): This is the normal send-routine for non-ssi files + * + * @returns: - 1: data has been written (so call tcp_ouput) + * - 0: no data has been written (no need to call tcp_output) + */ +static u8_t +http_send_data_nonssi(struct tcp_pcb *pcb, struct http_state *hs) +{ + err_t err; + u16_t len; + u16_t mss; + u8_t data_to_send = 0; + + /* We are not processing an SHTML file so no tag checking is necessary. + * Just send the data as we received it from the file. */ + + /* We cannot send more data than space available in the send + buffer. */ + if (tcp_sndbuf(pcb) < hs->left) { + len = tcp_sndbuf(pcb); + } else { + len = (u16_t)hs->left; + LWIP_ASSERT("hs->left did not fit into u16_t!", (len == hs->left)); + } + mss = tcp_mss(pcb); + if (len > (2 * mss)) { + len = 2 * mss; + } + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + hs->file += len; + hs->left -= len; + } + + return data_to_send; +} + +#if LWIP_HTTPD_SSI +/** Sub-function of http_send(): This is the send-routine for ssi files + * + * @returns: - 1: data has been written (so call tcp_ouput) + * - 0: no data has been written (no need to call tcp_output) + */ +static u8_t +http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs) +{ + err_t err = ERR_OK; + u16_t len; + u16_t mss; + u8_t data_to_send = 0; + + struct http_ssi_state *ssi = hs->ssi; + LWIP_ASSERT("ssi != NULL", ssi != NULL); + /* We are processing an SHTML file so need to scan for tags and replace + * them with insert strings. We need to be careful here since a tag may + * straddle the boundary of two blocks read from the file and we may also + * have to split the insert string between two tcp_write operations. */ + + /* Do we have remaining data to send before parsing more? */ + if(ssi->parsed > hs->file) { + /* We cannot send more data than space available in the send + buffer. */ + if (tcp_sndbuf(pcb) < (ssi->parsed - hs->file)) { + len = tcp_sndbuf(pcb); + } else { + LWIP_ASSERT("Data size does not fit into u16_t!", + (ssi->parsed - hs->file) <= 0xffff); + len = (u16_t)(ssi->parsed - hs->file); + } + mss = tcp_mss(pcb); + if(len > (2 * mss)) { + len = 2 * mss; + } + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + hs->file += len; + hs->left -= len; + } + + /* If the send buffer is full, return now. */ + if(tcp_sndbuf(pcb) == 0) { + return data_to_send; + } + } + + LWIP_DEBUGF(HTTPD_DEBUG, ("State %d, %d left\n", ssi->tag_state, (int)ssi->parse_left)); + + /* We have sent all the data that was already parsed so continue parsing + * the buffer contents looking for SSI tags. */ + while((ssi->parse_left) && (err == ERR_OK)) { + /* How much data could we send? */ + len = tcp_sndbuf(pcb); + if (len == 0) { + return data_to_send; + } + switch(ssi->tag_state) { + case TAG_NONE: + /* We are not currently processing an SSI tag so scan for the + * start of the lead-in marker. */ + if(*ssi->parsed == g_pcTagLeadIn[0]) { + /* We found what could be the lead-in for a new tag so change + * state appropriately. */ + ssi->tag_state = TAG_LEADIN; + ssi->tag_index = 1; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + ssi->tag_started = ssi->parsed; +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */ + } + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + break; + + case TAG_LEADIN: + /* We are processing the lead-in marker, looking for the start of + * the tag name. */ + + /* Have we reached the end of the leadin? */ + if(ssi->tag_index == LEN_TAG_LEAD_IN) { + ssi->tag_index = 0; + ssi->tag_state = TAG_FOUND; + } else { + /* Have we found the next character we expect for the tag leadin? */ + if(*ssi->parsed == g_pcTagLeadIn[ssi->tag_index]) { + /* Yes - move to the next one unless we have found the complete + * leadin, in which case we start looking for the tag itself */ + ssi->tag_index++; + } else { + /* We found an unexpected character so this is not a tag. Move + * back to idle state. */ + ssi->tag_state = TAG_NONE; + } + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + } + break; + + case TAG_FOUND: + /* We are reading the tag name, looking for the start of the + * lead-out marker and removing any whitespace found. */ + + /* Remove leading whitespace between the tag leading and the first + * tag name character. */ + if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') || + (*ssi->parsed == '\t') || (*ssi->parsed == '\n') || + (*ssi->parsed == '\r'))) { + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + break; + } + + /* Have we found the end of the tag name? This is signalled by + * us finding the first leadout character or whitespace */ + if((*ssi->parsed == g_pcTagLeadOut[0]) || + (*ssi->parsed == ' ') || (*ssi->parsed == '\t') || + (*ssi->parsed == '\n') || (*ssi->parsed == '\r')) { + + if(ssi->tag_index == 0) { + /* We read a zero length tag so ignore it. */ + ssi->tag_state = TAG_NONE; + } else { + /* We read a non-empty tag so go ahead and look for the + * leadout string. */ + ssi->tag_state = TAG_LEADOUT; + LWIP_ASSERT("ssi->tag_index <= 0xff", ssi->tag_index <= 0xff); + ssi->tag_name_len = (u8_t)ssi->tag_index; + ssi->tag_name[ssi->tag_index] = '\0'; + if(*ssi->parsed == g_pcTagLeadOut[0]) { + ssi->tag_index = 1; + } else { + ssi->tag_index = 0; + } + } + } else { + /* This character is part of the tag name so save it */ + if(ssi->tag_index < LWIP_HTTPD_MAX_TAG_NAME_LEN) { + ssi->tag_name[ssi->tag_index++] = *ssi->parsed; + } else { + /* The tag was too long so ignore it. */ + ssi->tag_state = TAG_NONE; + } + } + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + + break; + + /* We are looking for the end of the lead-out marker. */ + case TAG_LEADOUT: + /* Remove leading whitespace between the tag leading and the first + * tag leadout character. */ + if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') || + (*ssi->parsed == '\t') || (*ssi->parsed == '\n') || + (*ssi->parsed == '\r'))) { + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + break; + } + + /* Have we found the next character we expect for the tag leadout? */ + if(*ssi->parsed == g_pcTagLeadOut[ssi->tag_index]) { + /* Yes - move to the next one unless we have found the complete + * leadout, in which case we need to call the client to process + * the tag. */ + + /* Move on to the next character in the buffer */ + ssi->parse_left--; + ssi->parsed++; + + if(ssi->tag_index == (LEN_TAG_LEAD_OUT - 1)) { + /* Call the client to ask for the insert string for the + * tag we just found. */ +#if LWIP_HTTPD_SSI_MULTIPART + ssi->tag_part = 0; /* start with tag part 0 */ +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + get_tag_insert(hs); + + /* Next time through, we are going to be sending data + * immediately, either the end of the block we start + * sending here or the insert string. */ + ssi->tag_index = 0; + ssi->tag_state = TAG_SENDING; + ssi->tag_end = ssi->parsed; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + ssi->parsed = ssi->tag_started; +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + + /* If there is any unsent data in the buffer prior to the + * tag, we need to send it now. */ + if (ssi->tag_end > hs->file) { + /* How much of the data can we send? */ +#if LWIP_HTTPD_SSI_INCLUDE_TAG + if(len > ssi->tag_end - hs->file) { + len = (u16_t)(ssi->tag_end - hs->file); + } +#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + if(len > ssi->tag_started - hs->file) { + /* we would include the tag in sending */ + len = (u16_t)(ssi->tag_started - hs->file); + } +#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + if(ssi->tag_started <= hs->file) { + /* pretend to have sent the tag, too */ + len += ssi->tag_end - ssi->tag_started; + } +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + hs->file += len; + hs->left -= len; + } + } + } else { + ssi->tag_index++; + } + } else { + /* We found an unexpected character so this is not a tag. Move + * back to idle state. */ + ssi->parse_left--; + ssi->parsed++; + ssi->tag_state = TAG_NONE; + } + break; + + /* + * We have found a valid tag and are in the process of sending + * data as a result of that discovery. We send either remaining data + * from the file prior to the insert point or the insert string itself. + */ + case TAG_SENDING: + /* Do we have any remaining file data to send from the buffer prior + * to the tag? */ + if(ssi->tag_end > hs->file) { + /* How much of the data can we send? */ +#if LWIP_HTTPD_SSI_INCLUDE_TAG + if(len > ssi->tag_end - hs->file) { + len = (u16_t)(ssi->tag_end - hs->file); + } +#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + LWIP_ASSERT("hs->started >= hs->file", ssi->tag_started >= hs->file); + if (len > ssi->tag_started - hs->file) { + /* we would include the tag in sending */ + len = (u16_t)(ssi->tag_started - hs->file); + } +#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ + if (len != 0) { + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + } else { + err = ERR_OK; + } + if (err == ERR_OK) { + data_to_send = 1; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + if(ssi->tag_started <= hs->file) { + /* pretend to have sent the tag, too */ + len += ssi->tag_end - ssi->tag_started; + } +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + hs->file += len; + hs->left -= len; + } + } else { +#if LWIP_HTTPD_SSI_MULTIPART + if(ssi->tag_index >= ssi->tag_insert_len) { + /* Did the last SSIHandler have more to send? */ + if (ssi->tag_part != HTTPD_LAST_TAG_PART) { + /* If so, call it again */ + ssi->tag_index = 0; + get_tag_insert(hs); + } + } +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + + /* Do we still have insert data left to send? */ + if(ssi->tag_index < ssi->tag_insert_len) { + /* We are sending the insert string itself. How much of the + * insert can we send? */ + if(len > (ssi->tag_insert_len - ssi->tag_index)) { + len = (ssi->tag_insert_len - ssi->tag_index); + } + + /* Note that we set the copy flag here since we only have a + * single tag insert buffer per connection. If we don't do + * this, insert corruption can occur if more than one insert + * is processed before we call tcp_output. */ + err = http_write(pcb, &(ssi->tag_insert[ssi->tag_index]), &len, + HTTP_IS_TAG_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + ssi->tag_index += len; + /* Don't return here: keep on sending data */ + } + } else { +#if LWIP_HTTPD_SSI_MULTIPART + if (ssi->tag_part == HTTPD_LAST_TAG_PART) +#endif /* LWIP_HTTPD_SSI_MULTIPART */ + { + /* We have sent all the insert data so go back to looking for + * a new tag. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Everything sent.\n")); + ssi->tag_index = 0; + ssi->tag_state = TAG_NONE; +#if !LWIP_HTTPD_SSI_INCLUDE_TAG + ssi->parsed = ssi->tag_end; +#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ + } + } + break; + } + } + } + + /* If we drop out of the end of the for loop, this implies we must have + * file data to send so send it now. In TAG_SENDING state, we've already + * handled this so skip the send if that's the case. */ + if((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) { + /* We cannot send more data than space available in the send + buffer. */ + if (tcp_sndbuf(pcb) < (ssi->parsed - hs->file)) { + len = tcp_sndbuf(pcb); + } else { + LWIP_ASSERT("Data size does not fit into u16_t!", + (ssi->parsed - hs->file) <= 0xffff); + len = (u16_t)(ssi->parsed - hs->file); + } + if(len > (2 * tcp_mss(pcb))) { + len = 2 * tcp_mss(pcb); + } + + err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); + if (err == ERR_OK) { + data_to_send = 1; + hs->file += len; + hs->left -= len; + } + } + return data_to_send; +} +#endif /* LWIP_HTTPD_SSI */ + +/** + * Try to send more data on this pcb. + * + * @param pcb the pcb to send data + * @param hs connection state + */ +static u8_t +http_send(struct tcp_pcb *pcb, struct http_state *hs) +{ + u8_t data_to_send = HTTP_NO_DATA_TO_SEND; + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_send: pcb=%p hs=%p left=%d\n", (void*)pcb, + (void*)hs, hs != NULL ? (int)hs->left : 0)); + +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + if (hs->unrecved_bytes != 0) { + return 0; + } +#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ + + /* If we were passed a NULL state structure pointer, ignore the call. */ + if (hs == NULL) { + return 0; + } + +#if LWIP_HTTPD_FS_ASYNC_READ + /* Check if we are allowed to read from this file. + (e.g. SSI might want to delay sending until data is available) */ + if (!fs_is_file_ready(hs->handle, http_continue, hs)) { + return 0; + } +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +#if LWIP_HTTPD_DYNAMIC_HEADERS + /* Do we have any more header data to send for this file? */ + if(hs->hdr_index < NUM_FILE_HDR_STRINGS) { + data_to_send = http_send_headers(pcb, hs); + if (data_to_send != HTTP_DATA_TO_SEND_CONTINUE) { + return data_to_send; + } + } +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + + /* Have we run out of file data to send? If so, we need to read the next + * block from the file. */ + if (hs->left == 0) { + if (!http_check_eof(pcb, hs)) { + return 0; + } + } + +#if LWIP_HTTPD_SSI + if(hs->ssi) { + data_to_send = http_send_data_ssi(pcb, hs); + } else +#endif /* LWIP_HTTPD_SSI */ + { + data_to_send = http_send_data_nonssi(pcb, hs); + } + + if((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0)) { + /* We reached the end of the file so this request is done. + * This adds the FIN flag right into the last data segment. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); + http_eof(pcb, hs); + return 0; + } + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("send_data end.\n")); + return data_to_send; +} + +#if LWIP_HTTPD_SUPPORT_EXTSTATUS +/** Initialize a http connection with a file to send for an error message + * + * @param hs http connection state + * @param error_nr HTTP error number + * @return ERR_OK if file was found and hs has been initialized correctly + * another err_t otherwise + */ +static err_t +http_find_error_file(struct http_state *hs, u16_t error_nr) +{ + const char *uri1, *uri2, *uri3; + err_t err; + + if (error_nr == 501) { + uri1 = "/501.html"; + uri2 = "/501.htm"; + uri3 = "/501.shtml"; + } else { + /* 400 (bad request is the default) */ + uri1 = "/400.html"; + uri2 = "/400.htm"; + uri3 = "/400.shtml"; + } + err = fs_open(&hs->file_handle, uri1); + if (err != ERR_OK) { + err = fs_open(&hs->file_handle, uri2); + if (err != ERR_OK) { + err = fs_open(&hs->file_handle, uri3); + if (err != ERR_OK) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Error page for error %"U16_F" not found\n", + error_nr)); + return ERR_ARG; + } + } + } + return http_init_file(hs, &hs->file_handle, 0, NULL, 0); +} +#else /* LWIP_HTTPD_SUPPORT_EXTSTATUS */ +#define http_find_error_file(hs, error_nr) ERR_ARG +#endif /* LWIP_HTTPD_SUPPORT_EXTSTATUS */ + +/** + * Get the file struct for a 404 error page. + * Tries some file names and returns NULL if none found. + * + * @param uri pointer that receives the actual file name URI + * @return file struct for the error page or NULL no matching file was found + */ +static struct fs_file * +http_get_404_file(struct http_state *hs, const char **uri) +{ + err_t err; + + *uri = "/404.html"; + err = fs_open(&hs->file_handle, *uri); + if (err != ERR_OK) { + /* 404.html doesn't exist. Try 404.htm instead. */ + *uri = "/404.htm"; + err = fs_open(&hs->file_handle, *uri); + if (err != ERR_OK) { + /* 404.htm doesn't exist either. Try 404.shtml instead. */ + *uri = "/404.shtml"; + err = fs_open(&hs->file_handle, *uri); + if (err != ERR_OK) { + /* 404.htm doesn't exist either. Indicate to the caller that it should + * send back a default 404 page. + */ + *uri = NULL; + return NULL; + } + } + } + + return &hs->file_handle; +} + +#if LWIP_HTTPD_SUPPORT_POST +static err_t +http_handle_post_finished(struct http_state *hs) +{ +#if LWIP_HTTPD_POST_MANUAL_WND + /* Prevent multiple calls to httpd_post_finished, since it might have already + been called before from httpd_post_data_recved(). */ + if (hs->post_finished) { + return ERR_OK; + } + hs->post_finished = 1; +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + /* application error or POST finished */ + /* NULL-terminate the buffer */ + http_post_response_filename[0] = 0; + httpd_post_finished(hs, http_post_response_filename, LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN); + return http_find_file(hs, http_post_response_filename, 0); +} + +/** Pass received POST body data to the application and correctly handle + * returning a response document or closing the connection. + * ATTENTION: The application is responsible for the pbuf now, so don't free it! + * + * @param hs http connection state + * @param p pbuf to pass to the application + * @return ERR_OK if passed successfully, another err_t if the response file + * hasn't been found (after POST finished) + */ +static err_t +http_post_rxpbuf(struct http_state *hs, struct pbuf *p) +{ + err_t err; + + /* adjust remaining Content-Length */ + if (hs->post_content_len_left < p->tot_len) { + hs->post_content_len_left = 0; + } else { + hs->post_content_len_left -= p->tot_len; + } + err = httpd_post_receive_data(hs, p); + if ((err != ERR_OK) || (hs->post_content_len_left == 0)) { +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + if (hs->unrecved_bytes != 0) { + return ERR_OK; + } +#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ + /* application error or POST finished */ + return http_handle_post_finished(hs); + } + + return ERR_OK; +} + +/** Handle a post request. Called from http_parse_request when method 'POST' + * is found. + * + * @param p The input pbuf (containing the POST header and body). + * @param hs The http connection state. + * @param data HTTP request (header and part of body) from input pbuf(s). + * @param data_len Size of 'data'. + * @param uri The HTTP URI parsed from input pbuf(s). + * @param uri_end Pointer to the end of 'uri' (here, the rest of the HTTP + * header starts). + * @return ERR_OK: POST correctly parsed and accepted by the application. + * ERR_INPROGRESS: POST not completely parsed (no error yet) + * another err_t: Error parsing POST or denied by the application + */ +static err_t +http_post_request(struct pbuf **inp, struct http_state *hs, + char *data, u16_t data_len, char *uri, char *uri_end) +{ + err_t err; + /* search for end-of-header (first double-CRLF) */ + char* crlfcrlf = strnstr(uri_end + 1, CRLF CRLF, data_len - (uri_end + 1 - data)); + + if (crlfcrlf != NULL) { + /* search for "Content-Length: " */ +#define HTTP_HDR_CONTENT_LEN "Content-Length: " +#define HTTP_HDR_CONTENT_LEN_LEN 16 +#define HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN 10 + char *scontent_len = strnstr(uri_end + 1, HTTP_HDR_CONTENT_LEN, crlfcrlf - (uri_end + 1)); + if (scontent_len != NULL) { + char *scontent_len_end = strnstr(scontent_len + HTTP_HDR_CONTENT_LEN_LEN, CRLF, HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN); + if (scontent_len_end != NULL) { + int content_len; + char *conten_len_num = scontent_len + HTTP_HDR_CONTENT_LEN_LEN; + *scontent_len_end = 0; + content_len = atoi(conten_len_num); + if (content_len > 0) { + /* adjust length of HTTP header passed to application */ + const char *hdr_start_after_uri = uri_end + 1; + u16_t hdr_len = LWIP_MIN(data_len, crlfcrlf + 4 - data); + u16_t hdr_data_len = LWIP_MIN(data_len, crlfcrlf + 4 - hdr_start_after_uri); + u8_t post_auto_wnd = 1; + http_post_response_filename[0] = 0; + err = httpd_post_begin(hs, uri, hdr_start_after_uri, hdr_data_len, content_len, + http_post_response_filename, LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN, &post_auto_wnd); + if (err == ERR_OK) { + /* try to pass in data of the first pbuf(s) */ + struct pbuf *q = *inp; + u16_t start_offset = hdr_len; +#if LWIP_HTTPD_POST_MANUAL_WND + hs->no_auto_wnd = !post_auto_wnd; +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + /* set the Content-Length to be received for this POST */ + hs->post_content_len_left = (u32_t)content_len; + + /* get to the pbuf where the body starts */ + while((q != NULL) && (q->len <= start_offset)) { + struct pbuf *head = q; + start_offset -= q->len; + q = q->next; + /* free the head pbuf */ + head->next = NULL; + pbuf_free(head); + } + *inp = NULL; + if (q != NULL) { + /* hide the remaining HTTP header */ + pbuf_header(q, -(s16_t)start_offset); +#if LWIP_HTTPD_POST_MANUAL_WND + if (!post_auto_wnd) { + /* already tcp_recved() this data... */ + hs->unrecved_bytes = q->tot_len; + } +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + return http_post_rxpbuf(hs, q); + } else { + return ERR_OK; + } + } else { + /* return file passed from application */ + return http_find_file(hs, http_post_response_filename, 0); + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n", + conten_len_num)); + return ERR_ARG; + } + } + } + } + /* if we come here, the POST is incomplete */ +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + return ERR_INPROGRESS; +#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + return ERR_ARG; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ +} + +#if LWIP_HTTPD_POST_MANUAL_WND +/** A POST implementation can call this function to update the TCP window. + * This can be used to throttle data reception (e.g. when received data is + * programmed to flash and data is received faster than programmed). + * + * @param connection A connection handle passed to httpd_post_begin for which + * httpd_post_finished has *NOT* been called yet! + * @param recved_len Length of data received (for window update) + */ +void httpd_post_data_recved(void *connection, u16_t recved_len) +{ + struct http_state *hs = (struct http_state*)connection; + if (hs != NULL) { + if (hs->no_auto_wnd) { + u16_t len = recved_len; + if (hs->unrecved_bytes >= recved_len) { + hs->unrecved_bytes -= recved_len; + } else { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("httpd_post_data_recved: recved_len too big\n")); + len = (u16_t)hs->unrecved_bytes; + hs->unrecved_bytes = 0; + } + if (hs->pcb != NULL) { + if (len != 0) { + tcp_recved(hs->pcb, len); + } + if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) { + /* finished handling POST */ + http_handle_post_finished(hs); + http_send(hs->pcb, hs); + } + } + } + } +} +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + +#endif /* LWIP_HTTPD_SUPPORT_POST */ + +#if LWIP_HTTPD_FS_ASYNC_READ +/** Try to send more data if file has been blocked before + * This is a callback function passed to fs_read_async(). + */ +static void +http_continue(void *connection) +{ + struct http_state *hs = (struct http_state*)connection; + if (hs && (hs->pcb) && (hs->handle)) { + LWIP_ASSERT("hs->pcb != NULL", hs->pcb != NULL); + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("httpd_continue: try to send more data\n")); + if (http_send(hs->pcb, hs)) { + /* If we wrote anything to be sent, go ahead and send it now. */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n")); + tcp_output(hs->pcb); + } + } +} +#endif /* LWIP_HTTPD_FS_ASYNC_READ */ + +/** + * When data has been received in the correct state, try to parse it + * as a HTTP request. + * + * @param p the received pbuf + * @param hs the connection state + * @param pcb the tcp_pcb which received this packet + * @return ERR_OK if request was OK and hs has been initialized correctly + * ERR_INPROGRESS if request was OK so far but not fully received + * another err_t otherwise + */ +static err_t +http_parse_request(struct pbuf **inp, struct http_state *hs, struct tcp_pcb *pcb) +{ + char *data; + char *crlf; + u16_t data_len; + struct pbuf *p = *inp; +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + u16_t clen; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ +#if LWIP_HTTPD_SUPPORT_POST + err_t err; +#endif /* LWIP_HTTPD_SUPPORT_POST */ + + LWIP_UNUSED_ARG(pcb); /* only used for post */ + LWIP_ASSERT("p != NULL", p != NULL); + LWIP_ASSERT("hs != NULL", hs != NULL); + + if ((hs->handle != NULL) || (hs->file != NULL)) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Received data while sending a file\n")); + /* already sending a file */ + /* @todo: abort? */ + return ERR_USE; + } + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + + LWIP_DEBUGF(HTTPD_DEBUG, ("Received %"U16_F" bytes\n", p->tot_len)); + + /* first check allowed characters in this pbuf? */ + + /* enqueue the pbuf */ + if (hs->req == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("First pbuf\n")); + hs->req = p; + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("pbuf enqueued\n")); + pbuf_cat(hs->req, p); + } + + if (hs->req->next != NULL) { + data_len = LWIP_MIN(hs->req->tot_len, LWIP_HTTPD_MAX_REQ_LENGTH); + pbuf_copy_partial(hs->req, httpd_req_buf, data_len, 0); + data = httpd_req_buf; + } else +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + { + data = (char *)p->payload; + data_len = p->len; + if (p->len != p->tot_len) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: incomplete header due to chained pbufs\n")); + } + } + + /* Parse WebSocket request */ + hs->is_websocket = 0; + unsigned char *retval = NULL; + if (strncasestr(data, WS_HEADER, data_len)) { + LWIP_DEBUGF(HTTPD_DEBUG, ("WebSocket opening handshake\n")); + char *key_start = strncasestr(data, "Sec-WebSocket-Key: ", data_len); + if (key_start) { + key_start += 19; + char *key_end = strncasestr(key_start, "\r\n", data_len); + if (key_end) { + char key[64]; + int len = sizeof (char) * (key_end - key_start); + if ((len + sizeof (WS_GUID) < sizeof (key)) && (len > 0)) { + /* Allocate response buffer */ + retval = mem_malloc(WS_BUF_LEN); + if (retval == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Out of memory\n")); + return ERR_MEM; + } + unsigned char *retval_ptr; + retval_ptr = memcpy(retval, WS_RSP, sizeof(WS_RSP)); + retval_ptr += sizeof(WS_RSP) - 1; + + /* Concatenate key */ + memcpy(key, key_start, len); + strlcpy(&key[len], WS_GUID, sizeof(key)); + LWIP_DEBUGF(HTTPD_DEBUG, ("Resulting key: %s\n", key)); + + /* Get SHA1 */ + int key_len = sizeof(WS_GUID) - 1 + len; + unsigned char sha1sum[20]; + mbedtls_sha1((unsigned char *) key, key_len, sha1sum); + + /* Base64 encode */ + unsigned int olen; + mbedtls_base64_encode(NULL, 0, &olen, sha1sum, 20); //get length + int ok = mbedtls_base64_encode(retval_ptr, WS_BUF_LEN, &olen, sha1sum, 20); + + if (ok == 0) { + memcpy(&retval_ptr[olen], CRLF CRLF, sizeof(CRLF CRLF)); + hs->is_websocket = 1; + LWIP_DEBUGF(HTTPD_DEBUG, ("Base64 encoded: %s\n", retval_ptr)); + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("Key overflow")); + return ERR_MEM; + } + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("error: malformed packet\n")); + return ERR_ARG; + } + } + + /* received enough data for minimal request? */ + if (data_len >= MIN_REQ_LEN) { + /* wait for CRLF before parsing anything */ + crlf = strnstr(data, CRLF, data_len); + if (crlf != NULL) { +#if LWIP_HTTPD_SUPPORT_POST + int is_post = 0; +#endif /* LWIP_HTTPD_SUPPORT_POST */ + int is_09 = 0; + char *sp1, *sp2; + u16_t left_len, uri_len; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("CRLF received, parsing request\n")); + /* parse method */ + if (!strncmp(data, "GET ", 4)) { + sp1 = data + 3; + /* received GET request */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received GET request\"\n")); +#if LWIP_HTTPD_SUPPORT_POST + } else if (!strncmp(data, "POST ", 5)) { + /* store request type */ + is_post = 1; + sp1 = data + 4; + /* received GET request */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received POST request\n")); +#endif /* LWIP_HTTPD_SUPPORT_POST */ + } else { + /* null-terminate the METHOD (pbuf is freed anyway wen returning) */ + data[4] = 0; + /* unsupported method! */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported request method (not implemented): \"%s\"\n", + data)); + return http_find_error_file(hs, 501); + } + /* if we come here, method is OK, parse URI */ + left_len = data_len - ((sp1 +1) - data); + sp2 = strnstr(sp1 + 1, " ", left_len); +#if LWIP_HTTPD_SUPPORT_V09 + if (sp2 == NULL) { + /* HTTP 0.9: respond with correct protocol version */ + sp2 = strnstr(sp1 + 1, CRLF, left_len); + is_09 = 1; +#if LWIP_HTTPD_SUPPORT_POST + if (is_post) { + /* HTTP/0.9 does not support POST */ + goto badrequest; + } +#endif /* LWIP_HTTPD_SUPPORT_POST */ + } +#endif /* LWIP_HTTPD_SUPPORT_V09 */ + uri_len = sp2 - (sp1 + 1); + if ((sp2 != 0) && (sp2 > sp1)) { + /* wait for CRLFCRLF (indicating end of HTTP headers) before parsing anything */ + if (strnstr(data, CRLF CRLF, data_len) != NULL) { + char *uri = sp1 + 1; +#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE + if (!is_09 && strnstr(data, HTTP11_CONNECTIONKEEPALIVE, data_len)) { + hs->keepalive = 1; + } +#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ + /* null-terminate the METHOD (pbuf is freed anyway wen returning) */ + *sp1 = 0; + uri[uri_len] = 0; + LWIP_DEBUGF(HTTPD_DEBUG, ("Received \"%s\" request for URI: \"%s\"\n", + data, uri)); +#if LWIP_HTTPD_SUPPORT_POST + if (is_post) { +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + struct pbuf **q = &hs->req; +#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + struct pbuf **q = inp; +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + err = http_post_request(q, hs, data, data_len, uri, sp2); + if (err != ERR_OK) { + /* restore header for next try */ + *sp1 = ' '; + *sp2 = ' '; + uri[uri_len] = ' '; + } + if (err == ERR_ARG) { + goto badrequest; + } + return err; + } else +#endif /* LWIP_HTTPD_SUPPORT_POST */ + { + if (hs->is_websocket && retval != NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Sending:\n%s\n", retval)); + u16_t len = strlen((char *) retval); + http_write(pcb, retval, &len, 0); + mem_free(retval); + if(websocket_open_cb) + websocket_open_cb(pcb, uri); + return ERR_OK; // We handled this + } else { + return http_find_file(hs, uri, is_09); + } + } + } + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("invalid URI\n")); + } + } + } + +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + clen = pbuf_clen(hs->req); + if ((hs->req->tot_len <= LWIP_HTTPD_REQ_BUFSIZE) && + (clen <= LWIP_HTTPD_REQ_QUEUELEN)) { + /* request not fully received (too short or CRLF is missing) */ + return ERR_INPROGRESS; + } else +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + { +#if LWIP_HTTPD_SUPPORT_POST +badrequest: +#endif /* LWIP_HTTPD_SUPPORT_POST */ + LWIP_DEBUGF(HTTPD_DEBUG, ("bad request\n")); + /* could not parse request */ + return http_find_error_file(hs, 400); + } +} + +/** Try to find the file specified by uri and, if found, initialize hs + * accordingly. + * + * @param hs the connection state + * @param uri the HTTP header URI + * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response) + * @return ERR_OK if file was found and hs has been initialized correctly + * another err_t otherwise + */ +static err_t +http_find_file(struct http_state *hs, const char *uri, int is_09) +{ + size_t loop; + struct fs_file *file = NULL; + char *params; + err_t err; +#if LWIP_HTTPD_CGI + int i; + int count; +#endif /* LWIP_HTTPD_CGI */ +#if !LWIP_HTTPD_SSI + const +#endif /* !LWIP_HTTPD_SSI */ + /* By default, assume we will not be processing server-side-includes tags */ + u8_t tag_check = 0; + + /* Have we been asked for the default root file? */ + if((uri[0] == '/') && (uri[1] == 0)) { + /* Try each of the configured default filenames until we find one + that exists. */ + for (loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Looking for %s...\n", g_psDefaultFilenames[loop].name)); + err = fs_open(&hs->file_handle, (char *)g_psDefaultFilenames[loop].name); + uri = (char *)g_psDefaultFilenames[loop].name; + if(err == ERR_OK) { + file = &hs->file_handle; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opened.\n")); +#if LWIP_HTTPD_SSI + tag_check = g_psDefaultFilenames[loop].shtml; +#endif /* LWIP_HTTPD_SSI */ + break; + } + } + if (file == NULL) { + /* None of the default filenames exist so send back a 404 page */ + file = http_get_404_file(hs, &uri); +#if LWIP_HTTPD_SSI + tag_check = 0; +#endif /* LWIP_HTTPD_SSI */ + } + } else { + /* No - we've been asked for a specific file. */ + /* First, isolate the base URI (without any parameters) */ + params = (char *)strchr(uri, '?'); + if (params != NULL) { + /* URI contains parameters. NULL-terminate the base URI */ + *params = '\0'; + params++; + } + +#if LWIP_HTTPD_CGI + /* Does the base URI we have isolated correspond to a CGI handler? */ + if (g_iNumCGIs && g_pCGIs) { + for (i = 0; i < g_iNumCGIs; i++) { + if (strcmp(uri, g_pCGIs[i].pcCGIName) == 0) { + /* + * We found a CGI that handles this URI so extract the + * parameters and call the handler. + */ + count = extract_uri_parameters(hs, params); + uri = g_pCGIs[i].pfnCGIHandler(i, count, hs->params, + hs->param_vals); + break; + } + } + } +#endif /* LWIP_HTTPD_CGI */ + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri)); + + err = fs_open(&hs->file_handle, uri); + if (err == ERR_OK) { + file = &hs->file_handle; + } else { + file = http_get_404_file(hs, &uri); + } +#if LWIP_HTTPD_SSI + if (file != NULL) { + /* See if we have been asked for an shtml file and, if so, + enable tag checking. */ + tag_check = 0; + for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) { + if (strstr(uri, g_pcSSIExtensions[loop])) { + tag_check = 1; + break; + } + } + } +#endif /* LWIP_HTTPD_SSI */ + } + return http_init_file(hs, file, is_09, uri, tag_check); +} + +/** Initialize a http connection with a file to send (if found). + * Called by http_find_file and http_find_error_file. + * + * @param hs http connection state + * @param file file structure to send (or NULL if not found) + * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response) + * @param uri the HTTP header URI + * @param tag_check enable SSI tag checking + * @return ERR_OK if file was found and hs has been initialized correctly + * another err_t otherwise + */ +static err_t +http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check) +{ + if (file != NULL) { + /* file opened, initialise struct http_state */ +#if LWIP_HTTPD_SSI + if (tag_check) { + struct http_ssi_state *ssi = http_ssi_state_alloc(); + if (ssi != NULL) { + ssi->tag_index = 0; + ssi->tag_state = TAG_NONE; + ssi->parsed = file->data; + ssi->parse_left = file->len; + ssi->tag_end = file->data; + hs->ssi = ssi; + } + } +#else /* LWIP_HTTPD_SSI */ + LWIP_UNUSED_ARG(tag_check); +#endif /* LWIP_HTTPD_SSI */ + hs->handle = file; + hs->file = (char*)file->data; + LWIP_ASSERT("File length must be positive!", (file->len >= 0)); + hs->left = file->len; + hs->retries = 0; +#if LWIP_HTTPD_TIMING + hs->time_started = sys_now(); +#endif /* LWIP_HTTPD_TIMING */ +#if !LWIP_HTTPD_DYNAMIC_HEADERS + LWIP_ASSERT("HTTP headers not included in file system", hs->handle->http_header_included); +#endif /* !LWIP_HTTPD_DYNAMIC_HEADERS */ +#if LWIP_HTTPD_SUPPORT_V09 + if (hs->handle->http_header_included && is_09) { + /* HTTP/0.9 responses are sent without HTTP header, + search for the end of the header. */ + char *file_start = strnstr(hs->file, CRLF CRLF, hs->left); + if (file_start != NULL) { + size_t diff = file_start + 4 - hs->file; + hs->file += diff; + hs->left -= (u32_t)diff; + } + } +#endif /* LWIP_HTTPD_SUPPORT_V09*/ + } else { + hs->handle = NULL; + hs->file = NULL; + hs->left = 0; + hs->retries = 0; + } +#if LWIP_HTTPD_DYNAMIC_HEADERS + /* Determine the HTTP headers to send based on the file extension of + * the requested URI. */ + if ((hs->handle == NULL) || !hs->handle->http_header_included) { + get_http_headers(hs, (char*)uri); + } +#else /* LWIP_HTTPD_DYNAMIC_HEADERS */ + LWIP_UNUSED_ARG(uri); +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + return ERR_OK; +} + +/** + * The pcb had an error and is already deallocated. + * The argument might still be valid (if != NULL). + */ +static void +http_err(void *arg, err_t err) +{ + struct http_state *hs = (struct http_state *)arg; + LWIP_UNUSED_ARG(err); + + LWIP_DEBUGF(HTTPD_DEBUG, ("http_err: %s", lwip_strerr(err))); + + if (hs != NULL) { + http_state_free(hs); + } +} + +/** + * Data has been sent and acknowledged by the remote host. + * This means that more data can be sent. + */ +static err_t +http_sent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct http_state *hs = (struct http_state *)arg; + + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_sent %p\n", (void*)pcb)); + + LWIP_UNUSED_ARG(len); + + if (hs == NULL) { + return ERR_OK; + } + + hs->retries = 0; + + http_send(pcb, hs); + + return ERR_OK; +} + +/** + * The poll function is called every 2nd second. + * If there has been no data sent (which resets the retries) in 8 seconds, close. + * If the last portion of a file has not been sent in 2 seconds, close. + * + * This could be increased, but we don't want to waste resources for bad connections. + */ +static err_t +http_poll(void *arg, struct tcp_pcb *pcb) +{ + struct http_state *hs = (struct http_state *)arg; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n", + (void*)pcb, (void*)hs, tcp_debug_state_str(pcb->state))); + + if (hs == NULL) { + err_t closed; + /* arg is null, close. */ + LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n")); + closed = http_close_conn(pcb, NULL); + LWIP_UNUSED_ARG(closed); +#if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR + if (closed == ERR_MEM) { + tcp_abort(pcb); + return ERR_ABRT; + } +#endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */ + return ERR_OK; + } else { + hs->retries++; + if (hs->retries == ((hs->is_websocket) ? WS_TIMEOUT : HTTPD_MAX_RETRIES)) { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n")); + http_close_conn(pcb, hs); + return ERR_OK; + } + + /* If this connection has a file open, try to send some more data. If + * it has not yet received a GET request, don't do this since it will + * cause the connection to close immediately. */ + if(hs && (hs->handle)) { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: try to send more data\n")); + if(http_send(pcb, hs)) { + /* If we wrote anything to be sent, go ahead and send it now. */ + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n")); + tcp_output(pcb); + } + } + } + + return ERR_OK; +} + +void +websocket_register_callbacks(tWsOpenHandler ws_open_cb, tWsHandler ws_cb) +{ + websocket_open_cb = ws_open_cb; + websocket_cb = ws_cb; +} + +err_t +websocket_write(struct tcp_pcb *pcb, const uint8_t *data, uint16_t len, uint8_t mode) +{ + uint8_t *buf = mem_malloc(len + 4); + if (buf == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("[websocket_write] out of memory\n")); + return ERR_MEM; + } + + int offset = 2; + buf[0] = 0x80 | mode; + if (len > 125) { + offset = 4; + buf[1] = 126; + buf[2] = len >> 8; + buf[3] = len; + } else { + buf[1] = len; + } + + memcpy(&buf[offset], data, len); + len += offset; + + LWIP_DEBUGF(HTTPD_DEBUG, ("[websocket_write] sending packet\n")); + err_t retval = http_write(pcb, buf, &len, TCP_WRITE_FLAG_COPY); + mem_free(buf); + + return retval; +} + +/** + * Send status code 1000 (normal closure). + */ +static err_t +websocket_send_close(struct tcp_pcb *pcb) +{ + const u8_t buf[] = {0x88, 0x02, 0x03, 0xe8}; + u16_t len = sizeof (buf); + LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] closing connection\n")); + return tcp_write(pcb, buf, len, TCP_WRITE_FLAG_COPY); +} + +/** + * Parse websocket frame. + * + * @return ERR_OK: frame parsed + * ERR_CLSD: close request from client + * ERR_VAL: invalid frame. + */ +static err_t +websocket_parse(struct tcp_pcb *pcb, struct pbuf *p) +{ + u8_t *data = (u8_t *) p->payload; + u16_t data_len = p->len; + + if (data != NULL && data_len > 1) { + LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] frame received\n")); + if ((data[0] & 0x80) == 0) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: continuation frames not supported\n")); + return ERR_OK; + } + u8_t opcode = data[0] & 0x0F; + switch (opcode) { + case WS_TEXT_MODE: + case WS_BIN_MODE: + LWIP_DEBUGF(HTTPD_DEBUG, ("Opcode: 0x%hX, frame length: %d\n", opcode, data_len)); + if (data_len > 6 && websocket_cb != NULL) { + int data_offset = 6; + u8_t *dptr = &data[6]; + u8_t *kptr = &data[2]; + u16_t len = data[1] & 0x7F; + + if (len == 127) { + /* most likely won't happen inside non-fragmented frame */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: frame is too long\n")); + return ERR_OK; + } else if (len == 126) { + /* extended length */ + dptr += 2; + kptr += 2; + data_offset += 2; + len = (data[2] << 8) | data[3]; + } + + data_len -= data_offset; + + if (len > data_len) { + LWIP_DEBUGF(HTTPD_DEBUG, ("Error: incorrect frame size\n")); + return ERR_VAL; + } + + if (data_len != len) + LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: segmented frame received\n")); + + /* unmask */ + for (int i = 0; i < len; i++) + *(dptr++) ^= kptr[i % 4]; + + /* user callback */ + websocket_cb(pcb, &data[data_offset], len, opcode); + } + break; + case 0x08: // close + LWIP_DEBUGF(HTTPD_DEBUG, ("Close request\n")); + return ERR_CLSD; + default: + LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported opcode 0x%hX\n", opcode)); + break; + } + return ERR_OK; + } + return ERR_VAL; +} + +/** + * Data has been received on this pcb. + * For HTTP 1.0, this should normally only happen once (if the request fits in one packet). + */ +static err_t +http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + err_t parsed = ERR_ABRT; + struct http_state *hs = (struct http_state *)arg; + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void*)pcb, + (void*)p, lwip_strerr(err))); + + if (hs != NULL && hs->is_websocket) { + if (p == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: buffer error\n")); + http_close_or_abort_conn(pcb, hs, 0); + return ERR_BUF; + } + tcp_recved(pcb, p->tot_len); + err_t err = websocket_parse(pcb, p); + if (p != NULL) { + /* otherwise tcp buffer hogs */ + LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] freeing buffer\n")); + pbuf_free(p); + } + if (err == ERR_CLSD) { + http_close_conn(pcb, hs); + } + /* reset timeout */ + hs->retries = 0; + return ERR_OK; + } + + if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) { + /* error or closed by other side? */ + if (p != NULL) { + /* Inform TCP that we have taken the data. */ + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } + if (hs == NULL) { + /* this should not happen, only to be robust */ + LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n")); + } + http_close_conn(pcb, hs); + return ERR_OK; + } + +#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND + if (hs->no_auto_wnd) { + hs->unrecved_bytes += p->tot_len; + } else +#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ + { + /* Inform TCP that we have taken the data. */ + tcp_recved(pcb, p->tot_len); + } + +#if LWIP_HTTPD_SUPPORT_POST + if (hs->post_content_len_left > 0) { + /* reset idle counter when POST data is received */ + hs->retries = 0; + /* this is data for a POST, pass the complete pbuf to the application */ + http_post_rxpbuf(hs, p); + /* pbuf is passed to the application, don't free it! */ + if (hs->post_content_len_left == 0) { + /* all data received, send response or close connection */ + http_send(pcb, hs); + } + return ERR_OK; + } else +#endif /* LWIP_HTTPD_SUPPORT_POST */ + { + if (hs->handle == NULL) { + parsed = http_parse_request(&p, hs, pcb); + LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK + || parsed == ERR_INPROGRESS ||parsed == ERR_ARG + || parsed == ERR_USE || parsed == ERR_MEM); + } else { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n")); + } +#if LWIP_HTTPD_SUPPORT_REQUESTLIST + if (parsed != ERR_INPROGRESS) { + /* request fully parsed or error */ + if (hs->req != NULL) { + pbuf_free(hs->req); + hs->req = NULL; + } + } +#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + if (p != NULL) { + /* pbuf not passed to application, free it now */ + pbuf_free(p); + } +#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ + if (parsed == ERR_OK) { +#if LWIP_HTTPD_SUPPORT_POST + if (hs->post_content_len_left == 0) +#endif /* LWIP_HTTPD_SUPPORT_POST */ + { + LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: data %p len %"S32_F"\n", hs->file, hs->left)); + http_send(pcb, hs); + } + } else if (parsed == ERR_ARG || parsed == ERR_MEM) { + /* @todo: close on ERR_USE? */ + http_close_conn(pcb, hs); + } + } + return ERR_OK; +} + +/** + * A new incoming connection has been accepted. + */ +static err_t +http_accept(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct http_state *hs; + struct tcp_pcb_listen *lpcb = (struct tcp_pcb_listen*)arg; + LWIP_UNUSED_ARG(err); + LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void*)pcb, arg)); + + /* Decrease the listen backlog counter */ + tcp_accepted(lpcb); + /* Set priority */ + tcp_setprio(pcb, HTTPD_TCP_PRIO); + + /* Allocate memory for the structure that holds the state of the + connection - initialized by that function. */ + hs = http_state_alloc(); + if (hs == NULL) { + LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n")); + return ERR_MEM; + } + hs->pcb = pcb; + + /* Tell TCP that this is the structure we wish to be passed for our + callbacks. */ + tcp_arg(pcb, hs); + + /* Set up the various callback functions */ + tcp_recv(pcb, http_recv); + tcp_err(pcb, http_err); + tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL); + tcp_sent(pcb, http_sent); + + return ERR_OK; +} + +/** + * Initialize the httpd with the specified local address. + */ +static void +httpd_init_addr(ip_addr_t *local_addr) +{ + struct tcp_pcb *pcb; + err_t err; + + pcb = tcp_new(); + LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL); + tcp_setprio(pcb, HTTPD_TCP_PRIO); + /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */ + err = tcp_bind(pcb, local_addr, HTTPD_SERVER_PORT); + LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK); + pcb = tcp_listen(pcb); + LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL); + /* initialize callback arg and accept callback */ + tcp_arg(pcb, pcb); + tcp_accept(pcb, http_accept); +} + +/** + * Initialize the httpd: set up a listening PCB and bind it to the defined port + */ +void +httpd_init(void) +{ +#if HTTPD_USE_MEM_POOL + LWIP_ASSERT("memp_sizes[MEMP_HTTPD_STATE] >= sizeof(http_state)", + memp_sizes[MEMP_HTTPD_STATE] >= sizeof(http_state)); + LWIP_ASSERT("memp_sizes[MEMP_HTTPD_SSI_STATE] >= sizeof(http_ssi_state)", + memp_sizes[MEMP_HTTPD_SSI_STATE] >= sizeof(http_ssi_state)); +#endif + LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n")); + + httpd_init_addr(IP_ADDR_ANY); +} + +#if LWIP_HTTPD_SSI +/** + * Set the SSI handler function. + * + * @param ssi_handler the SSI handler function + * @param tags an array of SSI tag strings to search for in SSI-enabled files + * @param num_tags number of tags in the 'tags' array + */ +void +http_set_ssi_handler(tSSIHandler ssi_handler, const char **tags, int num_tags) +{ + LWIP_DEBUGF(HTTPD_DEBUG, ("http_set_ssi_handler\n")); + + LWIP_ASSERT("no ssi_handler given", ssi_handler != NULL); + LWIP_ASSERT("no tags given", tags != NULL); + LWIP_ASSERT("invalid number of tags", num_tags > 0); + + g_pfnSSIHandler = ssi_handler; + g_ppcTags = tags; + g_iNumTags = num_tags; +} +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_CGI +/** + * Set an array of CGI filenames/handler functions + * + * @param cgis an array of CGI filenames/handler functions + * @param num_handlers number of elements in the 'cgis' array + */ +void +http_set_cgi_handlers(const tCGI *cgis, int num_handlers) +{ + LWIP_ASSERT("no cgis given", cgis != NULL); + LWIP_ASSERT("invalid number of handlers", num_handlers > 0); + + g_pCGIs = cgis; + g_iNumCGIs = num_handlers; +} +#endif /* LWIP_HTTPD_CGI */ + +#endif /* LWIP_TCP */ diff --git a/extras/httpd/httpd.h b/extras/httpd/httpd.h new file mode 100644 index 0000000..59b0095 --- /dev/null +++ b/extras/httpd/httpd.h @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * This version of the file has been modified by Texas Instruments to offer + * simple server-side-include (SSI) and Common Gateway Interface (CGI) + * capability. + */ + +#ifndef __HTTPD_H__ +#define __HTTPD_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/tcp.h" + + +/** Set this to 1 to support CGI */ +#ifndef LWIP_HTTPD_CGI +#define LWIP_HTTPD_CGI 0 +#endif + +/** Set this to 1 to support SSI (Server-Side-Includes) */ +#ifndef LWIP_HTTPD_SSI +#define LWIP_HTTPD_SSI 0 +#endif + +/** Set this to 1 to support HTTP POST */ +#ifndef LWIP_HTTPD_SUPPORT_POST +#define LWIP_HTTPD_SUPPORT_POST 0 +#endif + +#if LWIP_HTTPD_CGI + +/* + * Function pointer for a CGI script handler. + * + * This function is called each time the HTTPD server is asked for a file + * whose name was previously registered as a CGI function using a call to + * http_set_cgi_handler. The iIndex parameter provides the index of the + * CGI within the ppcURLs array passed to http_set_cgi_handler. Parameters + * pcParam and pcValue provide access to the parameters provided along with + * the URI. iNumParams provides a count of the entries in the pcParam and + * pcValue arrays. Each entry in the pcParam array contains the name of a + * parameter with the corresponding entry in the pcValue array containing the + * value for that parameter. Note that pcParam may contain multiple elements + * with the same name if, for example, a multi-selection list control is used + * in the form generating the data. + * + * The function should return a pointer to a character string which is the + * path and filename of the response that is to be sent to the connected + * browser, for example "/thanks.htm" or "/response/error.ssi". + * + * The maximum number of parameters that will be passed to this function via + * iNumParams is defined by LWIP_HTTPD_MAX_CGI_PARAMETERS. Any parameters in the incoming + * HTTP request above this number will be discarded. + * + * Requests intended for use by this CGI mechanism must be sent using the GET + * method (which encodes all parameters within the URI rather than in a block + * later in the request). Attempts to use the POST method will result in the + * request being ignored. + * + */ +typedef const char *(*tCGIHandler)(int iIndex, int iNumParams, char *pcParam[], + char *pcValue[]); + +/* + * Structure defining the base filename (URL) of a CGI and the associated + * function which is to be called when that URL is requested. + */ +typedef struct +{ + const char *pcCGIName; + tCGIHandler pfnCGIHandler; +} tCGI; + +void http_set_cgi_handlers(const tCGI *pCGIs, int iNumHandlers); + + +/* The maximum number of parameters that the CGI handler can be sent. */ +#ifndef LWIP_HTTPD_MAX_CGI_PARAMETERS +#define LWIP_HTTPD_MAX_CGI_PARAMETERS 16 +#endif + +#endif /* LWIP_HTTPD_CGI */ + +#if LWIP_HTTPD_SSI + +/** LWIP_HTTPD_SSI_MULTIPART==1: SSI handler function is called with 2 more + * arguments indicating a counter for insert string that are too long to be + * inserted at once: the SSI handler function must then set 'next_tag_part' + * which will be passed back to it in the next call. */ +#ifndef LWIP_HTTPD_SSI_MULTIPART +#define LWIP_HTTPD_SSI_MULTIPART 0 +#endif + +/* + * Function pointer for the SSI tag handler callback. + * + * This function will be called each time the HTTPD server detects a tag of the + * form in a .shtml, .ssi or .shtm file where "name" appears as + * one of the tags supplied to http_set_ssi_handler in the ppcTags array. The + * returned insert string, which will be appended after the the string + * "" in file sent back to the client,should be written to pointer + * pcInsert. iInsertLen contains the size of the buffer pointed to by + * pcInsert. The iIndex parameter provides the zero-based index of the tag as + * found in the ppcTags array and identifies the tag that is to be processed. + * + * The handler returns the number of characters written to pcInsert excluding + * any terminating NULL or a negative number to indicate a failure (tag not + * recognized, for example). + * + * Note that the behavior of this SSI mechanism is somewhat different from the + * "normal" SSI processing as found in, for example, the Apache web server. In + * this case, the inserted text is appended following the SSI tag rather than + * replacing the tag entirely. This allows for an implementation that does not + * require significant additional buffering of output data yet which will still + * offer usable SSI functionality. One downside to this approach is when + * attempting to use SSI within JavaScript. The SSI tag is structured to + * resemble an HTML comment but this syntax does not constitute a comment + * within JavaScript and, hence, leaving the tag in place will result in + * problems in these cases. To work around this, any SSI tag which needs to + * output JavaScript code must do so in an encapsulated way, sending the whole + * HTML section as a single include. + */ +typedef u16_t (*tSSIHandler)(int iIndex, char *pcInsert, int iInsertLen +#if LWIP_HTTPD_SSI_MULTIPART + , u16_t current_tag_part, u16_t *next_tag_part +#endif /* LWIP_HTTPD_SSI_MULTIPART */ +#if LWIP_HTTPD_FILE_STATE + , void *connection_state +#endif /* LWIP_HTTPD_FILE_STATE */ + ); + +void http_set_ssi_handler(tSSIHandler pfnSSIHandler, + const char **ppcTags, int iNumTags); + +/* The maximum length of the string comprising the tag name */ +#ifndef LWIP_HTTPD_MAX_TAG_NAME_LEN +#define LWIP_HTTPD_MAX_TAG_NAME_LEN 8 +#endif + +/* The maximum length of string that can be returned to replace any given tag */ +#ifndef LWIP_HTTPD_MAX_TAG_INSERT_LEN +#define LWIP_HTTPD_MAX_TAG_INSERT_LEN 192 +#endif + +#endif /* LWIP_HTTPD_SSI */ + +#if LWIP_HTTPD_SUPPORT_POST + +/* These functions must be implemented by the application */ + +/** Called when a POST request has been received. The application can decide + * whether to accept it or not. + * + * @param connection Unique connection identifier, valid until httpd_post_end + * is called. + * @param uri The HTTP header URI receiving the POST request. + * @param http_request The raw HTTP request (the first packet, normally). + * @param http_request_len Size of 'http_request'. + * @param content_len Content-Length from HTTP header. + * @param response_uri Filename of response file, to be filled when denying the + * request + * @param response_uri_len Size of the 'response_uri' buffer. + * @param post_auto_wnd Set this to 0 to let the callback code handle window + * updates by calling 'httpd_post_data_recved' (to throttle rx speed) + * default is 1 (httpd handles window updates automatically) + * @return ERR_OK: Accept the POST request, data may be passed in + * another err_t: Deny the POST request, send back 'bad request'. + */ +err_t httpd_post_begin(void *connection, const char *uri, const char *http_request, + u16_t http_request_len, int content_len, char *response_uri, + u16_t response_uri_len, u8_t *post_auto_wnd); + +/** Called for each pbuf of data that has been received for a POST. + * ATTENTION: The application is responsible for freeing the pbufs passed in! + * + * @param connection Unique connection identifier. + * @param p Received data. + * @return ERR_OK: Data accepted. + * another err_t: Data denied, http_post_get_response_uri will be called. + */ +err_t httpd_post_receive_data(void *connection, struct pbuf *p); + +/** Called when all data is received or when the connection is closed. + * The application must return the filename/URI of a file to send in response + * to this POST request. If the response_uri buffer is untouched, a 404 + * response is returned. + * + * @param connection Unique connection identifier. + * @param response_uri Filename of response file, to be filled when denying the request + * @param response_uri_len Size of the 'response_uri' buffer. + */ +void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len); + +#ifndef LWIP_HTTPD_POST_MANUAL_WND +#define LWIP_HTTPD_POST_MANUAL_WND 0 +#endif + +#if LWIP_HTTPD_POST_MANUAL_WND +void httpd_post_data_recved(void *connection, u16_t recved_len); +#endif /* LWIP_HTTPD_POST_MANUAL_WND */ + +#endif /* LWIP_HTTPD_SUPPORT_POST */ + +enum { + WS_TEXT_MODE = 0x01, + WS_BIN_MODE = 0x02, +} WS_MODE; + +typedef void (*tWsHandler)(struct tcp_pcb *pcb, uint8_t *data, u16_t data_len, uint8_t mode); +typedef void (*tWsOpenHandler)(struct tcp_pcb *pcb, const char *uri); + +/** + * Write data into a websocket. + * + * @param pcb tcp_pcb to send. + * @param data data to send. + * @param len data length. + * @param mode WS_TEXT_MODE or WS_BIN_MODE. + * @return ERR_OK if write succeeded. + */ +err_t websocket_write(struct tcp_pcb *pcb, const uint8_t *data, uint16_t len, uint8_t mode); + +/** + * Register websocket callback functions. Use NULL if callback is not needed. + * + * @param ws_open_cb called when new websocket is opened. + * @param ws_cb called when data is received from client. + */ +void websocket_register_callbacks(tWsOpenHandler ws_open_cb, tWsHandler ws_cb); + +void httpd_init(void); + +#endif /* __HTTPD_H__ */ diff --git a/extras/httpd/httpd_structs.h b/extras/httpd/httpd_structs.h new file mode 100644 index 0000000..51dc807 --- /dev/null +++ b/extras/httpd/httpd_structs.h @@ -0,0 +1,125 @@ +#ifndef __HTTPD_STRUCTS_H__ +#define __HTTPD_STRUCTS_H__ + +#include "httpd.h" + +/** This string is passed in the HTTP header as "Server: " */ +#ifndef HTTPD_SERVER_AGENT +#define HTTPD_SERVER_AGENT "lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip)" +#endif + +/** Set this to 1 if you want to include code that creates HTTP headers + * at runtime. Default is off: HTTP headers are then created statically + * by the makefsdata tool. Static headers mean smaller code size, but + * the (readonly) fsdata will grow a bit as every file includes the HTTP + * header. */ +#ifndef LWIP_HTTPD_DYNAMIC_HEADERS +#define LWIP_HTTPD_DYNAMIC_HEADERS 0 +#endif + + +#if LWIP_HTTPD_DYNAMIC_HEADERS +/** This struct is used for a list of HTTP header strings for various + * filename extensions. */ +typedef struct +{ + const char *extension; + int headerIndex; +} tHTTPHeader; + +/** A list of strings used in HTTP headers */ +static const char * const g_psHTTPHeaderStrings[] = +{ + "Content-type: text/html\r\n\r\n", + "Content-type: text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache\r\n\r\n", + "Content-type: image/gif\r\n\r\n", + "Content-type: image/png\r\n\r\n", + "Content-type: image/jpeg\r\n\r\n", + "Content-type: image/bmp\r\n\r\n", + "Content-type: image/x-icon\r\n\r\n", + "Content-type: application/octet-stream\r\n\r\n", + "Content-type: application/x-javascript\r\n\r\n", + "Content-type: application/x-javascript\r\n\r\n", + "Content-type: text/css\r\n\r\n", + "Content-type: application/x-shockwave-flash\r\n\r\n", + "Content-type: text/xml\r\n\r\n", + "Content-type: text/plain\r\n\r\n", + "HTTP/1.0 200 OK\r\n", + "HTTP/1.0 404 File not found\r\n", + "HTTP/1.0 400 Bad Request\r\n", + "HTTP/1.0 501 Not Implemented\r\n", + "HTTP/1.1 200 OK\r\n", + "HTTP/1.1 404 File not found\r\n", + "HTTP/1.1 400 Bad Request\r\n", + "HTTP/1.1 501 Not Implemented\r\n", + "Content-Length: ", + "Connection: Close\r\n", + "Connection: keep-alive\r\n", + "Server: "HTTPD_SERVER_AGENT"\r\n", + "\r\n

404: The requested file cannot be found.

\r\n" +}; + +/* Indexes into the g_psHTTPHeaderStrings array */ +#define HTTP_HDR_HTML 0 /* text/html */ +#define HTTP_HDR_SSI 1 /* text/html Expires... */ +#define HTTP_HDR_GIF 2 /* image/gif */ +#define HTTP_HDR_PNG 3 /* image/png */ +#define HTTP_HDR_JPG 4 /* image/jpeg */ +#define HTTP_HDR_BMP 5 /* image/bmp */ +#define HTTP_HDR_ICO 6 /* image/x-icon */ +#define HTTP_HDR_APP 7 /* application/octet-stream */ +#define HTTP_HDR_JS 8 /* application/x-javascript */ +#define HTTP_HDR_RA 9 /* application/x-javascript */ +#define HTTP_HDR_CSS 10 /* text/css */ +#define HTTP_HDR_SWF 11 /* application/x-shockwave-flash */ +#define HTTP_HDR_XML 12 /* text/xml */ +#define HTTP_HDR_DEFAULT_TYPE 13 /* text/plain */ +#define HTTP_HDR_OK 14 /* 200 OK */ +#define HTTP_HDR_NOT_FOUND 15 /* 404 File not found */ +#define HTTP_HDR_BAD_REQUEST 16 /* 400 Bad request */ +#define HTTP_HDR_NOT_IMPL 17 /* 501 Not Implemented */ +#define HTTP_HDR_OK_11 18 /* 200 OK */ +#define HTTP_HDR_NOT_FOUND_11 19 /* 404 File not found */ +#define HTTP_HDR_BAD_REQUEST_11 20 /* 400 Bad request */ +#define HTTP_HDR_NOT_IMPL_11 21 /* 501 Not Implemented */ +#define HTTP_HDR_CONTENT_LENGTH 22 /* Content-Length: (HTTP 1.1)*/ +#define HTTP_HDR_CONN_CLOSE 23 /* Connection: Close (HTTP 1.1) */ +#define HTTP_HDR_CONN_KEEPALIVE 24 /* Connection: keep-alive (HTTP 1.1) */ +#define HTTP_HDR_SERVER 25 /* Server: HTTPD_SERVER_AGENT */ +#define DEFAULT_404_HTML 26 /* default 404 body */ + +/** A list of extension-to-HTTP header strings */ +const static tHTTPHeader g_psHTTPHeaders[] = +{ + { "html", HTTP_HDR_HTML}, + { "htm", HTTP_HDR_HTML}, + { "shtml",HTTP_HDR_SSI}, + { "shtm", HTTP_HDR_SSI}, + { "ssi", HTTP_HDR_SSI}, + { "gif", HTTP_HDR_GIF}, + { "png", HTTP_HDR_PNG}, + { "jpg", HTTP_HDR_JPG}, + { "bmp", HTTP_HDR_BMP}, + { "ico", HTTP_HDR_ICO}, + { "class",HTTP_HDR_APP}, + { "cls", HTTP_HDR_APP}, + { "js", HTTP_HDR_JS}, + { "ram", HTTP_HDR_RA}, + { "css", HTTP_HDR_CSS}, + { "swf", HTTP_HDR_SWF}, + { "xml", HTTP_HDR_XML}, + { "xsl", HTTP_HDR_XML} +}; + +#define NUM_HTTP_HEADERS (sizeof(g_psHTTPHeaders) / sizeof(tHTTPHeader)) + +#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ + +#if LWIP_HTTPD_SSI +static const char * const g_pcSSIExtensions[] = { + ".shtml", ".shtm", ".ssi", ".xml" +}; +#define NUM_SHTML_EXTENSIONS (sizeof(g_pcSSIExtensions) / sizeof(const char *)) +#endif /* LWIP_HTTPD_SSI */ + +#endif /* __HTTPD_STRUCTS_H__ */ diff --git a/extras/httpd/readme.txt b/extras/httpd/readme.txt new file mode 100644 index 0000000..ebf1109 --- /dev/null +++ b/extras/httpd/readme.txt @@ -0,0 +1,11 @@ +This is a basic HTTP server with WebSockets based on httpd from LwIP. + +WebSockets implementation supports binary and text modes. Multiple sockets are supported. Continuation frames are not implemented. +By default, a WebSocket is closed after 20 seconds of inactivity to conserve memory. This behavior can be changed by overriding `WS_TIMEOUT` option. + +To enable debugging extra flags `-DLWIP_DEBUG=1 -DHTTPD_DEBUG=LWIP_DBG_ON` should be passed at compile-time. + +This module expects your project to provide "fsdata.c" created with "makefsdata" utility. +See examples/http_server. + +Maintained by lujji (https://github.com/lujji/esp-httpd). diff --git a/extras/httpd/strcasestr.c b/extras/httpd/strcasestr.c new file mode 100644 index 0000000..26a0e53 --- /dev/null +++ b/extras/httpd/strcasestr.c @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 2001 Mike Barcroft + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. + */ +#include "strcasestr.h" +#include +#include + +char * +strcasestr(s, find) +const char *s, *find; +{ + char c, sc; + size_t len; + + if ((c = *find++) != 0) { + c = tolower((unsigned char) c); + len = strlen(find); + do { + do { + if ((sc = *s++) == 0) + return (NULL); + } while ((char) tolower((unsigned char) sc) != c); + } while (strncasecmp(s, find, len) != 0); + s--; + } + return ((char *) s); +} + +char * +strncasestr(s, find, slen) +const char *s; +const char *find; +size_t slen; +{ + char c, sc; + size_t len; + + if ((c = *find++) != '\0') { + len = strlen(find); + do { + do { + if (slen-- < 1 || (sc = *s++) == '\0') + return (NULL); + } while (sc != c); + if (len > slen) + return (NULL); + } while (strncasecmp(s, find, len) != 0); + s--; + } + return ((char *) s); +} \ No newline at end of file diff --git a/extras/httpd/strcasestr.h b/extras/httpd/strcasestr.h new file mode 100644 index 0000000..4947c11 --- /dev/null +++ b/extras/httpd/strcasestr.h @@ -0,0 +1,9 @@ +#ifndef STRCASESTR_H +#define STRCASESTR_H + +#include + +char *strcasestr(const char *s, const char *find); +char *strncasestr(const char *s, const char * find, size_t slen); + +#endif /* STRCASESTR_H */ diff --git a/extras/i2s_dma/i2s_dma.c b/extras/i2s_dma/i2s_dma.c index 6cccc72..442cdf3 100644 --- a/extras/i2s_dma/i2s_dma.c +++ b/extras/i2s_dma/i2s_dma.c @@ -26,6 +26,7 @@ #include "esp/iomux.h" #include "esp/i2s_regs.h" #include "esp/interrupts.h" +#include "esp/iomux.h" #include "common_macros.h" #include diff --git a/extras/max7219/component.mk b/extras/max7219/component.mk new file mode 100644 index 0000000..29dcf2e --- /dev/null +++ b/extras/max7219/component.mk @@ -0,0 +1,7 @@ +# include it as 'max7219/max7219.h' +INC_DIRS += $(max7219_ROOT).. + +# args for passing into compile rule generation +max7219_SRC_DIR = $(max7219_ROOT) + +$(eval $(call component_compile_rules,max7219)) diff --git a/extras/max7219/max7219.c b/extras/max7219/max7219.c new file mode 100644 index 0000000..34ad069 --- /dev/null +++ b/extras/max7219/max7219.c @@ -0,0 +1,189 @@ +/* + * Driver for MAX7219/MAX7221 + * Serially Interfaced, 8-Digit LED Display Drivers + * + * Part of esp-open-rtos + * Copyright (C) 2017 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#include "max7219.h" +#include +#include +#include + +#include "max7219_priv.h" + +#define SPI_BUS 1 + +//#define MAX7219_DEBUG + +#ifdef MAX7219_DEBUG +#include +#define debug(fmt, ...) printf("%s: " fmt "\n", "MAX7219", ## __VA_ARGS__) +#else +#define debug(fmt, ...) +#endif + +#define ALL_CHIPS 0xff +#define ALL_DIGITS 8 + +#define REG_DIGIT_0 (1 << 8) +#define REG_DECODE_MODE (9 << 8) +#define REG_INTENSITY (10 << 8) +#define REG_SCAN_LIMIT (11 << 8) +#define REG_SHUTDOWN (12 << 8) +#define REG_DISPLAY_TEST (15 << 8) + +#define VAL_CLEAR_BCD 0x0f +#define VAL_CLEAR_NORMAL 0x00 + +static const spi_settings_t bus_settings = { + .mode = SPI_MODE0, + .freq_divider = SPI_FREQ_DIV_10M, + .msb = true, + .minimal_pins = true, + .endianness = SPI_BIG_ENDIAN +}; + +static void send(const max7219_display_t *disp, uint8_t chip, uint16_t value) +{ + uint16_t buf[MAX7219_MAX_CASCADE_SIZE] = { 0 }; + if (chip == ALL_CHIPS) + { + for (uint8_t i = 0; i < disp->cascade_size; i++) + buf[i] = value; + } + else buf[chip] = value; + + spi_settings_t old_settings; + spi_get_settings(SPI_BUS, &old_settings); + spi_set_settings(SPI_BUS, &bus_settings); + gpio_write(disp->cs_pin, false); + + spi_transfer(SPI_BUS, buf, NULL, disp->cascade_size, SPI_16BIT); + + gpio_write(disp->cs_pin, true); + spi_set_settings(SPI_BUS, &old_settings); +} + +bool max7219_init(max7219_display_t *disp) +{ + if (!disp->cascade_size || disp->cascade_size > MAX7219_MAX_CASCADE_SIZE) + { + debug("Invalid cascade size %d", disp->cascade_size); + return false; + } + + uint8_t max_digits = disp->cascade_size * ALL_DIGITS; + if (!disp->digits || disp->digits > max_digits) + { + debug("Invalid digits count %d, max %d", disp->cascade_size, max_digits); + return false; + } + + gpio_enable(disp->cs_pin, GPIO_OUTPUT); + gpio_write(disp->cs_pin, true); + + // Shutdown all chips + max7219_set_shutdown_mode(disp, true); + // Disable test + send(disp, ALL_CHIPS, REG_DISPLAY_TEST); + // Set max scan limit + send(disp, ALL_CHIPS, REG_SCAN_LIMIT | (ALL_DIGITS - 1)); + // Set normal decode mode & clear display + max7219_set_decode_mode(disp, false); + // Set minimal brightness + max7219_set_brightness(disp, 0); + // Wake up + max7219_set_shutdown_mode(disp, false); + + return true; +} + +void max7219_set_decode_mode(max7219_display_t *disp, bool bcd) +{ + disp->bcd = bcd; + send(disp, ALL_CHIPS, REG_DECODE_MODE | (bcd ? 0xff : 0)); + max7219_clear(disp); +} + +void max7219_set_brightness(const max7219_display_t *disp, uint8_t value) +{ + send(disp, ALL_CHIPS, REG_INTENSITY | (value > MAX7219_MAX_BRIGHTNESS ? MAX7219_MAX_BRIGHTNESS : value)); +} + +void max7219_set_shutdown_mode(const max7219_display_t *disp, bool shutdown) +{ + send(disp, ALL_CHIPS, REG_SHUTDOWN | !shutdown); +} + +bool max7219_set_digit(const max7219_display_t *disp, uint8_t digit, uint8_t val) +{ + if (digit >= disp->digits) + { + debug("Invalid digit: %d", digit); + return false; + } + + if (disp->mirrored) + digit = disp->digits - digit - 1; + + uint8_t c = digit / ALL_DIGITS; + uint8_t d = digit % ALL_DIGITS; + + send(disp, c, (REG_DIGIT_0 + ((uint16_t)d << 8)) | val); + + return true; +} + +void max7219_clear(const max7219_display_t *disp) +{ + uint8_t val = disp->bcd ? VAL_CLEAR_BCD : VAL_CLEAR_NORMAL; + for (uint8_t i = 0; i < ALL_DIGITS; i++) + send(disp, ALL_CHIPS, (REG_DIGIT_0 + ((uint16_t)i << 8)) | val); +} + +inline static uint8_t get_char(const max7219_display_t *disp, char c) +{ + if (disp->bcd) + { + if (c >= '0' && c <= '9') + return c - '0'; + switch (c) + { + case '-': + return 0x0a; + case 'E': + case 'e': + return 0x0b; + case 'H': + case 'h': + return 0x0c; + case 'L': + case 'l': + return 0x0d; + case 'P': + case 'p': + return 0x0e; + } + return VAL_CLEAR_BCD; + } + + return font_7seg[(c - 0x20) & 0x7f]; +} + +void max7219_draw_text(const max7219_display_t *disp, uint8_t pos, const char *s) +{ + while (s && pos < disp->digits) + { + uint8_t c = get_char(disp, *s); + if (*(s + 1) == '.') + { + c |= 0x80; + s++; + } + max7219_set_digit(disp, pos, c); + pos++; + s++; + } +} diff --git a/extras/max7219/max7219.h b/extras/max7219/max7219.h new file mode 100644 index 0000000..127d6c0 --- /dev/null +++ b/extras/max7219/max7219.h @@ -0,0 +1,96 @@ +/* + * Driver for MAX7219/MAX7221 + * Serially Interfaced, 8-Digit LED Display Drivers + * + * Part of esp-open-rtos + * Copyright (C) 2017 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#ifndef EXTRAS_MAX7219_H_ +#define EXTRAS_MAX7219_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MAX7219_MAX_CASCADE_SIZE 8 +#define MAX7219_MAX_BRIGHTNESS 31 + +/** + * Display descriptor + */ +typedef struct +{ + uint8_t cs_pin; //!< GPIO connected to CS/LOAD pin + + uint8_t digits; //!< Accessible digits in 7seg. Up to cascade_size * 8 + + uint8_t cascade_size; //!< Up to 8 MAX721xx cascaded + bool mirrored; //!< true for horizontally mirrored displays + + bool bcd; +} max7219_display_t; + +/** + * Initialize display: switch it to normal operation from shutdown mode, + * set scan limit to the max and clear + * @param disp Pointer to display descriptor + * @return false if error occured + */ +bool max7219_init(max7219_display_t *disp); + +/** + * Set decode mode and clear display + * @param disp Pointer to display descriptor + * @param bcd true to set BCD decode mode, false to normal + */ +void max7219_set_decode_mode(max7219_display_t *disp, bool bcd); + +/** + * Set display brightness + * @param disp Pointer to display descriptor + * @param value Brightness value, 0..MAX7219_MAX_BRIGHTNESS + */ +void max7219_set_brightness(const max7219_display_t *disp, uint8_t value); + +/** + * Shutdown display or set it to normal mode + * @param disp Pointer to display descriptor + * @param shutdown Shutdown display if true + */ +void max7219_set_shutdown_mode(const max7219_display_t *disp, bool shutdown); + +/** + * Write data to display digit + * @param disp Pointer to display descriptor + * @param digit Digit index, 0..disp->digits - 1 + * @param val Data + * @return false if error occured + */ +bool max7219_set_digit(const max7219_display_t *disp, uint8_t digit, uint8_t val); + +/** + * Clear display + * @param disp Pointer to display descriptor + */ +void max7219_clear(const max7219_display_t *disp); + +/** + * Draw text. + * Currently only 7-segment displays supported + * @param disp Pointer to display descriptor + * @param pos Start digit + * @param s Text + */ +void max7219_draw_text(const max7219_display_t *disp, uint8_t pos, const char *s); + +#ifdef __cplusplus +extern "C" +} +#endif + +#endif /* EXTRAS_MAX7219_H_ */ diff --git a/extras/max7219/max7219_priv.h b/extras/max7219/max7219_priv.h new file mode 100644 index 0000000..f7c03cd --- /dev/null +++ b/extras/max7219/max7219_priv.h @@ -0,0 +1,36 @@ +/* + * Driver for MAX7219/MAX7221 + * Serially Interfaced, 8-Digit LED Display Drivers + * + * Part of esp-open-rtos + * Copyright (C) 2017 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#ifndef EXTRAS_MAX7219_PRIV_H_ +#define EXTRAS_MAX7219_PRIV_H_ + +static const uint8_t font_7seg[] = { + /* ' ' ! " # $ % & ' ( ) */ + 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x02, 0x4e, 0x78, + /* * + , - . / 0 1 2 3 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x30, 0x6d, 0x79, + /* 4 5 6 7 8 9 : ; < = */ + 0x33, 0x5b, 0x5f, 0x70, 0x7f, 0x7b, 0x00, 0x00, 0x0d, 0x09, + /* > ? @ A B C D E F G */ + 0x19, 0x65, 0x00, 0x77, 0x1f, 0x4e, 0x3d, 0x4f, 0x47, 0x5e, + /* H I J K L M N O P Q */ + 0x37, 0x06, 0x38, 0x57, 0x0e, 0x76, 0x15, 0x1d, 0x67, 0x73, + /* R S T U V W X Y Z [ */ + 0x05, 0x5b, 0x0f, 0x1c, 0x3e, 0x2a, 0x49, 0x3b, 0x6d, 0x4e, + /* \ ] ^ _ ` a b c d e */ + 0x00, 0x78, 0x00, 0x08, 0x02, 0x77, 0x1f, 0x4e, 0x3d, 0x4f, + /* f g h i j k l m n o */ + 0x47, 0x5e, 0x37, 0x06, 0x38, 0x57, 0x0e, 0x76, 0x15, 0x1d, + /* p q r s t u v w x y */ + 0x67, 0x73, 0x05, 0x5b, 0x0f, 0x1c, 0x3e, 0x2a, 0x49, 0x3b, + /* z { | } ~ */ + 0x6d, 0x4e, 0x06, 0x78, 0x00 +}; + + +#endif /* EXTRAS_MAX7219_PRIV_H_ */ diff --git a/extras/spiffs/mkspiffs/mkspiffs.c b/extras/spiffs/mkspiffs/mkspiffs.c index 85fb35f..9ed6a7d 100644 --- a/extras/spiffs/mkspiffs/mkspiffs.c +++ b/extras/spiffs/mkspiffs/mkspiffs.c @@ -178,7 +178,7 @@ static bool process_directory(const char *direcotry) { DIR *dp; struct dirent *ep; - char path[256]; + char path[256], *filename; dp = opendir(direcotry); if (dp != NULL) { @@ -187,12 +187,22 @@ static bool process_directory(const char *direcotry) !strcmp(ep->d_name, "..")) { continue; } + if(ep->d_type == DT_DIR) { + char *new_dir_name = malloc(strlen(direcotry) + strlen(ep->d_name) + 2); + sprintf(new_dir_name, "%s/%s", direcotry, ep->d_name); + process_directory(new_dir_name); + free(new_dir_name); + continue; + } if (ep->d_type != DT_REG) { continue; // not a regular file } sprintf(path, "%s/%s", direcotry, ep->d_name); - printf("Processing file %s\n", path); - if (!process_file(path, ep->d_name)) { + filename = strchr(path, '/'); + filename = filename ? &filename[1] : path; + + printf("Processing file source %s, dest: %s\n", path, filename); + if (!process_file(path, filename)) { printf("Error processing file\n"); break; } diff --git a/extras/ssd1306/ssd1306.c b/extras/ssd1306/ssd1306.c index dae29fa..c4f43a9 100644 --- a/extras/ssd1306/ssd1306.c +++ b/extras/ssd1306/ssd1306.c @@ -259,7 +259,7 @@ int ssd1306_load_frame_buffer(const ssd1306_t *dev, uint8_t buf[]) #if (SSD1306_I2C_SUPPORT) case SSD1306_PROTO_I2C: for (i = 0; i < len; i++) { - if(dev->screen == SH1106_SCREEN) sh1106_go_coordinate(dev,0,i/dev->width); + if(dev->screen == SH1106_SCREEN && i%dev->width == 0) sh1106_go_coordinate(dev,0,i/dev->width); i2c_start(); if (!i2c_write(dev->addr << 1)) { debug("Error while xmitting I2C slave address\n"); diff --git a/include/espressif/esp_system.h b/include/espressif/esp_system.h index c7185d4..690bc60 100644 --- a/include/espressif/esp_system.h +++ b/include/espressif/esp_system.h @@ -28,6 +28,8 @@ struct sdk_rst_info{ uint32_t rtn_addr; }; +struct netif *sdk_system_get_netif(uint32_t mode); + struct sdk_rst_info* sdk_system_get_rst_info(void); const char* sdk_system_get_sdk_version(void); diff --git a/open_esplibs/libmain/user_interface.c b/open_esplibs/libmain/user_interface.c index de38e6e..eed34a1 100644 --- a/open_esplibs/libmain/user_interface.c +++ b/open_esplibs/libmain/user_interface.c @@ -21,6 +21,8 @@ #include "esp/sar_regs.h" #include "esp/wdev_regs.h" #include "esp/uart.h" +#include "esp/rtc_regs.h" +#include "esp/iomux.h" #include "etstimer.h" #include "espressif/sdk_private.h" @@ -515,6 +517,10 @@ struct sdk_rst_info *sdk_system_get_rst_info(void) { return &sdk_rst_if; } +struct netif *sdk_system_get_netif(uint32_t mode) { + return _get_netif(mode); +} + static struct netif *_get_netif(uint32_t mode) { struct sdk_g_ic_netif_info *info; diff --git a/open_esplibs/libwpa/wpa_main.c b/open_esplibs/libwpa/wpa_main.c index 28acb45..1c70516 100644 --- a/open_esplibs/libwpa/wpa_main.c +++ b/open_esplibs/libwpa/wpa_main.c @@ -15,6 +15,7 @@ #include "esplibs/libwpa.h" #include "esplibs/libpp.h" #include "lwip/dhcp.h" +#include "esp/rtcmem_regs.h" static void wpa_callback1(struct pbuf* pb) { struct netif *netif = sdk_g_ic.v.station_netif_info->netif;