From f64935eb1dae74a5be2fedf1f4e0686dd9085637 Mon Sep 17 00:00:00 2001 From: lujji Date: Fri, 27 Jan 2017 08:38:01 +0000 Subject: [PATCH] HTTP server (#324) extras/httpd and http_server example --- examples/http_server/Makefile | 13 + examples/http_server/fsdata/fs/404.html | 25 + examples/http_server/fsdata/fs/about.html | 26 + .../http_server/fsdata/fs/css/siimple.min.css | 8 + examples/http_server/fsdata/fs/css/style.css | 73 + .../http_server/fsdata/fs/img/favicon.png | Bin 0 -> 760 bytes examples/http_server/fsdata/fs/index.ssi | 76 + examples/http_server/fsdata/fsdata.c | 1361 +++++++++ examples/http_server/fsdata/makefsdata | 114 + examples/http_server/fsdata/readme.txt | 2 + examples/http_server/http_server.c | 114 + extras/httpd/component.mk | 9 + extras/httpd/fs.c | 180 ++ extras/httpd/fs.h | 132 + extras/httpd/fsdata.h | 50 + extras/httpd/httpd.c | 2504 +++++++++++++++++ extras/httpd/httpd.h | 236 ++ extras/httpd/httpd_structs.h | 125 + extras/httpd/readme.txt | 2 + 19 files changed, 5050 insertions(+) create mode 100644 examples/http_server/Makefile create mode 100644 examples/http_server/fsdata/fs/404.html create mode 100644 examples/http_server/fsdata/fs/about.html create mode 100644 examples/http_server/fsdata/fs/css/siimple.min.css create mode 100644 examples/http_server/fsdata/fs/css/style.css create mode 100644 examples/http_server/fsdata/fs/img/favicon.png create mode 100644 examples/http_server/fsdata/fs/index.ssi create mode 100644 examples/http_server/fsdata/fsdata.c create mode 100755 examples/http_server/fsdata/makefsdata create mode 100644 examples/http_server/fsdata/readme.txt create mode 100644 examples/http_server/http_server.c create mode 100644 extras/httpd/component.mk create mode 100644 extras/httpd/fs.c create mode 100644 extras/httpd/fs.h create mode 100644 extras/httpd/fsdata.h create mode 100644 extras/httpd/httpd.c create mode 100644 extras/httpd/httpd.h create mode 100644 extras/httpd/httpd_structs.h create mode 100644 extras/httpd/readme.txt diff --git a/examples/http_server/Makefile b/examples/http_server/Makefile new file mode 100644 index 0000000..42fdd33 --- /dev/null +++ b/examples/http_server/Makefile @@ -0,0 +1,13 @@ +PROGRAM=http_server + +#ESPBAUD=921600 + +EXTRA_CFLAGS=-DLWIP_HTTPD_CGI=1 -DLWIP_HTTPD_SSI=1 -I./fsdata + +EXTRA_COMPONENTS=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..8155203 --- /dev/null +++ b/examples/http_server/fsdata/fs/404.html @@ -0,0 +1,25 @@ + + + + + + + + + + + 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..1692485 --- /dev/null +++ b/examples/http_server/fsdata/fs/about.html @@ -0,0 +1,26 @@ + + + + + + + + + + + HTTP Server + + + + +
+

About

+

This server is built on httpd from LwIP.

+

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 0000000000000000000000000000000000000000..126f24bde71131a4525e4f01194a005c1c02080b GIT binary patch literal 760 zcmVE%W_PcPXx9ZNFu_^23#{9&T!sj(X?Ov8tY$dSU1=sy~EZQ`omVL_qUWZ9%H1 zk5`YM)(#yzwFvCllzBG?EpsM!c68LUC%KEkR2PY;AFJ&U_N(lT2hnwnbTW#f`iSh> zd+yQPkDqrOOuYBuo~8puv&sHcgtl-|6c6k5Hdb5E+a1KGt3)GVGMUUl*qP}7 zqOtC=LaC1Hx|~1SgK631j2g>EjjCl+YYJMnO)R7%TuC?*V`yY_yaN!fOu4RP^yOZ< z0xGT$M1vZ$E9Z?9g0ilG9~So^G1D}OpYYoe)M&K~YZkrKyo3F;n}^Q+uj{*x<)0hWnb z{JjAA`G0ayUTP2bCnjl>sGB8L*UFUEO?HGd7IS%~uFp^?6j+$Q$8Wh`@p;`h|CNK> z!t~vfBj;o+q@OwRQW_xyUwnO&?{3VoRw(es>w_#lxKBivd}RvrKbumQ;JfcW1rjR4_TGMyY6=#?`+-zow& q&;V9};`0F8U3s?5a!Z$6kN*OAnJ3*~Q27S{0000 + + + + + + + + + + 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/fsdata.c b/examples/http_server/fsdata/fsdata.c new file mode 100644 index 0000000..90d8c06 --- /dev/null +++ b/examples/http_server/fsdata/fsdata.c @@ -0,0 +1,1361 @@ +#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, 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, 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, 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, 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, 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, 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_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, 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, 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, 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, 0x75, 0x69, 0x6C, 0x74, 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, 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, 0x0A, }; + +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_about_html[] = {{ +file_404_html, +data_about_html, data_about_html + 12, +sizeof(data_about_html) - 12, +1 +}}; + +const struct fsdata_file file_img_favicon_png[] = {{ +file_about_html, +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 6 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..fd1937c --- /dev/null +++ b/examples/http_server/http_server.c @@ -0,0 +1,114 @@ +/* + * 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 +}; + +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"; +} + +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)); +} + +void httpd_task(void *pvParameters) +{ + tCGI pCGIs[] = { + {"/gpio", (tCGIHandler) gpio_cgi_handler}, + {"/about", (tCGIHandler) about_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])); + 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", 1024, NULL, 2, NULL); +} 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..2cb7575 --- /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..c021e2f --- /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..6f6c557 --- /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..04e4bc5 --- /dev/null +++ b/extras/httpd/httpd.c @@ -0,0 +1,2504 @@ +/* + * 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 */ + +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. */ + + 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); +#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*/ + + + 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 */ + { + 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")); + } + } + + /* 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 */ + { + 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 == 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; +} + +/** + * 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 ((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); + } 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) { + /* @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..88cefce --- /dev/null +++ b/extras/httpd/httpd.h @@ -0,0 +1,236 @@ +/* + * 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" + + +/** 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 */ + +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..8cf4ed7 --- /dev/null +++ b/extras/httpd/readme.txt @@ -0,0 +1,2 @@ +Note: this module expects your project to provide "fsdata.c" created with "makefsdata" utility. +See examples/http_server.