mirror of
https://github.com/cesanta/mongoose.git
synced 2024-11-24 02:59:01 +08:00
Add SSI include file=, include virtual=
This commit is contained in:
parent
86824795a0
commit
c7e09bd7b4
4
Makefile
4
Makefile
@ -1,6 +1,6 @@
|
|||||||
SRCS = $(wildcard src/*.c)
|
SRCS = $(wildcard src/*.c)
|
||||||
HDRS = $(wildcard src/*.h)
|
HDRS = $(wildcard src/*.h)
|
||||||
DEFS ?= -DMG_MAX_HTTP_HEADERS=5 -DMG_ENABLE_LINES -DMG_ENABLE_HTTP_DEBUG_ENDPOINT=1 -DMG_ENABLE_DIRECTORY_LISTING=1
|
DEFS ?= -DMG_MAX_HTTP_HEADERS=5 -DMG_ENABLE_LINES -DMG_ENABLE_HTTP_DEBUG_ENDPOINT=1 -DMG_ENABLE_DIRECTORY_LISTING=1 -DMG_ENABLE_SSI=1
|
||||||
CFLAGS ?= -W -Wall -Werror -Isrc -I. -O0 -g $(DEFS) $(TFLAGS) $(EXTRA)
|
CFLAGS ?= -W -Wall -Werror -Isrc -I. -O0 -g $(DEFS) $(TFLAGS) $(EXTRA)
|
||||||
SSL ?= MBEDTLS
|
SSL ?= MBEDTLS
|
||||||
CDIR ?= $(realpath $(CURDIR))
|
CDIR ?= $(realpath $(CURDIR))
|
||||||
@ -99,7 +99,7 @@ mongoose.c: $(SRCS) Makefile
|
|||||||
(cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/private.h src/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
|
(cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/private.h src/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
|
||||||
|
|
||||||
mongoose.h: $(HDRS) Makefile
|
mongoose.h: $(HDRS) Makefile
|
||||||
(cat src/license.h src/version.h ; cat src/arch.h src/arch_*.h src/config.h src/str.h src/log.h src/timer.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/tls.h src/ws.h src/sntp.h src/mqtt.h src/dns.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,')> $@
|
(cat src/license.h src/version.h ; cat src/arch.h src/arch_*.h src/config.h src/str.h src/log.h src/timer.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/ws.h src/sntp.h src/mqtt.h src/dns.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,')> $@
|
||||||
|
|
||||||
clean: EXAMPLE_TARGET = clean
|
clean: EXAMPLE_TARGET = clean
|
||||||
clean: ex
|
clean: ex
|
||||||
|
@ -223,6 +223,7 @@ Here is a list of build constants and their default values:
|
|||||||
|`MG_ENABLE_DIRECTORY_LISTING` | 0 | Enable directory listing for HTTP server |
|
|`MG_ENABLE_DIRECTORY_LISTING` | 0 | Enable directory listing for HTTP server |
|
||||||
|`MG_ENABLE_HTTP_DEBUG_ENDPOINT` | 0 | Enable `/debug/info` debug URI |
|
|`MG_ENABLE_HTTP_DEBUG_ENDPOINT` | 0 | Enable `/debug/info` debug URI |
|
||||||
|`MG_ENABLE_SOCKETPAIR` | 0 | Enable `mg_socketpair()` for multi-threading |
|
|`MG_ENABLE_SOCKETPAIR` | 0 | Enable `mg_socketpair()` for multi-threading |
|
||||||
|
|`MG_ENABLE_SSI` | 0 | Enable serving SSI files by `mg_http_serve_dir()` |
|
||||||
|`MG_IO_SIZE` | 512 | Granularity of the send/recv IO buffer growth |
|
|`MG_IO_SIZE` | 512 | Granularity of the send/recv IO buffer growth |
|
||||||
|`MG_MAX_RECV_BUF_SIZE` | (3 * 1024 * 1024) | Maximum recv buffer size |
|
|`MG_MAX_RECV_BUF_SIZE` | (3 * 1024 * 1024) | Maximum recv buffer size |
|
||||||
|`MG_MAX_HTTP_HEADERS` | 40 | Maximum number of HTTP headers |
|
|`MG_MAX_HTTP_HEADERS` | 40 | Maximum number of HTTP headers |
|
||||||
@ -242,7 +243,8 @@ static const char *s_web_root_dir = ".";
|
|||||||
static const char *s_listening_address = "http://localhost:8000";
|
static const char *s_listening_address = "http://localhost:8000";
|
||||||
|
|
||||||
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) mg_http_serve_dir(c, ev_data, s_web_root_dir);
|
struct mg_http_serve_opts opts = {.root_dir = s_web_root_dir};
|
||||||
|
if (ev == MG_EV_HTTP_MSG) mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
@ -628,11 +630,16 @@ Write a chunk of data in chunked encoding format.
|
|||||||
### mg\_serve\_dir()
|
### mg\_serve\_dir()
|
||||||
|
|
||||||
```c
|
```c
|
||||||
|
struct mg_http_serve_opts {
|
||||||
|
const char *root_dir; // Web root directory, must be non-NULL
|
||||||
|
const char *ssi_pattern; // SSI filename pattern, e.g. #.shtml
|
||||||
|
};
|
||||||
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
|
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
|
||||||
const char *path);
|
const struct mg_http_serve_opts *opts);
|
||||||
```
|
```
|
||||||
|
|
||||||
Serve static files using `path` as a root directory.
|
Serve static files according to the given options. Note that in order to
|
||||||
|
enable SSI, set a `-DMG_ENABLE_SSI=1` build flag.
|
||||||
|
|
||||||
|
|
||||||
### mg\_serve\_file()
|
### mg\_serve\_file()
|
||||||
|
@ -129,7 +129,8 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
u->token);
|
u->token);
|
||||||
mg_http_printf_chunk(c, "");
|
mg_http_printf_chunk(c, "");
|
||||||
} else {
|
} else {
|
||||||
mg_http_serve_dir(c, ev_data, "web_root");
|
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
||||||
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,7 +147,7 @@ static void broadcast_mjpeg_frame(struct mg_mgr *mgr) {
|
|||||||
char *data = mg_file_read(path); // Read next file
|
char *data = mg_file_read(path); // Read next file
|
||||||
struct mg_connection *c;
|
struct mg_connection *c;
|
||||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||||
if (c->label[0] != 'S') continue; // Skip non-stream connections
|
if (c->label[0] != 'S') continue; // Skip non-stream connections
|
||||||
if (data == NULL || size == 0) continue; // Skip on file read error
|
if (data == NULL || size == 0) continue; // Skip on file read error
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
"--foo\r\nContent-Type: image/jpeg\r\n"
|
"--foo\r\nContent-Type: image/jpeg\r\n"
|
||||||
|
@ -66,7 +66,8 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
c->label[0] = 'W'; // Mark ourselves as a config watcher
|
c->label[0] = 'W'; // Mark ourselves as a config watcher
|
||||||
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
||||||
} else {
|
} else {
|
||||||
mg_http_serve_dir(c, ev_data, "web_root");
|
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
||||||
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
PROG ?= example
|
PROG ?= example
|
||||||
ROOT ?= $(realpath $(CURDIR)/../..)
|
ROOT ?= $(realpath $(CURDIR)/../..)
|
||||||
DEFS ?= -DMG_ENABLE_LINES=1 -DMG_ENABLE_DIRECTORY_LISTING=1
|
DEFS ?= -DMG_ENABLE_LINES=1 -DMG_ENABLE_DIRECTORY_LISTING=1 -DMG_ENABLE_SSI=1
|
||||||
CFLAGS ?= -I../.. -W -Wall -DMG_ENABLE_IPV6=1 $(DEFS) $(EXTRA)
|
CFLAGS ?= -I../.. -W -Wall -DMG_ENABLE_IPV6=1 $(DEFS) $(EXTRA)
|
||||||
VCFLAGS = /nologo /W3 /O2 /I../.. $(DEFS) $(EXTRA)
|
VCFLAGS = /nologo /W3 /O2 /I../.. $(DEFS) $(EXTRA)
|
||||||
VC98 = docker run --rm -e WINEDEBUG=-all -v $(ROOT):$(ROOT) -w $(CURDIR) docker.io/mdashnet/vc98
|
VC98 = docker run --rm -e WINEDEBUG=-all -v $(ROOT):$(ROOT) -w $(CURDIR) docker.io/mdashnet/vc98
|
||||||
|
@ -8,11 +8,13 @@ static const char *s_web_root_dir = ".";
|
|||||||
static const char *s_listening_address = "http://localhost:8000";
|
static const char *s_listening_address = "http://localhost:8000";
|
||||||
static const char *s_enable_hexdump = "no";
|
static const char *s_enable_hexdump = "no";
|
||||||
static const char *s_rewrites = "";
|
static const char *s_rewrites = "";
|
||||||
|
static const char *s_ssi_pattern = "#.shtml";
|
||||||
|
|
||||||
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
// struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_serve_opts opts = {.root_dir = s_web_root_dir,
|
||||||
mg_http_serve_dir(c, ev_data, s_web_root_dir);
|
.ssi_pattern = s_ssi_pattern};
|
||||||
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
(void) fn_data;
|
||||||
}
|
}
|
||||||
@ -23,11 +25,12 @@ static void usage(const char *prog) {
|
|||||||
"\nUsage: %s OPTIONS\n"
|
"\nUsage: %s OPTIONS\n"
|
||||||
" -D LEVEL - debug level, from 0 to 4, default: '%s'\n"
|
" -D LEVEL - debug level, from 0 to 4, default: '%s'\n"
|
||||||
" -H yes|no - enable traffic hexdump, default: '%s'\n"
|
" -H yes|no - enable traffic hexdump, default: '%s'\n"
|
||||||
|
" -S GLOB - glob pattern for SSI files, default: '%s'\n"
|
||||||
" -d DIR - directory to serve, default: '%s'\n"
|
" -d DIR - directory to serve, default: '%s'\n"
|
||||||
" -l ADDR - listening address, default: '%s'\n"
|
" -l ADDR - listening address, default: '%s'\n"
|
||||||
" -r LIST - list of URI=DIR,... URI rewrites, default: '%s'\n",
|
" -r LIST - list of URI=DIR,... URI rewrites, default: '%s'\n",
|
||||||
MG_VERSION, prog, s_debug_level, s_enable_hexdump, s_web_root_dir,
|
MG_VERSION, prog, s_debug_level, s_enable_hexdump, s_ssi_pattern,
|
||||||
s_listening_address, s_rewrites);
|
s_web_root_dir, s_listening_address, s_rewrites);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +47,8 @@ int main(int argc, char *argv[]) {
|
|||||||
s_debug_level = argv[++i];
|
s_debug_level = argv[++i];
|
||||||
} else if (strcmp(argv[i], "-H") == 0) {
|
} else if (strcmp(argv[i], "-H") == 0) {
|
||||||
s_enable_hexdump = argv[++i];
|
s_enable_hexdump = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-S") == 0) {
|
||||||
|
s_ssi_pattern = argv[++i];
|
||||||
} else if (strcmp(argv[i], "-l") == 0) {
|
} else if (strcmp(argv[i], "-l") == 0) {
|
||||||
s_listening_address = argv[++i];
|
s_listening_address = argv[++i];
|
||||||
} else if (strcmp(argv[i], "-r") == 0) {
|
} else if (strcmp(argv[i], "-r") == 0) {
|
||||||
|
@ -12,7 +12,8 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
if (mg_http_match_uri(hm, "/upload")) {
|
if (mg_http_match_uri(hm, "/upload")) {
|
||||||
mg_http_upload(c, hm, "/tmp");
|
mg_http_upload(c, hm, "/tmp");
|
||||||
} else {
|
} else {
|
||||||
mg_http_serve_dir(c, ev_data, "web_root");
|
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
||||||
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
mg_http_reply(c, 200, "", "{\"result\": \"%.*s\"}\n", (int) hm->uri.len,
|
mg_http_reply(c, 200, "", "{\"result\": \"%.*s\"}\n", (int) hm->uri.len,
|
||||||
hm->uri.ptr);
|
hm->uri.ptr);
|
||||||
} else {
|
} else {
|
||||||
mg_http_serve_dir(c, ev_data, s_web_directory); // Serve static files
|
struct mg_http_serve_opts opts = {.root_dir = s_web_directory};
|
||||||
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
(void) fn_data;
|
||||||
|
@ -16,7 +16,8 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
c->label[0] = 'L'; // Mark that connection as live log listener
|
c->label[0] = 'L'; // Mark that connection as live log listener
|
||||||
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
||||||
} else {
|
} else {
|
||||||
mg_http_serve_dir(c, ev_data, "web_root");
|
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
||||||
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,8 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
u->token);
|
u->token);
|
||||||
mg_http_printf_chunk(c, "");
|
mg_http_printf_chunk(c, "");
|
||||||
} else {
|
} else {
|
||||||
mg_http_serve_dir(c, ev_data, "web_root");
|
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
||||||
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,8 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
"Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
|
"Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
|
||||||
"Content-Type: multipart/x-mixed-replace; boundary=--foo\r\n\r\n");
|
"Content-Type: multipart/x-mixed-replace; boundary=--foo\r\n\r\n");
|
||||||
} else {
|
} else {
|
||||||
mg_http_serve_dir(c, ev_data, "web_root");
|
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
||||||
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,7 +36,7 @@ static void broadcast_mjpeg_frame(struct mg_mgr *mgr) {
|
|||||||
char *data = mg_file_read(path); // Read next file
|
char *data = mg_file_read(path); // Read next file
|
||||||
struct mg_connection *c;
|
struct mg_connection *c;
|
||||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||||
if (c->label[0] != 'S') continue; // Skip non-stream connections
|
if (c->label[0] != 'S') continue; // Skip non-stream connections
|
||||||
if (data == NULL || size == 0) continue; // Skip on file read error
|
if (data == NULL || size == 0) continue; // Skip on file read error
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
"--foo\r\nContent-Type: image/jpeg\r\n"
|
"--foo\r\nContent-Type: image/jpeg\r\n"
|
||||||
|
@ -27,7 +27,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
mg_http_reply(c, 200, "", "{\"result\": %d}\n", 123);
|
mg_http_reply(c, 200, "", "{\"result\": %d}\n", 123);
|
||||||
} else {
|
} else {
|
||||||
// Serve static files
|
// Serve static files
|
||||||
mg_http_serve_dir(c, ev_data, s_web_directory);
|
struct mg_http_serve_opts opts = {.root_dir = s_web_directory};
|
||||||
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
} else if (ev == MG_EV_WS_MSG) {
|
} else if (ev == MG_EV_WS_MSG) {
|
||||||
// Got websocket frame. Received data is wm->data. Echo it back!
|
// Got websocket frame. Received data is wm->data. Echo it back!
|
||||||
|
106
mongoose.c
106
mongoose.c
@ -422,6 +422,7 @@ void mg_error(struct mg_connection *c, const char *fmt, ...) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct http_data {
|
struct http_data {
|
||||||
void *old_pfn_data; // Previous pfn_data
|
void *old_pfn_data; // Previous pfn_data
|
||||||
FILE *fp; // For static file serving
|
FILE *fp; // For static file serving
|
||||||
@ -1009,7 +1010,7 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
const struct mg_http_serve_opts *opts) {
|
struct mg_http_serve_opts *opts) {
|
||||||
char path[PATH_MAX + 2], root[sizeof(path) - 2], real[sizeof(path) - 2];
|
char path[PATH_MAX + 2], root[sizeof(path) - 2], real[sizeof(path) - 2];
|
||||||
path[0] = root[0] = real[0] = '\0';
|
path[0] = root[0] = real[0] = '\0';
|
||||||
if (realpath(opts->root_dir, root) == NULL)
|
if (realpath(opts->root_dir, root) == NULL)
|
||||||
@ -1028,12 +1029,22 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
// LOG(LL_INFO, ("[%s] [%s] [%s] [%s]", dir, root, path, real));
|
// LOG(LL_INFO, ("[%s] [%s] [%s] [%s]", dir, root, path, real));
|
||||||
if (mg_is_dir(real)) {
|
if (mg_is_dir(real)) {
|
||||||
strncat(real, "/index.html", sizeof(real) - strlen(real) - 1);
|
strncat(real, "/index.html", sizeof(real) - strlen(real) - 1);
|
||||||
|
real[sizeof(real) - 1] = '\0';
|
||||||
is_index = true;
|
is_index = true;
|
||||||
}
|
}
|
||||||
if (strlen(real) < strlen(root) || memcmp(real, root, strlen(root)) != 0) {
|
if (strlen(real) < strlen(root) || memcmp(real, root, strlen(root)) != 0) {
|
||||||
mg_http_reply(c, 404, "", "Not found %.*s\n", hm->uri.len, hm->uri.ptr);
|
mg_http_reply(c, 404, "", "Not found %.*s\n", hm->uri.len, hm->uri.ptr);
|
||||||
} else {
|
} else {
|
||||||
FILE *fp = fopen(real, "r");
|
FILE *fp = fopen(real, "r");
|
||||||
|
#if MG_ENABLE_SSI
|
||||||
|
if (is_index && fp == NULL) {
|
||||||
|
char *p = real + strlen(real);
|
||||||
|
while (p > real && p[-1] != '/') p--;
|
||||||
|
strncpy(p, "index.shtml", &real[sizeof(real)] - p - 2);
|
||||||
|
real[sizeof(real) - 1] = '\0';
|
||||||
|
fp = fopen(real, "r");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
|
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
|
||||||
snprintf(c->label, sizeof(c->label) - 1, "<-F %s", real);
|
snprintf(c->label, sizeof(c->label) - 1, "<-F %s", real);
|
||||||
#endif
|
#endif
|
||||||
@ -1042,6 +1053,12 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
listdir(c, hm, real);
|
listdir(c, hm, real);
|
||||||
#else
|
#else
|
||||||
mg_http_reply(c, 403, "", "%s", "Directory listing not supported");
|
mg_http_reply(c, 403, "", "%s", "Directory listing not supported");
|
||||||
|
#endif
|
||||||
|
#if MG_ENABLE_SSI
|
||||||
|
} else if (opts->ssi_pattern != NULL &&
|
||||||
|
mg_globmatch(opts->ssi_pattern, strlen(opts->ssi_pattern),
|
||||||
|
real, strlen(real))) {
|
||||||
|
mg_http_serve_ssi(c, root, real);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
mg_http_serve_file(c, hm, real, guess_content_type(real));
|
mg_http_serve_file(c, hm, real, guess_content_type(real));
|
||||||
@ -2984,6 +3001,93 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef MG_ENABLE_LINES
|
||||||
|
#line 1 "src/ssi.c"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef MG_MAX_SSI_DEPTH
|
||||||
|
#define MG_MAX_SSI_DEPTH 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MG_ENABLE_SSI
|
||||||
|
static char *mg_ssi(const char *path, const char *root, int depth) {
|
||||||
|
struct mg_iobuf b = {NULL, 0, 0};
|
||||||
|
FILE *fp = fopen(path, "rb");
|
||||||
|
if (fp != NULL) {
|
||||||
|
char buf[BUFSIZ], arg[sizeof(buf)];
|
||||||
|
int ch, intag = 0;
|
||||||
|
size_t len = 0, align = MG_IO_SIZE;
|
||||||
|
while ((ch = fgetc(fp)) != EOF) {
|
||||||
|
if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') {
|
||||||
|
buf[len++] = ch & 0xff;
|
||||||
|
if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) {
|
||||||
|
char tmp[PATH_MAX], *p = (char *) path + strlen(path), *data;
|
||||||
|
while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
|
||||||
|
snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
|
||||||
|
if (depth < MG_MAX_SSI_DEPTH &&
|
||||||
|
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
|
||||||
|
mg_iobuf_append(&b, data, strlen(data), align);
|
||||||
|
free(data);
|
||||||
|
} else {
|
||||||
|
LOG(LL_ERROR, ("%s: file=%s error or too deep", path, arg));
|
||||||
|
}
|
||||||
|
} else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) {
|
||||||
|
char tmp[PATH_MAX], *data;
|
||||||
|
snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
|
||||||
|
if (depth < MG_MAX_SSI_DEPTH &&
|
||||||
|
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
|
||||||
|
mg_iobuf_append(&b, data, strlen(data), align);
|
||||||
|
free(data);
|
||||||
|
} else {
|
||||||
|
LOG(LL_ERROR, ("%s: virtual=%s error or too deep", path, arg));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unknown SSI tag
|
||||||
|
LOG(LL_INFO, ("Unknown SSI tag: %.*s", (int) len, buf));
|
||||||
|
mg_iobuf_append(&b, buf, len, align);
|
||||||
|
}
|
||||||
|
intag = 0;
|
||||||
|
len = 0;
|
||||||
|
} else if (ch == '<') {
|
||||||
|
intag = 1;
|
||||||
|
if (len > 0) mg_iobuf_append(&b, buf, len, align);
|
||||||
|
len = 0;
|
||||||
|
buf[len++] = ch & 0xff;
|
||||||
|
} else if (intag) {
|
||||||
|
if (len == 5 && strncmp(buf, "<!--#", 5) != 0) {
|
||||||
|
intag = 0;
|
||||||
|
} else if (len >= sizeof(buf) - 2) {
|
||||||
|
LOG(LL_ERROR, ("%s: SSI tag is too large", path));
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
buf[len++] = ch & 0xff;
|
||||||
|
} else {
|
||||||
|
buf[len++] = ch & 0xff;
|
||||||
|
if (len >= sizeof(buf)) {
|
||||||
|
mg_iobuf_append(&b, buf, len, align);
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (len > 0) mg_iobuf_append(&b, buf, len, align);
|
||||||
|
if (b.len > 0) mg_iobuf_append(&b, "", 1, align); // nul-terminate
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
(void) depth;
|
||||||
|
(void) root;
|
||||||
|
return (char *) b.buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
|
||||||
|
const char *fullpath) {
|
||||||
|
char *data = mg_ssi(fullpath, root, 0);
|
||||||
|
mg_http_reply(c, 200, "", "%s", data == NULL ? "" : data);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef MG_ENABLE_LINES
|
#ifdef MG_ENABLE_LINES
|
||||||
#line 1 "src/str.c"
|
#line 1 "src/str.c"
|
||||||
#endif
|
#endif
|
||||||
|
14
mongoose.h
14
mongoose.h
@ -296,6 +296,10 @@ typedef int socklen_t;
|
|||||||
#define MG_ENABLE_FS 1
|
#define MG_ENABLE_FS 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_SSI
|
||||||
|
#define MG_ENABLE_SSI 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef MG_ENABLE_IPV6
|
#ifndef MG_ENABLE_IPV6
|
||||||
#define MG_ENABLE_IPV6 0
|
#define MG_ENABLE_IPV6 0
|
||||||
#endif
|
#endif
|
||||||
@ -667,8 +671,8 @@ struct mg_http_message {
|
|||||||
|
|
||||||
// Parameter for mg_http_serve_dir()
|
// Parameter for mg_http_serve_dir()
|
||||||
struct mg_http_serve_opts {
|
struct mg_http_serve_opts {
|
||||||
const char *root_dir;
|
const char *root_dir; // Web root directory, must be non-NULL
|
||||||
const char *ssi_pattern;
|
const char *ssi_pattern; // SSI filename pattern, e.g. #.shtml
|
||||||
};
|
};
|
||||||
|
|
||||||
int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
|
int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
|
||||||
@ -680,7 +684,7 @@ struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url,
|
|||||||
struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
|
struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
|
||||||
mg_event_handler_t fn, void *fn_data);
|
mg_event_handler_t fn, void *fn_data);
|
||||||
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
|
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
|
||||||
const struct mg_http_serve_opts *);
|
struct mg_http_serve_opts *);
|
||||||
void mg_http_serve_file(struct mg_connection *, struct mg_http_message *,
|
void mg_http_serve_file(struct mg_connection *, struct mg_http_message *,
|
||||||
const char *, const char *mime);
|
const char *, const char *mime);
|
||||||
void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
|
void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
|
||||||
@ -696,6 +700,10 @@ int mg_http_upload(struct mg_connection *, struct mg_http_message *hm,
|
|||||||
void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
|
void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
|
||||||
|
|
||||||
|
|
||||||
|
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
|
||||||
|
const char *fullpath);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct mg_tls_opts {
|
struct mg_tls_opts {
|
||||||
const char *ca; // CA certificate file. For both listeners and clients
|
const char *ca; // CA certificate file. For both listeners and clients
|
||||||
|
@ -22,6 +22,10 @@
|
|||||||
#define MG_ENABLE_FS 1
|
#define MG_ENABLE_FS 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_ENABLE_SSI
|
||||||
|
#define MG_ENABLE_SSI 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef MG_ENABLE_IPV6
|
#ifndef MG_ENABLE_IPV6
|
||||||
#define MG_ENABLE_IPV6 0
|
#define MG_ENABLE_IPV6 0
|
||||||
#endif
|
#endif
|
||||||
|
19
src/http.c
19
src/http.c
@ -4,6 +4,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
|
#include "ssi.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "ws.h"
|
#include "ws.h"
|
||||||
@ -595,7 +596,7 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
const struct mg_http_serve_opts *opts) {
|
struct mg_http_serve_opts *opts) {
|
||||||
char path[PATH_MAX + 2], root[sizeof(path) - 2], real[sizeof(path) - 2];
|
char path[PATH_MAX + 2], root[sizeof(path) - 2], real[sizeof(path) - 2];
|
||||||
path[0] = root[0] = real[0] = '\0';
|
path[0] = root[0] = real[0] = '\0';
|
||||||
if (realpath(opts->root_dir, root) == NULL)
|
if (realpath(opts->root_dir, root) == NULL)
|
||||||
@ -614,12 +615,22 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
// LOG(LL_INFO, ("[%s] [%s] [%s] [%s]", dir, root, path, real));
|
// LOG(LL_INFO, ("[%s] [%s] [%s] [%s]", dir, root, path, real));
|
||||||
if (mg_is_dir(real)) {
|
if (mg_is_dir(real)) {
|
||||||
strncat(real, "/index.html", sizeof(real) - strlen(real) - 1);
|
strncat(real, "/index.html", sizeof(real) - strlen(real) - 1);
|
||||||
|
real[sizeof(real) - 1] = '\0';
|
||||||
is_index = true;
|
is_index = true;
|
||||||
}
|
}
|
||||||
if (strlen(real) < strlen(root) || memcmp(real, root, strlen(root)) != 0) {
|
if (strlen(real) < strlen(root) || memcmp(real, root, strlen(root)) != 0) {
|
||||||
mg_http_reply(c, 404, "", "Not found %.*s\n", hm->uri.len, hm->uri.ptr);
|
mg_http_reply(c, 404, "", "Not found %.*s\n", hm->uri.len, hm->uri.ptr);
|
||||||
} else {
|
} else {
|
||||||
FILE *fp = fopen(real, "r");
|
FILE *fp = fopen(real, "r");
|
||||||
|
#if MG_ENABLE_SSI
|
||||||
|
if (is_index && fp == NULL) {
|
||||||
|
char *p = real + strlen(real);
|
||||||
|
while (p > real && p[-1] != '/') p--;
|
||||||
|
strncpy(p, "index.shtml", &real[sizeof(real)] - p - 2);
|
||||||
|
real[sizeof(real) - 1] = '\0';
|
||||||
|
fp = fopen(real, "r");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
|
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
|
||||||
snprintf(c->label, sizeof(c->label) - 1, "<-F %s", real);
|
snprintf(c->label, sizeof(c->label) - 1, "<-F %s", real);
|
||||||
#endif
|
#endif
|
||||||
@ -628,6 +639,12 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
listdir(c, hm, real);
|
listdir(c, hm, real);
|
||||||
#else
|
#else
|
||||||
mg_http_reply(c, 403, "", "%s", "Directory listing not supported");
|
mg_http_reply(c, 403, "", "%s", "Directory listing not supported");
|
||||||
|
#endif
|
||||||
|
#if MG_ENABLE_SSI
|
||||||
|
} else if (opts->ssi_pattern != NULL &&
|
||||||
|
mg_globmatch(opts->ssi_pattern, strlen(opts->ssi_pattern),
|
||||||
|
real, strlen(real))) {
|
||||||
|
mg_http_serve_ssi(c, root, real);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
mg_http_serve_file(c, hm, real, guess_content_type(real));
|
mg_http_serve_file(c, hm, real, guess_content_type(real));
|
||||||
|
@ -21,8 +21,8 @@ struct mg_http_message {
|
|||||||
|
|
||||||
// Parameter for mg_http_serve_dir()
|
// Parameter for mg_http_serve_dir()
|
||||||
struct mg_http_serve_opts {
|
struct mg_http_serve_opts {
|
||||||
const char *root_dir;
|
const char *root_dir; // Web root directory, must be non-NULL
|
||||||
const char *ssi_pattern;
|
const char *ssi_pattern; // SSI filename pattern, e.g. #.shtml
|
||||||
};
|
};
|
||||||
|
|
||||||
int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
|
int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
|
||||||
@ -34,7 +34,7 @@ struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url,
|
|||||||
struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
|
struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
|
||||||
mg_event_handler_t fn, void *fn_data);
|
mg_event_handler_t fn, void *fn_data);
|
||||||
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
|
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
|
||||||
const struct mg_http_serve_opts *);
|
struct mg_http_serve_opts *);
|
||||||
void mg_http_serve_file(struct mg_connection *, struct mg_http_message *,
|
void mg_http_serve_file(struct mg_connection *, struct mg_http_message *,
|
||||||
const char *, const char *mime);
|
const char *, const char *mime);
|
||||||
void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
|
void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
|
||||||
|
83
src/ssi.c
Normal file
83
src/ssi.c
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#include "log.h"
|
||||||
|
#include "ssi.h"
|
||||||
|
|
||||||
|
#ifndef MG_MAX_SSI_DEPTH
|
||||||
|
#define MG_MAX_SSI_DEPTH 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MG_ENABLE_SSI
|
||||||
|
static char *mg_ssi(const char *path, const char *root, int depth) {
|
||||||
|
struct mg_iobuf b = {NULL, 0, 0};
|
||||||
|
FILE *fp = fopen(path, "rb");
|
||||||
|
if (fp != NULL) {
|
||||||
|
char buf[BUFSIZ], arg[sizeof(buf)];
|
||||||
|
int ch, intag = 0;
|
||||||
|
size_t len = 0, align = MG_IO_SIZE;
|
||||||
|
while ((ch = fgetc(fp)) != EOF) {
|
||||||
|
if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') {
|
||||||
|
buf[len++] = ch & 0xff;
|
||||||
|
if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) {
|
||||||
|
char tmp[PATH_MAX], *p = (char *) path + strlen(path), *data;
|
||||||
|
while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
|
||||||
|
snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
|
||||||
|
if (depth < MG_MAX_SSI_DEPTH &&
|
||||||
|
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
|
||||||
|
mg_iobuf_append(&b, data, strlen(data), align);
|
||||||
|
free(data);
|
||||||
|
} else {
|
||||||
|
LOG(LL_ERROR, ("%s: file=%s error or too deep", path, arg));
|
||||||
|
}
|
||||||
|
} else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) {
|
||||||
|
char tmp[PATH_MAX], *data;
|
||||||
|
snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
|
||||||
|
if (depth < MG_MAX_SSI_DEPTH &&
|
||||||
|
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
|
||||||
|
mg_iobuf_append(&b, data, strlen(data), align);
|
||||||
|
free(data);
|
||||||
|
} else {
|
||||||
|
LOG(LL_ERROR, ("%s: virtual=%s error or too deep", path, arg));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unknown SSI tag
|
||||||
|
LOG(LL_INFO, ("Unknown SSI tag: %.*s", (int) len, buf));
|
||||||
|
mg_iobuf_append(&b, buf, len, align);
|
||||||
|
}
|
||||||
|
intag = 0;
|
||||||
|
len = 0;
|
||||||
|
} else if (ch == '<') {
|
||||||
|
intag = 1;
|
||||||
|
if (len > 0) mg_iobuf_append(&b, buf, len, align);
|
||||||
|
len = 0;
|
||||||
|
buf[len++] = ch & 0xff;
|
||||||
|
} else if (intag) {
|
||||||
|
if (len == 5 && strncmp(buf, "<!--#", 5) != 0) {
|
||||||
|
intag = 0;
|
||||||
|
} else if (len >= sizeof(buf) - 2) {
|
||||||
|
LOG(LL_ERROR, ("%s: SSI tag is too large", path));
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
buf[len++] = ch & 0xff;
|
||||||
|
} else {
|
||||||
|
buf[len++] = ch & 0xff;
|
||||||
|
if (len >= sizeof(buf)) {
|
||||||
|
mg_iobuf_append(&b, buf, len, align);
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (len > 0) mg_iobuf_append(&b, buf, len, align);
|
||||||
|
if (b.len > 0) mg_iobuf_append(&b, "", 1, align); // nul-terminate
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
(void) depth;
|
||||||
|
(void) root;
|
||||||
|
return (char *) b.buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
|
||||||
|
const char *fullpath) {
|
||||||
|
char *data = mg_ssi(fullpath, root, 0);
|
||||||
|
mg_http_reply(c, 200, "", "%s", data == NULL ? "" : data);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
#endif
|
4
src/ssi.h
Normal file
4
src/ssi.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "http.h"
|
||||||
|
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
|
||||||
|
const char *fullpath);
|
1
test/data/ssi/f1.txt
Normal file
1
test/data/ssi/f1.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
this is f1
|
3
test/data/ssi/index.shtml
Normal file
3
test/data/ssi/index.shtml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
this is index
|
||||||
|
<!--#include file="nested.shtml" -->
|
||||||
|
<!--#include file="recurse.shtml" -->
|
3
test/data/ssi/nested.shtml
Normal file
3
test/data/ssi/nested.shtml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
this is nested
|
||||||
|
<!--#include virtual="/notexist.txt"-->
|
||||||
|
<!--#include virtual="/ssi/f1.txt"-->
|
2
test/data/ssi/recurse.shtml
Normal file
2
test/data/ssi/recurse.shtml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<!--#include file="recurse.shtml"-->
|
||||||
|
recurse
|
@ -349,7 +349,7 @@ static void eh1(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
struct mg_http_serve_opts opts = {".", NULL};
|
struct mg_http_serve_opts opts = {".", NULL};
|
||||||
mg_http_serve_dir(c, hm, &opts);
|
mg_http_serve_dir(c, hm, &opts);
|
||||||
} else {
|
} else {
|
||||||
struct mg_http_serve_opts opts = {"./test/data", NULL};
|
struct mg_http_serve_opts opts = {"./test/data", "#.shtml"};
|
||||||
mg_http_serve_dir(c, hm, &opts);
|
mg_http_serve_dir(c, hm, &opts);
|
||||||
}
|
}
|
||||||
} else if (ev == MG_EV_WS_MSG) {
|
} else if (ev == MG_EV_WS_MSG) {
|
||||||
@ -477,6 +477,17 @@ static void test_http_server(void) {
|
|||||||
"Content-Length: 4\r\n\r\nkuku") == 200);
|
"Content-Length: 4\r\n\r\nkuku") == 200);
|
||||||
ASSERT(cmpbody(buf, "kuku") == 0);
|
ASSERT(cmpbody(buf, "kuku") == 0);
|
||||||
|
|
||||||
|
ASSERT(fetch(&mgr, buf, url, "GET /ssi HTTP/1.1\r\n\r\n") == 200);
|
||||||
|
ASSERT(cmpbody(buf,
|
||||||
|
"this is index\n"
|
||||||
|
"this is nested\n\n"
|
||||||
|
"this is f1\n\n\n\n"
|
||||||
|
"recurse\n\n"
|
||||||
|
"recurse\n\n"
|
||||||
|
"recurse\n\n"
|
||||||
|
"recurse\n\n"
|
||||||
|
"recurse\n\n") == 0);
|
||||||
|
|
||||||
ASSERT(fetch(&mgr, buf, url, "GET /badroot HTTP/1.0\r\n\n") == 400);
|
ASSERT(fetch(&mgr, buf, url, "GET /badroot HTTP/1.0\r\n\n") == 400);
|
||||||
#if MG_ARCH == MG_ARCH_WIN32
|
#if MG_ARCH == MG_ARCH_WIN32
|
||||||
ASSERT(cmpbody(buf, "Bad web root [Z:\\BAAADDD!]\n") == 0);
|
ASSERT(cmpbody(buf, "Bad web root [Z:\\BAAADDD!]\n") == 0);
|
||||||
|
Loading…
Reference in New Issue
Block a user