mirror of
https://github.com/lammertb/libhttp.git
synced 2026-01-27 08:02:47 +03:00
Moved SSI functions to own file
This commit is contained in:
1
Makefile
1
Makefile
@@ -95,6 +95,7 @@ LIB_SOURCES = src/libhttp.c \
|
||||
src/httplib_set_throttle.c \
|
||||
src/httplib_set_uid_option.c \
|
||||
src/httplib_set_websocket_handler.c \
|
||||
src/httplib_ssi.c \
|
||||
src/httplib_ssl_error.c \
|
||||
src/httplib_ssl_get_client_cert_info.c \
|
||||
src/httplib_ssl_get_protocol.c \
|
||||
|
||||
239
src/httplib_ssi.c
Normal file
239
src/httplib_ssi.c
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Lammert Bies
|
||||
* Copyright (c) 2013-2016 the Civetweb developers
|
||||
* Copyright (c) 2004-2013 Sergey Lyubka
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "libhttp-private.h"
|
||||
|
||||
|
||||
|
||||
static void send_ssi_file(struct mg_connection *, const char *, struct file *, int);
|
||||
|
||||
|
||||
static void do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, int include_level) {
|
||||
|
||||
char file_name[MG_BUF_LEN];
|
||||
char path[512];
|
||||
char *p;
|
||||
struct file file = STRUCT_FILE_INITIALIZER;
|
||||
size_t len;
|
||||
int truncated = 0;
|
||||
|
||||
if ( conn == NULL ) return;
|
||||
|
||||
/* sscanf() is safe here, since send_ssi_file() also uses buffer
|
||||
* of size MG_BUF_LEN to get the tag. So strlen(tag) is
|
||||
* always < MG_BUF_LEN. */
|
||||
if (sscanf(tag, " virtual=\"%511[^\"]\"", file_name) == 1) {
|
||||
/* File name is relative to the webserver root */
|
||||
file_name[511] = 0;
|
||||
XX_httplib_snprintf(conn, &truncated, path, sizeof(path), "%s/%s", conn->ctx->config[DOCUMENT_ROOT], file_name);
|
||||
|
||||
} else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) {
|
||||
/* File name is relative to the webserver working directory
|
||||
* or it is absolute system path */
|
||||
file_name[511] = 0;
|
||||
XX_httplib_snprintf(conn, &truncated, path, sizeof(path), "%s", file_name);
|
||||
|
||||
} else if (sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1
|
||||
|| sscanf(tag, " \"%511[^\"]\"", file_name) == 1) {
|
||||
/* File name is relative to the currect document */
|
||||
file_name[511] = 0;
|
||||
XX_httplib_snprintf(conn, &truncated, path, sizeof(path), "%s", ssi);
|
||||
|
||||
if (!truncated) {
|
||||
if ((p = strrchr(path, '/')) != NULL) p[1] = '\0';
|
||||
len = strlen(path);
|
||||
XX_httplib_snprintf(conn, &truncated, path + len, sizeof(path) - len, "%s", file_name);
|
||||
}
|
||||
|
||||
} else {
|
||||
mg_cry(conn, "Bad SSI #include: [%s]", tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (truncated) {
|
||||
mg_cry(conn, "SSI #include path length overflow: [%s]", tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!XX_httplib_fopen(conn, path, "rb", &file)) {
|
||||
mg_cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s", tag, path, strerror(ERRNO));
|
||||
} else {
|
||||
XX_httplib_fclose_on_exec(&file, conn);
|
||||
if (XX_httplib_match_prefix(conn->ctx->config[SSI_EXTENSIONS], strlen(conn->ctx->config[SSI_EXTENSIONS]), path) > 0) {
|
||||
|
||||
send_ssi_file(conn, path, &file, include_level + 1);
|
||||
} else {
|
||||
XX_httplib_send_file_data(conn, &file, 0, INT64_MAX);
|
||||
}
|
||||
XX_httplib_fclose(&file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if !defined(NO_POPEN)
|
||||
static void do_ssi_exec(struct mg_connection *conn, char *tag) {
|
||||
|
||||
char cmd[1024] = "";
|
||||
struct file file = STRUCT_FILE_INITIALIZER;
|
||||
|
||||
if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) {
|
||||
mg_cry(conn, "Bad SSI #exec: [%s]", tag);
|
||||
} else {
|
||||
cmd[1023] = 0;
|
||||
if ((file.fp = popen(cmd, "r")) == NULL) {
|
||||
mg_cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
|
||||
} else {
|
||||
XX_httplib_send_file_data(conn, &file, 0, INT64_MAX);
|
||||
pclose(file.fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !NO_POPEN */
|
||||
|
||||
|
||||
static int mg_fgetc( struct file *filep, int offset ) {
|
||||
|
||||
if ( filep == NULL ) return EOF;
|
||||
if ( filep->membuf != NULL && offset >= 0 && ((unsigned int)(offset)) < filep->size ) return ((const unsigned char *)filep->membuf)[offset];
|
||||
if ( filep->fp != NULL ) return fgetc( filep->fp );
|
||||
|
||||
return EOF;
|
||||
|
||||
} /* mg_fgetc */
|
||||
|
||||
|
||||
static void send_ssi_file( struct mg_connection *conn, const char *path, struct file *filep, int include_level ) {
|
||||
|
||||
char buf[MG_BUF_LEN];
|
||||
int ch;
|
||||
int offset;
|
||||
int len;
|
||||
int in_ssi_tag;
|
||||
|
||||
if (include_level > 10) {
|
||||
mg_cry(conn, "SSI #include level is too deep (%s)", path);
|
||||
return;
|
||||
}
|
||||
|
||||
in_ssi_tag = len = offset = 0;
|
||||
while ((ch = mg_fgetc(filep, offset)) != EOF) {
|
||||
if (in_ssi_tag && ch == '>') {
|
||||
in_ssi_tag = 0;
|
||||
buf[len++] = (char)ch;
|
||||
buf[len] = '\0';
|
||||
/* assert(len <= (int) sizeof(buf)); */
|
||||
if (len > (int)sizeof(buf)) break;
|
||||
if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
|
||||
/* Not an SSI tag, pass it */
|
||||
mg_write( conn, buf, (size_t)len );
|
||||
} else {
|
||||
if (!memcmp(buf + 5, "include", 7)) {
|
||||
do_ssi_include(conn, path, buf + 12, include_level);
|
||||
#if !defined(NO_POPEN)
|
||||
} else if (!memcmp(buf + 5, "exec", 4)) {
|
||||
do_ssi_exec(conn, buf + 9);
|
||||
#endif /* !NO_POPEN */
|
||||
} else mg_cry(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
|
||||
}
|
||||
len = 0;
|
||||
} else if (in_ssi_tag) {
|
||||
if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
|
||||
/* Not an SSI tag */
|
||||
in_ssi_tag = 0;
|
||||
} else if (len == (int)sizeof(buf) - 2) {
|
||||
mg_cry(conn, "%s: SSI tag is too large", path);
|
||||
len = 0;
|
||||
}
|
||||
buf[len++] = (char)(ch & 0xff);
|
||||
} else if (ch == '<') {
|
||||
in_ssi_tag = 1;
|
||||
if (len > 0) {
|
||||
mg_write(conn, buf, (size_t)len);
|
||||
}
|
||||
len = 0;
|
||||
buf[len++] = (char)(ch & 0xff);
|
||||
} else {
|
||||
buf[len++] = (char)(ch & 0xff);
|
||||
if (len == (int)sizeof(buf)) {
|
||||
mg_write(conn, buf, (size_t)len);
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the rest of buffered data */
|
||||
if (len > 0) mg_write(conn, buf, (size_t)len);
|
||||
}
|
||||
|
||||
|
||||
void XX_httplib_handle_ssi_file_request( struct mg_connection *conn, const char *path, struct file *filep ) {
|
||||
|
||||
char date[64];
|
||||
time_t curtime;
|
||||
const char *cors1;
|
||||
const char *cors2;
|
||||
const char *cors3;
|
||||
|
||||
if ( conn == NULL || path == NULL || filep == NULL ) return;
|
||||
|
||||
curtime = time( NULL );
|
||||
|
||||
if (mg_get_header(conn, "Origin")) {
|
||||
/* Cross-origin resource sharing (CORS). */
|
||||
cors1 = "Access-Control-Allow-Origin: ";
|
||||
cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
|
||||
cors3 = "\r\n";
|
||||
} else {
|
||||
cors1 = "";
|
||||
cors2 = "";
|
||||
cors3 = "";
|
||||
}
|
||||
|
||||
if (!XX_httplib_fopen(conn, path, "rb", filep)) {
|
||||
/* File exists (precondition for calling this function),
|
||||
* but can not be opened by the server. */
|
||||
XX_httplib_send_http_error(conn, 500, "Error: Cannot read file\nfopen(%s): %s", path, strerror(ERRNO));
|
||||
} else {
|
||||
conn->must_close = 1;
|
||||
XX_httplib_gmt_time_string(date, sizeof(date), &curtime);
|
||||
XX_httplib_fclose_on_exec(filep, conn);
|
||||
mg_printf(conn, "HTTP/1.1 200 OK\r\n");
|
||||
XX_httplib_send_no_cache_header(conn);
|
||||
mg_printf(conn,
|
||||
"%s%s%s"
|
||||
"Date: %s\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Connection: %s\r\n\r\n",
|
||||
cors1,
|
||||
cors2,
|
||||
cors3,
|
||||
date,
|
||||
XX_httplib_suggest_connection_header(conn));
|
||||
send_ssi_file(conn, path, filep, 0);
|
||||
XX_httplib_fclose(filep);
|
||||
}
|
||||
|
||||
} /* XX_httplib_handle_ssi_file_request */
|
||||
@@ -657,6 +657,14 @@ struct mg_workerTLS {
|
||||
};
|
||||
|
||||
|
||||
#define PASSWORDS_FILE_NAME ".htpasswd"
|
||||
#define CGI_ENVIRONMENT_SIZE (4096)
|
||||
#define MAX_CGI_ENVIR_VARS (256)
|
||||
#define MG_BUF_LEN (8192)
|
||||
|
||||
mg_static_assert(MAX_REQUEST_SIZE >= 256, "request size length must be a positive number");
|
||||
|
||||
|
||||
/* Describes listening socket, or socket which was accept()-ed by the master
|
||||
* thread and queued for future handling by the worker thread. */
|
||||
struct socket {
|
||||
@@ -864,6 +872,7 @@ void XX_httplib_delete_file( struct mg_connection *conn, const char *path );
|
||||
void XX_httplib_discard_unread_request_data( struct mg_connection *conn );
|
||||
struct mg_connection * XX_httplib_fc( struct mg_context *ctx );
|
||||
void XX_httplib_fclose( struct file *filep );
|
||||
void XX_httplib_fclose_on_exec( struct file *filep, struct mg_connection *conn );
|
||||
int XX_httplib_fopen( const struct mg_connection *conn, const char *path, const char *mode, struct file *filep );
|
||||
void XX_httplib_free_context( struct mg_context *ctx );
|
||||
int XX_httplib_get_first_ssl_listener_index( const struct mg_context *ctx );
|
||||
@@ -917,7 +926,9 @@ void XX_httplib_remove_double_dots_and_double_slashes( char *s );
|
||||
void XX_httplib_reset_per_request_attributes( struct mg_connection *conn );
|
||||
int XX_httplib_scan_directory( struct mg_connection *conn, const char *dir, void *data, void (*cb)(struct de *, void *) );
|
||||
void XX_httplib_send_authorization_request( struct mg_connection *conn );
|
||||
void XX_httplib_send_http_error( struct mg_connection *, int, PRINTF_FORMAT_STRING(const char *fmt), ... ) PRINTF_ARGS(3, 4);
|
||||
void XX_httplib_send_file_data( struct mg_connection *conn, struct file *filep, int64_t offset, int64_t len );
|
||||
void XX_httplib_send_http_error( struct mg_connection *, int, PRINTF_FORMAT_STRING(const char *fmt), ... ) PRINTF_ARGS(3, 4);
|
||||
int XX_httplib_send_no_cache_header( struct mg_connection *conn );
|
||||
void XX_httplib_send_options( struct mg_connection *conn );
|
||||
int XX_httplib_send_static_cache_header( struct mg_connection *conn );
|
||||
int XX_httplib_send_websocket_handshake( struct mg_connection *conn, const char *websock_key );
|
||||
|
||||
253
src/libhttp.c
253
src/libhttp.c
@@ -162,13 +162,6 @@ pthread_mutexattr_t XX_httplib_pthread_mutex_attr;
|
||||
#endif /* _WIN32 */
|
||||
|
||||
|
||||
#define PASSWORDS_FILE_NAME ".htpasswd"
|
||||
#define CGI_ENVIRONMENT_SIZE (4096)
|
||||
#define MAX_CGI_ENVIR_VARS (256)
|
||||
#define MG_BUF_LEN (8192)
|
||||
|
||||
mg_static_assert(MAX_REQUEST_SIZE >= 256, "request size length must be a positive number");
|
||||
|
||||
#if !defined(DEBUG_TRACE)
|
||||
#if defined(DEBUG)
|
||||
|
||||
@@ -1482,7 +1475,7 @@ const char * XX_httplib_suggest_connection_header( const struct mg_connection *c
|
||||
} /* XX_httplib_suggest_connection_header */
|
||||
|
||||
|
||||
static int send_no_cache_header(struct mg_connection *conn) {
|
||||
int XX_httplib_send_no_cache_header( struct mg_connection *conn ) {
|
||||
|
||||
/* Send all current and obsolete cache opt-out directives. */
|
||||
return mg_printf(conn,
|
||||
@@ -1490,7 +1483,8 @@ static int send_no_cache_header(struct mg_connection *conn) {
|
||||
"must-revalidate, private, max-age=0\r\n"
|
||||
"Pragma: no-cache\r\n"
|
||||
"Expires: 0\r\n");
|
||||
}
|
||||
|
||||
} /* XX_httplib_send_no_cache_header */
|
||||
|
||||
|
||||
int XX_httplib_send_static_cache_header(struct mg_connection *conn) {
|
||||
@@ -1504,7 +1498,7 @@ int XX_httplib_send_static_cache_header(struct mg_connection *conn) {
|
||||
* and may be used differently in the future. */
|
||||
/* If a file should not be cached, do not only send
|
||||
* max-age=0, but also pragmas and Expires headers. */
|
||||
return send_no_cache_header(conn);
|
||||
return XX_httplib_send_no_cache_header(conn);
|
||||
}
|
||||
|
||||
/* Use "Cache-Control: max-age" instead of "Expires" header.
|
||||
@@ -1517,7 +1511,7 @@ int XX_httplib_send_static_cache_header(struct mg_connection *conn) {
|
||||
* as undefined. */
|
||||
return mg_printf(conn, "Cache-Control: max-age=%u\r\n", (unsigned)max_age);
|
||||
#else /* NO_CACHING */
|
||||
return send_no_cache_header(conn);
|
||||
return XX_httplib_send_no_cache_header(conn);
|
||||
#endif /* !NO_CACHING */
|
||||
|
||||
} /* XX_httplib_send_static_cache_header */
|
||||
@@ -1605,7 +1599,7 @@ void XX_httplib_send_http_error( struct mg_connection *conn, int status, const c
|
||||
|
||||
conn->must_close = 1;
|
||||
mg_printf(conn, "HTTP/1.1 %d %s\r\n", status, status_text);
|
||||
send_no_cache_header(conn);
|
||||
XX_httplib_send_no_cache_header(conn);
|
||||
if (has_body) mg_printf(conn, "%s", "Content-Type: text/plain; charset=utf-8\r\n");
|
||||
mg_printf(conn, "Date: %s\r\n" "Connection: close\r\n\r\n", date);
|
||||
|
||||
@@ -4182,7 +4176,7 @@ void XX_httplib_send_authorization_request( struct mg_connection *conn ) {
|
||||
XX_httplib_gmt_time_string(date, sizeof(date), &curtime);
|
||||
|
||||
mg_printf(conn, "HTTP/1.1 401 Unauthorized\r\n");
|
||||
send_no_cache_header(conn);
|
||||
XX_httplib_send_no_cache_header(conn);
|
||||
mg_printf(conn,
|
||||
"Date: %s\r\n"
|
||||
"Connection: %s\r\n"
|
||||
@@ -4771,7 +4765,7 @@ void XX_httplib_handle_directory_request( struct mg_connection *conn, const char
|
||||
|
||||
|
||||
/* Send len bytes from the opened file to the client. */
|
||||
static void send_file_data(struct mg_connection *conn, struct file *filep, int64_t offset, int64_t len) {
|
||||
void XX_httplib_send_file_data( struct mg_connection *conn, struct file *filep, int64_t offset, int64_t len ) {
|
||||
|
||||
char buf[MG_BUF_LEN];
|
||||
int to_read;
|
||||
@@ -4861,7 +4855,8 @@ static void send_file_data(struct mg_connection *conn, struct file *filep, int64
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* XX_httplib_send_file_data */
|
||||
|
||||
|
||||
static int parse_range_header(const char *header, int64_t *a, int64_t *b) {
|
||||
@@ -4878,7 +4873,7 @@ static void construct_etag(char *buf, size_t buf_len, const struct file *filep)
|
||||
}
|
||||
|
||||
|
||||
static void fclose_on_exec(struct file *filep, struct mg_connection *conn) {
|
||||
void XX_httplib_fclose_on_exec( struct file *filep, struct mg_connection *conn ) {
|
||||
|
||||
if (filep != NULL && filep->fp != NULL) {
|
||||
#ifdef _WIN32
|
||||
@@ -4889,7 +4884,9 @@ static void fclose_on_exec(struct file *filep, struct mg_connection *conn) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} /* XX_httplib_fclose_on_exec */
|
||||
|
||||
|
||||
|
||||
void XX_httplib_handle_static_file_request( struct mg_connection *conn, const char *path, struct file *filep, const char *mime_type, const char *additional_headers ) {
|
||||
@@ -4947,7 +4944,7 @@ void XX_httplib_handle_static_file_request( struct mg_connection *conn, const ch
|
||||
return;
|
||||
}
|
||||
|
||||
fclose_on_exec(filep, conn);
|
||||
XX_httplib_fclose_on_exec(filep, conn);
|
||||
|
||||
/* If Range: header specified, act accordingly */
|
||||
r1 = r2 = 0;
|
||||
@@ -5019,7 +5016,7 @@ void XX_httplib_handle_static_file_request( struct mg_connection *conn, const ch
|
||||
mg_printf(conn, "%.*s\r\n\r\n", (int)strlen(additional_headers), additional_headers);
|
||||
} else mg_printf(conn, "\r\n");
|
||||
|
||||
if (strcmp(conn->request_info.request_method, "HEAD") != 0) send_file_data(conn, filep, r1, cl);
|
||||
if (strcmp(conn->request_info.request_method, "HEAD") != 0) XX_httplib_send_file_data(conn, filep, r1, cl);
|
||||
XX_httplib_fclose(filep);
|
||||
|
||||
} /* XX_handle_static_file_request */
|
||||
@@ -5922,7 +5919,7 @@ void XX_httplib_handle_cgi_request( struct mg_connection *conn, const char *prog
|
||||
mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len));
|
||||
|
||||
/* Read the rest of CGI output and send to the client */
|
||||
send_file_data(conn, &fout, 0, INT64_MAX);
|
||||
XX_httplib_send_file_data(conn, &fout, 0, INT64_MAX);
|
||||
|
||||
done:
|
||||
XX_httplib_free(blk.var);
|
||||
@@ -6059,7 +6056,7 @@ void XX_httplib_put_file( struct mg_connection *conn, const char *path ) {
|
||||
/* put_dir returns 0 if path is a directory */
|
||||
XX_httplib_gmt_time_string(date, sizeof(date), &curtime);
|
||||
mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, mg_get_response_code_text(NULL, conn->status_code));
|
||||
send_no_cache_header(conn);
|
||||
XX_httplib_send_no_cache_header(conn);
|
||||
mg_printf(conn, "Date: %s\r\n" "Content-Length: 0\r\n" "Connection: %s\r\n\r\n", date, XX_httplib_suggest_connection_header(conn));
|
||||
|
||||
/* Request to create a directory has been fulfilled successfully.
|
||||
@@ -6086,7 +6083,7 @@ void XX_httplib_put_file( struct mg_connection *conn, const char *path ) {
|
||||
return;
|
||||
}
|
||||
|
||||
fclose_on_exec(&file, conn);
|
||||
XX_httplib_fclose_on_exec(&file, conn);
|
||||
range = mg_get_header(conn, "Content-Range");
|
||||
r1 = r2 = 0;
|
||||
if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
|
||||
@@ -6104,7 +6101,7 @@ void XX_httplib_put_file( struct mg_connection *conn, const char *path ) {
|
||||
|
||||
XX_httplib_gmt_time_string(date, sizeof(date), &curtime);
|
||||
mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, mg_get_response_code_text(NULL, conn->status_code));
|
||||
send_no_cache_header(conn);
|
||||
XX_httplib_send_no_cache_header(conn);
|
||||
mg_printf(conn, "Date: %s\r\n" "Content-Length: 0\r\n" "Connection: %s\r\n\r\n", date, XX_httplib_suggest_connection_header(conn));
|
||||
|
||||
XX_httplib_fclose(&file);
|
||||
@@ -6160,213 +6157,3 @@ void XX_httplib_delete_file( struct mg_connection *conn, const char *path ) {
|
||||
} /* XX_httplib_delete_file */
|
||||
|
||||
#endif /* !NO_FILES */
|
||||
|
||||
|
||||
static void send_ssi_file(struct mg_connection *, const char *, struct file *, int);
|
||||
|
||||
|
||||
static void do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, int include_level) {
|
||||
|
||||
char file_name[MG_BUF_LEN], path[512];
|
||||
char *p;
|
||||
struct file file = STRUCT_FILE_INITIALIZER;
|
||||
size_t len;
|
||||
int truncated = 0;
|
||||
|
||||
if ( conn == NULL ) return;
|
||||
|
||||
/* sscanf() is safe here, since send_ssi_file() also uses buffer
|
||||
* of size MG_BUF_LEN to get the tag. So strlen(tag) is
|
||||
* always < MG_BUF_LEN. */
|
||||
if (sscanf(tag, " virtual=\"%511[^\"]\"", file_name) == 1) {
|
||||
/* File name is relative to the webserver root */
|
||||
file_name[511] = 0;
|
||||
XX_httplib_snprintf(conn, &truncated, path, sizeof(path), "%s/%s", conn->ctx->config[DOCUMENT_ROOT], file_name);
|
||||
|
||||
} else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) {
|
||||
/* File name is relative to the webserver working directory
|
||||
* or it is absolute system path */
|
||||
file_name[511] = 0;
|
||||
XX_httplib_snprintf(conn, &truncated, path, sizeof(path), "%s", file_name);
|
||||
|
||||
} else if (sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1
|
||||
|| sscanf(tag, " \"%511[^\"]\"", file_name) == 1) {
|
||||
/* File name is relative to the currect document */
|
||||
file_name[511] = 0;
|
||||
XX_httplib_snprintf(conn, &truncated, path, sizeof(path), "%s", ssi);
|
||||
|
||||
if (!truncated) {
|
||||
if ((p = strrchr(path, '/')) != NULL) p[1] = '\0';
|
||||
len = strlen(path);
|
||||
XX_httplib_snprintf(conn, &truncated, path + len, sizeof(path) - len, "%s", file_name);
|
||||
}
|
||||
|
||||
} else {
|
||||
mg_cry(conn, "Bad SSI #include: [%s]", tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (truncated) {
|
||||
mg_cry(conn, "SSI #include path length overflow: [%s]", tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!XX_httplib_fopen(conn, path, "rb", &file)) {
|
||||
mg_cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s", tag, path, strerror(ERRNO));
|
||||
} else {
|
||||
fclose_on_exec(&file, conn);
|
||||
if (XX_httplib_match_prefix(conn->ctx->config[SSI_EXTENSIONS], strlen(conn->ctx->config[SSI_EXTENSIONS]), path) > 0) {
|
||||
|
||||
send_ssi_file(conn, path, &file, include_level + 1);
|
||||
} else {
|
||||
send_file_data(conn, &file, 0, INT64_MAX);
|
||||
}
|
||||
XX_httplib_fclose(&file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if !defined(NO_POPEN)
|
||||
static void do_ssi_exec(struct mg_connection *conn, char *tag) {
|
||||
|
||||
char cmd[1024] = "";
|
||||
struct file file = STRUCT_FILE_INITIALIZER;
|
||||
|
||||
if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) {
|
||||
mg_cry(conn, "Bad SSI #exec: [%s]", tag);
|
||||
} else {
|
||||
cmd[1023] = 0;
|
||||
if ((file.fp = popen(cmd, "r")) == NULL) {
|
||||
mg_cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
|
||||
} else {
|
||||
send_file_data(conn, &file, 0, INT64_MAX);
|
||||
pclose(file.fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !NO_POPEN */
|
||||
|
||||
|
||||
static int mg_fgetc( struct file *filep, int offset ) {
|
||||
|
||||
if ( filep == NULL ) return EOF;
|
||||
if ( filep->membuf != NULL && offset >= 0 && ((unsigned int)(offset)) < filep->size ) return ((const unsigned char *)filep->membuf)[offset];
|
||||
if ( filep->fp != NULL ) return fgetc( filep->fp );
|
||||
|
||||
return EOF;
|
||||
|
||||
} /* mg_fgetc */
|
||||
|
||||
|
||||
static void send_ssi_file( struct mg_connection *conn, const char *path, struct file *filep, int include_level ) {
|
||||
|
||||
char buf[MG_BUF_LEN];
|
||||
int ch;
|
||||
int offset;
|
||||
int len;
|
||||
int in_ssi_tag;
|
||||
|
||||
if (include_level > 10) {
|
||||
mg_cry(conn, "SSI #include level is too deep (%s)", path);
|
||||
return;
|
||||
}
|
||||
|
||||
in_ssi_tag = len = offset = 0;
|
||||
while ((ch = mg_fgetc(filep, offset)) != EOF) {
|
||||
if (in_ssi_tag && ch == '>') {
|
||||
in_ssi_tag = 0;
|
||||
buf[len++] = (char)ch;
|
||||
buf[len] = '\0';
|
||||
/* assert(len <= (int) sizeof(buf)); */
|
||||
if (len > (int)sizeof(buf)) break;
|
||||
if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
|
||||
/* Not an SSI tag, pass it */
|
||||
mg_write( conn, buf, (size_t)len );
|
||||
} else {
|
||||
if (!memcmp(buf + 5, "include", 7)) {
|
||||
do_ssi_include(conn, path, buf + 12, include_level);
|
||||
#if !defined(NO_POPEN)
|
||||
} else if (!memcmp(buf + 5, "exec", 4)) {
|
||||
do_ssi_exec(conn, buf + 9);
|
||||
#endif /* !NO_POPEN */
|
||||
} else mg_cry(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
|
||||
}
|
||||
len = 0;
|
||||
} else if (in_ssi_tag) {
|
||||
if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
|
||||
/* Not an SSI tag */
|
||||
in_ssi_tag = 0;
|
||||
} else if (len == (int)sizeof(buf) - 2) {
|
||||
mg_cry(conn, "%s: SSI tag is too large", path);
|
||||
len = 0;
|
||||
}
|
||||
buf[len++] = (char)(ch & 0xff);
|
||||
} else if (ch == '<') {
|
||||
in_ssi_tag = 1;
|
||||
if (len > 0) {
|
||||
mg_write(conn, buf, (size_t)len);
|
||||
}
|
||||
len = 0;
|
||||
buf[len++] = (char)(ch & 0xff);
|
||||
} else {
|
||||
buf[len++] = (char)(ch & 0xff);
|
||||
if (len == (int)sizeof(buf)) {
|
||||
mg_write(conn, buf, (size_t)len);
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the rest of buffered data */
|
||||
if (len > 0) mg_write(conn, buf, (size_t)len);
|
||||
}
|
||||
|
||||
|
||||
void XX_httplib_handle_ssi_file_request( struct mg_connection *conn, const char *path, struct file *filep ) {
|
||||
|
||||
char date[64];
|
||||
time_t curtime;
|
||||
const char *cors1;
|
||||
const char *cors2;
|
||||
const char *cors3;
|
||||
|
||||
if ( conn == NULL || path == NULL || filep == NULL ) return;
|
||||
|
||||
curtime = time( NULL );
|
||||
|
||||
if (mg_get_header(conn, "Origin")) {
|
||||
/* Cross-origin resource sharing (CORS). */
|
||||
cors1 = "Access-Control-Allow-Origin: ";
|
||||
cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
|
||||
cors3 = "\r\n";
|
||||
} else {
|
||||
cors1 = "";
|
||||
cors2 = "";
|
||||
cors3 = "";
|
||||
}
|
||||
|
||||
if (!XX_httplib_fopen(conn, path, "rb", filep)) {
|
||||
/* File exists (precondition for calling this function),
|
||||
* but can not be opened by the server. */
|
||||
XX_httplib_send_http_error(conn, 500, "Error: Cannot read file\nfopen(%s): %s", path, strerror(ERRNO));
|
||||
} else {
|
||||
conn->must_close = 1;
|
||||
XX_httplib_gmt_time_string(date, sizeof(date), &curtime);
|
||||
fclose_on_exec(filep, conn);
|
||||
mg_printf(conn, "HTTP/1.1 200 OK\r\n");
|
||||
send_no_cache_header(conn);
|
||||
mg_printf(conn,
|
||||
"%s%s%s"
|
||||
"Date: %s\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Connection: %s\r\n\r\n",
|
||||
cors1,
|
||||
cors2,
|
||||
cors3,
|
||||
date,
|
||||
XX_httplib_suggest_connection_header(conn));
|
||||
send_ssi_file(conn, path, filep, 0);
|
||||
XX_httplib_fclose(filep);
|
||||
}
|
||||
|
||||
} /* XX_httplib_handle_ssi_file_request */
|
||||
|
||||
Reference in New Issue
Block a user