version 1.1, 2013/10/14 10:32:48
|
version 1.1.1.3, 2016/11/02 10:35:00
|
Line 1
|
Line 1
|
|
#include "first.h" |
|
|
#include "base.h" |
#include "base.h" |
#include "log.h" |
#include "log.h" |
#include "buffer.h" |
#include "buffer.h" |
#include "stat_cache.h" |
|
|
|
#include "plugin.h" |
#include "plugin.h" |
#include "stream.h" |
#include "stream.h" |
Line 20
|
Line 21
|
#include <stdlib.h> |
#include <stdlib.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <string.h> |
#include <string.h> |
|
#include <strings.h> |
#include <errno.h> |
#include <errno.h> |
|
#include <fcntl.h> |
#include <time.h> |
#include <time.h> |
#include <unistd.h> |
#include <unistd.h> |
|
|
Line 37
|
Line 40
|
#endif |
#endif |
|
|
#include "etag.h" |
#include "etag.h" |
#include "version.h" |
|
|
|
/* The newest modified time of included files for include statement */ |
/* The newest modified time of included files for include statement */ |
static volatile time_t include_file_last_mtime = 0; |
static volatile time_t include_file_last_mtime = 0; |
Line 69 FREE_FUNC(mod_ssi_free) {
|
Line 71 FREE_FUNC(mod_ssi_free) {
|
for (i = 0; i < srv->config_context->used; i++) { |
for (i = 0; i < srv->config_context->used; i++) { |
plugin_config *s = p->config_storage[i]; |
plugin_config *s = p->config_storage[i]; |
|
|
|
if (NULL == s) continue; |
|
|
array_free(s->ssi_extension); |
array_free(s->ssi_extension); |
buffer_free(s->content_type); |
buffer_free(s->content_type); |
|
|
Line 79 FREE_FUNC(mod_ssi_free) {
|
Line 83 FREE_FUNC(mod_ssi_free) {
|
|
|
array_free(p->ssi_vars); |
array_free(p->ssi_vars); |
array_free(p->ssi_cgi_env); |
array_free(p->ssi_cgi_env); |
#ifdef HAVE_PCRE_H |
|
pcre_free(p->ssi_regex); |
|
#endif |
|
buffer_free(p->timefmt); |
buffer_free(p->timefmt); |
buffer_free(p->stat_fn); |
buffer_free(p->stat_fn); |
|
|
Line 95 FREE_FUNC(mod_ssi_free) {
|
Line 96 FREE_FUNC(mod_ssi_free) {
|
SETDEFAULTS_FUNC(mod_ssi_set_defaults) { |
SETDEFAULTS_FUNC(mod_ssi_set_defaults) { |
plugin_data *p = p_d; |
plugin_data *p = p_d; |
size_t i = 0; |
size_t i = 0; |
#ifdef HAVE_PCRE_H |
|
const char *errptr; |
|
int erroff; |
|
#endif |
|
|
|
config_values_t cv[] = { |
config_values_t cv[] = { |
{ "ssi.extension", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ |
{ "ssi.extension", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ |
{ "ssi.content-type", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ |
{ "ssi.content-type", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ |
|
{ "ssi.conditional-requests", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ |
|
{ "ssi.exec", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ |
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } |
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } |
}; |
}; |
|
|
if (!p) return HANDLER_ERROR; |
if (!p) return HANDLER_ERROR; |
|
|
p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); | p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); |
|
|
for (i = 0; i < srv->config_context->used; i++) { |
for (i = 0; i < srv->config_context->used; i++) { |
|
data_config const* config = (data_config const*)srv->config_context->data[i]; |
plugin_config *s; |
plugin_config *s; |
|
|
s = calloc(1, sizeof(plugin_config)); |
s = calloc(1, sizeof(plugin_config)); |
s->ssi_extension = array_init(); |
s->ssi_extension = array_init(); |
s->content_type = buffer_init(); |
s->content_type = buffer_init(); |
|
s->conditional_requests = 0; |
|
s->ssi_exec = 1; |
|
|
cv[0].destination = s->ssi_extension; |
cv[0].destination = s->ssi_extension; |
cv[1].destination = s->content_type; |
cv[1].destination = s->content_type; |
|
cv[2].destination = &(s->conditional_requests); |
|
cv[3].destination = &(s->ssi_exec); |
|
|
p->config_storage[i] = s; |
p->config_storage[i] = s; |
|
|
if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { | if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { |
return HANDLER_ERROR; |
return HANDLER_ERROR; |
} |
} |
} |
} |
|
|
#ifdef HAVE_PCRE_H |
|
/* allow 2 params */ |
|
if (NULL == (p->ssi_regex = pcre_compile("<!--#([a-z]+)\\s+(?:([a-z]+)=\"(.*?)(?<!\\\\)\"\\s*)?(?:([a-z]+)=\"(.*?)(?<!\\\\)\"\\s*)?-->", 0, &errptr, &erroff, NULL))) { |
|
log_error_write(srv, __FILE__, __LINE__, "sds", |
|
"ssi: pcre ", |
|
erroff, errptr); |
|
return HANDLER_ERROR; |
|
} |
|
#else |
|
log_error_write(srv, __FILE__, __LINE__, "s", |
|
"mod_ssi: pcre support is missing, please recompile with pcre support or remove mod_ssi from the list of modules"); |
|
return HANDLER_ERROR; |
|
#endif |
|
|
|
return HANDLER_GO_ON; |
return HANDLER_GO_ON; |
} |
} |
|
|
|
|
static int ssi_env_add(array *env, const char *key, const char *val) { |
static int ssi_env_add(array *env, const char *key, const char *val) { |
data_string *ds; |
data_string *ds; |
|
|
Line 172 static int ssi_env_add_request_headers(server *srv, co
|
Line 163 static int ssi_env_add_request_headers(server *srv, co
|
|
|
ds = (data_string *)con->request.headers->data[i]; |
ds = (data_string *)con->request.headers->data[i]; |
|
|
if (ds->value->used && ds->key->used) { | if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) { |
size_t j; | |
buffer_reset(srv->tmp_buf); | |
| |
/* don't forward the Authorization: Header */ |
/* don't forward the Authorization: Header */ |
if (0 == strcasecmp(ds->key->ptr, "AUTHORIZATION")) { | if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Authorization"))) { |
continue; |
continue; |
} |
} |
|
|
if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { | /* Do not emit HTTP_PROXY in environment. |
buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("HTTP_")); | * Some executables use HTTP_PROXY to configure |
srv->tmp_buf->used--; | * outgoing proxy. See also https://httpoxy.org/ */ |
| if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy"))) { |
| continue; |
} |
} |
|
|
buffer_prepare_append(srv->tmp_buf, ds->key->used + 2); | buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, CONST_BUF_LEN(ds->key), 1); |
for (j = 0; j < ds->key->used - 1; j++) { | |
char c = '_'; | |
if (light_isalpha(ds->key->ptr[j])) { | |
/* upper-case */ | |
c = ds->key->ptr[j] & ~32; | |
} else if (light_isdigit(ds->key->ptr[j])) { | |
/* copy */ | |
c = ds->key->ptr[j]; | |
} | |
srv->tmp_buf->ptr[srv->tmp_buf->used++] = c; | |
} | |
srv->tmp_buf->ptr[srv->tmp_buf->used] = '\0'; | |
|
|
ssi_env_add(p->ssi_cgi_env, srv->tmp_buf->ptr, ds->value->ptr); |
ssi_env_add(p->ssi_cgi_env, srv->tmp_buf->ptr, ds->value->ptr); |
} |
} |
Line 209 static int ssi_env_add_request_headers(server *srv, co
|
Line 187 static int ssi_env_add_request_headers(server *srv, co
|
|
|
ds = (data_string *)con->environment->data[i]; |
ds = (data_string *)con->environment->data[i]; |
|
|
if (ds->value->used && ds->key->used) { | if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) { |
size_t j; | buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, CONST_BUF_LEN(ds->key), 0); |
|
|
buffer_reset(srv->tmp_buf); |
|
buffer_prepare_append(srv->tmp_buf, ds->key->used + 2); |
|
|
|
for (j = 0; j < ds->key->used - 1; j++) { |
|
char c = '_'; |
|
if (light_isalpha(ds->key->ptr[j])) { |
|
/* upper-case */ |
|
c = ds->key->ptr[j] & ~32; |
|
} else if (light_isdigit(ds->key->ptr[j])) { |
|
/* copy */ |
|
c = ds->key->ptr[j]; |
|
} |
|
srv->tmp_buf->ptr[srv->tmp_buf->used++] = c; |
|
} |
|
srv->tmp_buf->ptr[srv->tmp_buf->used] = '\0'; |
|
|
|
ssi_env_add(p->ssi_cgi_env, srv->tmp_buf->ptr, ds->value->ptr); |
ssi_env_add(p->ssi_cgi_env, srv->tmp_buf->ptr, ds->value->ptr); |
} |
} |
} |
} |
Line 236 static int ssi_env_add_request_headers(server *srv, co
|
Line 198 static int ssi_env_add_request_headers(server *srv, co
|
} |
} |
|
|
static int build_ssi_cgi_vars(server *srv, connection *con, plugin_data *p) { |
static int build_ssi_cgi_vars(server *srv, connection *con, plugin_data *p) { |
char buf[32]; | char buf[LI_ITOSTRING_LENGTH]; |
|
|
server_socket *srv_sock = con->srv_socket; |
server_socket *srv_sock = con->srv_socket; |
|
|
Line 249 static int build_ssi_cgi_vars(server *srv, connection
|
Line 211 static int build_ssi_cgi_vars(server *srv, connection
|
|
|
array_reset(p->ssi_cgi_env); |
array_reset(p->ssi_cgi_env); |
|
|
ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_SOFTWARE"), PACKAGE_DESC); | ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_SOFTWARE"), con->conf.server_tag->ptr); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_NAME"), |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_NAME"), |
#ifdef HAVE_IPV6 |
#ifdef HAVE_IPV6 |
inet_ntop(srv_sock->addr.plain.sa_family, |
inet_ntop(srv_sock->addr.plain.sa_family, |
Line 263 static int build_ssi_cgi_vars(server *srv, connection
|
Line 225 static int build_ssi_cgi_vars(server *srv, connection
|
); |
); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("GATEWAY_INTERFACE"), "CGI/1.1"); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("GATEWAY_INTERFACE"), "CGI/1.1"); |
|
|
LI_ltostr(buf, | li_utostrn(buf, sizeof(buf), |
#ifdef HAVE_IPV6 |
#ifdef HAVE_IPV6 |
ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) |
ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) |
#else |
#else |
Line 279 static int build_ssi_cgi_vars(server *srv, connection
|
Line 241 static int build_ssi_cgi_vars(server *srv, connection
|
if (con->request.content_length > 0) { |
if (con->request.content_length > 0) { |
/* CGI-SPEC 6.1.2 and FastCGI spec 6.3 */ |
/* CGI-SPEC 6.1.2 and FastCGI spec 6.3 */ |
|
|
/* request.content_length < SSIZE_MAX, see request.c */ | li_itostrn(buf, sizeof(buf), con->request.content_length); |
LI_ltostr(buf, con->request.content_length); | |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("CONTENT_LENGTH"), buf); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("CONTENT_LENGTH"), buf); |
} |
} |
|
|
Line 301 static int build_ssi_cgi_vars(server *srv, connection
|
Line 262 static int build_ssi_cgi_vars(server *srv, connection
|
* parameter. |
* parameter. |
*/ |
*/ |
|
|
if (con->request.pathinfo->used) { | if (!buffer_string_is_empty(con->request.pathinfo)) { |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("PATH_INFO"), con->request.pathinfo->ptr); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("PATH_INFO"), con->request.pathinfo->ptr); |
} |
} |
|
|
ssi_env_add(p->ssi_cgi_env, CONST_STRING("SCRIPT_FILENAME"), con->physical.path->ptr); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("SCRIPT_FILENAME"), con->physical.path->ptr); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("DOCUMENT_ROOT"), con->physical.doc_root->ptr); | ssi_env_add(p->ssi_cgi_env, CONST_STRING("DOCUMENT_ROOT"), con->physical.basedir->ptr); |
|
|
ssi_env_add(p->ssi_cgi_env, CONST_STRING("REQUEST_URI"), con->request.uri->ptr); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("REQUEST_URI"), con->request.uri->ptr); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("QUERY_STRING"), con->uri.query->used ? con->uri.query->ptr : ""); | |
| if (!buffer_string_is_empty(con->uri.scheme)) { |
| ssi_env_add(p->ssi_cgi_env, CONST_STRING("REQUEST_SCHEME"), con->uri.scheme->ptr); |
| } |
| |
| ssi_env_add(p->ssi_cgi_env, CONST_STRING("QUERY_STRING"), buffer_is_empty(con->uri.query) ? "" : con->uri.query->ptr); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("REQUEST_METHOD"), get_http_method_name(con->request.http_method)); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("REQUEST_METHOD"), get_http_method_name(con->request.http_method)); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("REDIRECT_STATUS"), "200"); |
|
ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_PROTOCOL"), get_http_version_name(con->request.http_version)); |
ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_PROTOCOL"), get_http_version_name(con->request.http_version)); |
|
/* set REDIRECT_STATUS for php compiled with --force-redirect |
|
* (if REDIRECT_STATUS has not already been set by error handler) */ |
|
if (0 == con->error_handler_saved_status) { |
|
ssi_env_add(p->ssi_cgi_env, CONST_STRING("REDIRECT_STATUS"), "200"); |
|
} |
|
|
ssi_env_add_request_headers(srv, con, p); |
ssi_env_add_request_headers(srv, con, p); |
|
|
return 0; |
return 0; |
} |
} |
|
|
static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, | static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const char **l, size_t n, struct stat *st) { |
const char **l, size_t n) { | |
| /** |
| * <!--#element attribute=value attribute=value ... --> |
| * |
| * config DONE |
| * errmsg -- missing |
| * sizefmt DONE |
| * timefmt DONE |
| * echo DONE |
| * var DONE |
| * encoding -- missing |
| * exec DONE |
| * cgi -- never |
| * cmd DONE |
| * fsize DONE |
| * file DONE |
| * virtual DONE |
| * flastmod DONE |
| * file DONE |
| * virtual DONE |
| * include DONE |
| * file DONE |
| * virtual DONE |
| * printenv DONE |
| * set DONE |
| * var DONE |
| * value DONE |
| * |
| * if DONE |
| * elif DONE |
| * else DONE |
| * endif DONE |
| * |
| * |
| * expressions |
| * AND, OR DONE |
| * comp DONE |
| * ${...} -- missing |
| * $... DONE |
| * '...' DONE |
| * ( ... ) DONE |
| * |
| * |
| * |
| * ** all DONE ** |
| * DATE_GMT |
| * The current date in Greenwich Mean Time. |
| * DATE_LOCAL |
| * The current date in the local time zone. |
| * DOCUMENT_NAME |
| * The filename (excluding directories) of the document requested by the user. |
| * DOCUMENT_URI |
| * The (%-decoded) URL path of the document requested by the user. Note that in the case of nested include files, this is not then URL for the current document. |
| * LAST_MODIFIED |
| * The last modification date of the document requested by the user. |
| * USER_NAME |
| * Contains the owner of the file which included it. |
| * |
| */ |
| |
size_t i, ssicmd = 0; |
size_t i, ssicmd = 0; |
char buf[255]; |
char buf[255]; |
buffer *b = NULL; |
buffer *b = NULL; |
Line 360 static int process_ssi_stmt(server *srv, connection *c
|
Line 389 static int process_ssi_stmt(server *srv, connection *c
|
int var = 0; |
int var = 0; |
/* int enc = 0; */ |
/* int enc = 0; */ |
const char *var_val = NULL; |
const char *var_val = NULL; |
stat_cache_entry *sce = NULL; |
|
|
|
struct { |
struct { |
const char *var; |
const char *var; |
enum { SSI_ECHO_UNSET, SSI_ECHO_DATE_GMT, SSI_ECHO_DATE_LOCAL, SSI_ECHO_DOCUMENT_NAME, SSI_ECHO_DOCUMENT_URI, | enum { |
SSI_ECHO_LAST_MODIFIED, SSI_ECHO_USER_NAME } type; | SSI_ECHO_UNSET, |
| SSI_ECHO_DATE_GMT, |
| SSI_ECHO_DATE_LOCAL, |
| SSI_ECHO_DOCUMENT_NAME, |
| SSI_ECHO_DOCUMENT_URI, |
| SSI_ECHO_LAST_MODIFIED, |
| SSI_ECHO_USER_NAME, |
| SSI_ECHO_SCRIPT_URI, |
| SSI_ECHO_SCRIPT_URL, |
| } type; |
} echovars[] = { |
} echovars[] = { |
{ "DATE_GMT", SSI_ECHO_DATE_GMT }, |
{ "DATE_GMT", SSI_ECHO_DATE_GMT }, |
{ "DATE_LOCAL", SSI_ECHO_DATE_LOCAL }, |
{ "DATE_LOCAL", SSI_ECHO_DATE_LOCAL }, |
Line 373 static int process_ssi_stmt(server *srv, connection *c
|
Line 410 static int process_ssi_stmt(server *srv, connection *c
|
{ "DOCUMENT_URI", SSI_ECHO_DOCUMENT_URI }, |
{ "DOCUMENT_URI", SSI_ECHO_DOCUMENT_URI }, |
{ "LAST_MODIFIED", SSI_ECHO_LAST_MODIFIED }, |
{ "LAST_MODIFIED", SSI_ECHO_LAST_MODIFIED }, |
{ "USER_NAME", SSI_ECHO_USER_NAME }, |
{ "USER_NAME", SSI_ECHO_USER_NAME }, |
|
{ "SCRIPT_URI", SSI_ECHO_SCRIPT_URI }, |
|
{ "SCRIPT_URL", SSI_ECHO_SCRIPT_URL }, |
|
|
{ NULL, SSI_ECHO_UNSET } |
{ NULL, SSI_ECHO_UNSET } |
}; |
}; |
Line 415 static int process_ssi_stmt(server *srv, connection *c
|
Line 454 static int process_ssi_stmt(server *srv, connection *c
|
*/ |
*/ |
} else { |
} else { |
log_error_write(srv, __FILE__, __LINE__, "sss", |
log_error_write(srv, __FILE__, __LINE__, "sss", |
"ssi: unknow attribute for ", | "ssi: unknown attribute for ", |
l[1], l[i]); |
l[1], l[i]); |
} |
} |
} |
} |
Line 429 static int process_ssi_stmt(server *srv, connection *c
|
Line 468 static int process_ssi_stmt(server *srv, connection *c
|
break; |
break; |
} |
} |
|
|
stat_cache_get_entry(srv, con, con->physical.path, &sce); |
|
|
|
switch(var) { |
switch(var) { |
case SSI_ECHO_USER_NAME: { |
case SSI_ECHO_USER_NAME: { |
struct passwd *pw; |
struct passwd *pw; |
|
|
b = chunkqueue_get_append_buffer(con->write_queue); | b = buffer_init(); |
#ifdef HAVE_PWD_H |
#ifdef HAVE_PWD_H |
if (NULL == (pw = getpwuid(sce->st.st_uid))) { | if (NULL == (pw = getpwuid(st->st_uid))) { |
buffer_copy_long(b, sce->st.st_uid); | buffer_copy_int(b, st->st_uid); |
} else { |
} else { |
buffer_copy_string(b, pw->pw_name); |
buffer_copy_string(b, pw->pw_name); |
} |
} |
#else |
#else |
buffer_copy_long(b, sce->st.st_uid); | buffer_copy_int(b, st->st_uid); |
#endif |
#endif |
|
chunkqueue_append_buffer(con->write_queue, b); |
|
buffer_free(b); |
break; |
break; |
} |
} |
case SSI_ECHO_LAST_MODIFIED: { | case SSI_ECHO_LAST_MODIFIED: { |
time_t t = sce->st.st_mtime; | time_t t = st->st_mtime; |
|
|
b = chunkqueue_get_append_buffer(con->write_queue); |
|
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { |
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { |
buffer_copy_string_len(b, CONST_STR_LEN("(none)")); | chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)")); |
} else { |
} else { |
buffer_copy_string(b, buf); | chunkqueue_append_mem(con->write_queue, buf, strlen(buf)); |
} |
} |
break; |
break; |
} |
} |
case SSI_ECHO_DATE_LOCAL: { |
case SSI_ECHO_DATE_LOCAL: { |
time_t t = time(NULL); |
time_t t = time(NULL); |
|
|
b = chunkqueue_get_append_buffer(con->write_queue); |
|
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { |
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { |
buffer_copy_string_len(b, CONST_STR_LEN("(none)")); | chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)")); |
} else { |
} else { |
buffer_copy_string(b, buf); | chunkqueue_append_mem(con->write_queue, buf, strlen(buf)); |
} |
} |
break; |
break; |
} |
} |
case SSI_ECHO_DATE_GMT: { |
case SSI_ECHO_DATE_GMT: { |
time_t t = time(NULL); |
time_t t = time(NULL); |
|
|
b = chunkqueue_get_append_buffer(con->write_queue); |
|
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, gmtime(&t))) { |
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, gmtime(&t))) { |
buffer_copy_string_len(b, CONST_STR_LEN("(none)")); | chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)")); |
} else { |
} else { |
buffer_copy_string(b, buf); | chunkqueue_append_mem(con->write_queue, buf, strlen(buf)); |
} |
} |
break; |
break; |
} |
} |
case SSI_ECHO_DOCUMENT_NAME: { |
case SSI_ECHO_DOCUMENT_NAME: { |
char *sl; |
char *sl; |
|
|
b = chunkqueue_get_append_buffer(con->write_queue); |
|
if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) { |
if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) { |
buffer_copy_string_buffer(b, con->physical.path); | chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->physical.path)); |
} else { |
} else { |
buffer_copy_string(b, sl + 1); | chunkqueue_append_mem(con->write_queue, sl + 1, strlen(sl + 1)); |
} |
} |
break; |
break; |
} |
} |
case SSI_ECHO_DOCUMENT_URI: { |
case SSI_ECHO_DOCUMENT_URI: { |
b = chunkqueue_get_append_buffer(con->write_queue); | chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.path)); |
buffer_copy_string_buffer(b, con->uri.path); | |
break; |
break; |
} |
} |
|
case SSI_ECHO_SCRIPT_URI: { |
|
if (!buffer_string_is_empty(con->uri.scheme) && !buffer_string_is_empty(con->uri.authority)) { |
|
chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.scheme)); |
|
chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("://")); |
|
chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.authority)); |
|
chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->request.uri)); |
|
if (!buffer_string_is_empty(con->uri.query)) { |
|
chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("?")); |
|
chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.query)); |
|
} |
|
} |
|
break; |
|
} |
|
case SSI_ECHO_SCRIPT_URL: { |
|
chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->request.uri)); |
|
if (!buffer_string_is_empty(con->uri.query)) { |
|
chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("?")); |
|
chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.query)); |
|
} |
|
break; |
|
} |
default: { |
default: { |
data_string *ds; |
data_string *ds; |
/* check if it is a cgi-var */ | /* check if it is a cgi-var or a ssi-var */ |
|
|
b = chunkqueue_get_append_buffer(con->write_queue); | if (NULL != (ds = (data_string *)array_get_element(p->ssi_cgi_env, var_val)) || |
| NULL != (ds = (data_string *)array_get_element(p->ssi_vars, var_val))) { |
if (NULL != (ds = (data_string *)array_get_element(p->ssi_cgi_env, var_val))) { | chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(ds->value)); |
buffer_copy_string_buffer(b, ds->value); | |
} else { |
} else { |
buffer_copy_string_len(b, CONST_STR_LEN("(none)")); | chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)")); |
} |
} |
|
|
break; |
break; |
Line 517 static int process_ssi_stmt(server *srv, connection *c
|
Line 571 static int process_ssi_stmt(server *srv, connection *c
|
case SSI_FLASTMOD: |
case SSI_FLASTMOD: |
case SSI_FSIZE: { |
case SSI_FSIZE: { |
const char * file_path = NULL, *virt_path = NULL; |
const char * file_path = NULL, *virt_path = NULL; |
struct stat st; | struct stat stb; |
char *sl; |
char *sl; |
|
|
for (i = 2; i < n; i += 2) { |
for (i = 2; i < n; i += 2) { |
Line 527 static int process_ssi_stmt(server *srv, connection *c
|
Line 581 static int process_ssi_stmt(server *srv, connection *c
|
virt_path = l[i+1]; |
virt_path = l[i+1]; |
} else { |
} else { |
log_error_write(srv, __FILE__, __LINE__, "sss", |
log_error_write(srv, __FILE__, __LINE__, "sss", |
"ssi: unknow attribute for ", | "ssi: unknown attribute for ", |
l[1], l[i]); |
l[1], l[i]); |
} |
} |
} |
} |
Line 563 static int process_ssi_stmt(server *srv, connection *c
|
Line 617 static int process_ssi_stmt(server *srv, connection *c
|
buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); |
buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); |
} else { |
} else { |
/* virtual */ |
/* virtual */ |
|
size_t remain; |
|
|
if (virt_path[0] == '/') { |
if (virt_path[0] == '/') { |
buffer_copy_string(p->stat_fn, virt_path); |
buffer_copy_string(p->stat_fn, virt_path); |
Line 579 static int process_ssi_stmt(server *srv, connection *c
|
Line 634 static int process_ssi_stmt(server *srv, connection *c
|
|
|
/* we have an uri */ |
/* we have an uri */ |
|
|
buffer_copy_string_buffer(p->stat_fn, con->physical.doc_root); | /* Destination physical path (similar to code in mod_webdav.c) |
buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); | * src con->physical.path might have been remapped with mod_alias, mod_userdir. |
| * (but neither modifies con->physical.rel_path) |
| * Find matching prefix to support relative paths to current physical path. |
| * Aliasing of paths underneath current con->physical.basedir might not work. |
| * Likewise, mod_rewrite URL rewriting might thwart this comparison. |
| * Use mod_redirect instead of mod_alias to remap paths *under* this basedir. |
| * Use mod_redirect instead of mod_rewrite on *any* parts of path to basedir. |
| * (Related, use mod_auth to protect this basedir, but avoid attempting to |
| * use mod_auth on paths underneath this basedir, as target path is not |
| * validated with mod_auth) |
| */ |
| |
| /* find matching URI prefix |
| * check if remaining con->physical.rel_path matches suffix |
| * of con->physical.basedir so that we can use it to |
| * remap Destination physical path */ |
| { |
| const char *sep, *sep2; |
| sep = con->uri.path->ptr; |
| sep2 = srv->tmp_buf->ptr; |
| for (i = 0; sep[i] && sep[i] == sep2[i]; ++i) ; |
| while (i != 0 && sep[--i] != '/') ; /* find matching directory path */ |
| } |
| if (con->conf.force_lowercase_filenames) { |
| buffer_to_lower(srv->tmp_buf); |
| } |
| remain = buffer_string_length(con->uri.path) - i; |
| if (!con->conf.force_lowercase_filenames |
| ? buffer_is_equal_right_len(con->physical.path, con->physical.rel_path, remain) |
| :(buffer_string_length(con->physical.path) >= remain |
| && 0 == strncasecmp(con->physical.path->ptr+buffer_string_length(con->physical.path)-remain, con->physical.rel_path->ptr+i, remain))) { |
| buffer_copy_string_len(p->stat_fn, con->physical.path->ptr, buffer_string_length(con->physical.path)-remain); |
| buffer_append_string_len(p->stat_fn, srv->tmp_buf->ptr+i, buffer_string_length(srv->tmp_buf)-i); |
| } else { |
| /* unable to perform physical path remap here; |
| * assume doc_root/rel_path and no remapping */ |
| buffer_copy_buffer(p->stat_fn, con->physical.doc_root); |
| buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); |
| } |
} |
} |
|
|
if (0 == stat(p->stat_fn->ptr, &st)) { | if (0 == stat(p->stat_fn->ptr, &stb)) { |
time_t t = st.st_mtime; | time_t t = stb.st_mtime; |
|
|
switch (ssicmd) { |
switch (ssicmd) { |
case SSI_FSIZE: |
case SSI_FSIZE: |
b = chunkqueue_get_append_buffer(con->write_queue); | b = buffer_init(); |
if (p->sizefmt) { |
if (p->sizefmt) { |
int j = 0; |
int j = 0; |
const char *abr[] = { " B", " kB", " MB", " GB", " TB", NULL }; |
const char *abr[] = { " B", " kB", " MB", " GB", " TB", NULL }; |
|
|
off_t s = st.st_size; | off_t s = stb.st_size; |
|
|
for (j = 0; s > 1024 && abr[j+1]; s /= 1024, j++); |
for (j = 0; s > 1024 && abr[j+1]; s /= 1024, j++); |
|
|
buffer_copy_off_t(b, s); | buffer_copy_int(b, s); |
buffer_append_string(b, abr[j]); |
buffer_append_string(b, abr[j]); |
} else { |
} else { |
buffer_copy_off_t(b, st.st_size); | buffer_copy_int(b, stb.st_size); |
} |
} |
|
chunkqueue_append_buffer(con->write_queue, b); |
|
buffer_free(b); |
break; |
break; |
case SSI_FLASTMOD: |
case SSI_FLASTMOD: |
b = chunkqueue_get_append_buffer(con->write_queue); |
|
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { |
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { |
buffer_copy_string_len(b, CONST_STR_LEN("(none)")); | chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)")); |
} else { |
} else { |
buffer_copy_string(b, buf); | chunkqueue_append_mem(con->write_queue, buf, strlen(buf)); |
} |
} |
break; |
break; |
case SSI_INCLUDE: |
case SSI_INCLUDE: |
chunkqueue_append_file(con->write_queue, p->stat_fn, 0, st.st_size); | chunkqueue_append_file(con->write_queue, p->stat_fn, 0, stb.st_size); |
|
|
/* Keep the newest mtime of included files */ |
/* Keep the newest mtime of included files */ |
if (st.st_mtime > include_file_last_mtime) | if (stb.st_mtime > include_file_last_mtime) |
include_file_last_mtime = st.st_mtime; | include_file_last_mtime = stb.st_mtime; |
|
|
break; |
break; |
} |
} |
Line 636 static int process_ssi_stmt(server *srv, connection *c
|
Line 730 static int process_ssi_stmt(server *srv, connection *c
|
val = l[i+1]; |
val = l[i+1]; |
} else { |
} else { |
log_error_write(srv, __FILE__, __LINE__, "sss", |
log_error_write(srv, __FILE__, __LINE__, "sss", |
"ssi: unknow attribute for ", | "ssi: unknown attribute for ", |
l[1], l[i]); |
l[1], l[i]); |
} |
} |
} |
} |
Line 653 static int process_ssi_stmt(server *srv, connection *c
|
Line 747 static int process_ssi_stmt(server *srv, connection *c
|
buffer_copy_string(ds->value, val); |
buffer_copy_string(ds->value, val); |
|
|
array_insert_unique(p->ssi_vars, (data_unset *)ds); |
array_insert_unique(p->ssi_vars, (data_unset *)ds); |
|
} else if (key || val) { |
|
log_error_write(srv, __FILE__, __LINE__, "sSSss", |
|
"ssi: var and value have to be set in <!--#set", l[1], "=", l[2], "-->"); |
} else { |
} else { |
log_error_write(srv, __FILE__, __LINE__, "sss", | log_error_write(srv, __FILE__, __LINE__, "s", |
"ssi: var and value have to be set in", | "ssi: var and value have to be set in <!--#set var=... value=... -->"); |
l[0], l[1]); | |
} |
} |
break; |
break; |
} |
} |
Line 669 static int process_ssi_stmt(server *srv, connection *c
|
Line 765 static int process_ssi_stmt(server *srv, connection *c
|
} else if (0 == strcmp(l[i], "sizefmt")) { |
} else if (0 == strcmp(l[i], "sizefmt")) { |
if (0 == strcmp(l[i+1], "abbrev")) { |
if (0 == strcmp(l[i+1], "abbrev")) { |
p->sizefmt = 1; |
p->sizefmt = 1; |
} else if (0 == strcmp(l[i+1], "abbrev")) { | } else if (0 == strcmp(l[i+1], "bytes")) { |
p->sizefmt = 0; |
p->sizefmt = 0; |
} else { |
} else { |
log_error_write(srv, __FILE__, __LINE__, "sssss", |
log_error_write(srv, __FILE__, __LINE__, "sssss", |
"ssi: unknow value for attribute '", | "ssi: unknown value for attribute '", |
l[i], |
l[i], |
"' for ", |
"' for ", |
l[1], l[i+1]); |
l[1], l[i+1]); |
} |
} |
} else { |
} else { |
log_error_write(srv, __FILE__, __LINE__, "sss", |
log_error_write(srv, __FILE__, __LINE__, "sss", |
"ssi: unknow attribute for ", | "ssi: unknown attribute for ", |
l[1], l[i]); |
l[1], l[i]); |
} |
} |
} |
} |
Line 688 static int process_ssi_stmt(server *srv, connection *c
|
Line 784 static int process_ssi_stmt(server *srv, connection *c
|
case SSI_PRINTENV: |
case SSI_PRINTENV: |
if (p->if_is_false) break; |
if (p->if_is_false) break; |
|
|
b = chunkqueue_get_append_buffer(con->write_queue); | b = buffer_init(); |
for (i = 0; i < p->ssi_vars->used; i++) { |
for (i = 0; i < p->ssi_vars->used; i++) { |
data_string *ds = (data_string *)p->ssi_vars->data[p->ssi_vars->sorted[i]]; |
data_string *ds = (data_string *)p->ssi_vars->data[p->ssi_vars->sorted[i]]; |
|
|
Line 705 static int process_ssi_stmt(server *srv, connection *c
|
Line 801 static int process_ssi_stmt(server *srv, connection *c
|
buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML); |
buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML); |
buffer_append_string_len(b, CONST_STR_LEN("\n")); |
buffer_append_string_len(b, CONST_STR_LEN("\n")); |
} |
} |
|
chunkqueue_append_buffer(con->write_queue, b); |
|
buffer_free(b); |
|
|
break; |
break; |
case SSI_EXEC: { |
case SSI_EXEC: { |
Line 712 static int process_ssi_stmt(server *srv, connection *c
|
Line 810 static int process_ssi_stmt(server *srv, connection *c
|
pid_t pid; |
pid_t pid; |
int from_exec_fds[2]; |
int from_exec_fds[2]; |
|
|
|
if (!p->conf.ssi_exec) { /* <!--#exec ... --> disabled by config */ |
|
break; |
|
} |
|
|
for (i = 2; i < n; i += 2) { |
for (i = 2; i < n; i += 2) { |
if (0 == strcmp(l[i], "cmd")) { |
if (0 == strcmp(l[i], "cmd")) { |
cmd = l[i+1]; |
cmd = l[i+1]; |
} else { |
} else { |
log_error_write(srv, __FILE__, __LINE__, "sss", |
log_error_write(srv, __FILE__, __LINE__, "sss", |
"ssi: unknow attribute for ", | "ssi: unknown attribute for ", |
l[1], l[i]); |
l[1], l[i]); |
} |
} |
} |
} |
Line 787 static int process_ssi_stmt(server *srv, connection *c
|
Line 889 static int process_ssi_stmt(server *srv, connection *c
|
int toread; |
int toread; |
/* read everything from client and paste it into the output */ |
/* read everything from client and paste it into the output */ |
was_interrupted = 0; |
was_interrupted = 0; |
| |
while(1) { |
while(1) { |
if (ioctl(from_exec_fds[0], FIONREAD, &toread)) { |
if (ioctl(from_exec_fds[0], FIONREAD, &toread)) { |
log_error_write(srv, __FILE__, __LINE__, "s", |
log_error_write(srv, __FILE__, __LINE__, "s", |
"unexpected end-of-file (perhaps the ssi-exec process died)"); |
"unexpected end-of-file (perhaps the ssi-exec process died)"); |
return -1; |
return -1; |
} |
} |
| |
if (toread > 0) { |
if (toread > 0) { |
b = chunkqueue_get_append_buffer(con->write_queue); | char *mem; |
| size_t mem_len; |
buffer_prepare_copy(b, toread + 1); | |
| chunkqueue_get_memory(con->write_queue, &mem, &mem_len, 0, toread); |
if ((r = read(from_exec_fds[0], b->ptr, b->size - 1)) < 0) { | r = read(from_exec_fds[0], mem, mem_len); |
/* read failed */ | chunkqueue_use_memory(con->write_queue, r > 0 ? r : 0); |
break; | |
} else { | if (r < 0) break; /* read failed */ |
b->used = r; | |
b->ptr[b->used++] = '\0'; | |
} | |
} else { |
} else { |
break; |
break; |
} |
} |
Line 837 static int process_ssi_stmt(server *srv, connection *c
|
Line 936 static int process_ssi_stmt(server *srv, connection *c
|
expr = l[i+1]; |
expr = l[i+1]; |
} else { |
} else { |
log_error_write(srv, __FILE__, __LINE__, "sss", |
log_error_write(srv, __FILE__, __LINE__, "sss", |
"ssi: unknow attribute for ", | "ssi: unknown attribute for ", |
l[1], l[i]); |
l[1], l[i]); |
} |
} |
} |
} |
Line 891 static int process_ssi_stmt(server *srv, connection *c
|
Line 990 static int process_ssi_stmt(server *srv, connection *c
|
expr = l[i+1]; |
expr = l[i+1]; |
} else { |
} else { |
log_error_write(srv, __FILE__, __LINE__, "sss", |
log_error_write(srv, __FILE__, __LINE__, "sss", |
"ssi: unknow attribute for ", | "ssi: unknown attribute for ", |
l[1], l[i]); |
l[1], l[i]); |
} |
} |
} |
} |
Line 940 static int process_ssi_stmt(server *srv, connection *c
|
Line 1039 static int process_ssi_stmt(server *srv, connection *c
|
break; |
break; |
default: |
default: |
log_error_write(srv, __FILE__, __LINE__, "ss", |
log_error_write(srv, __FILE__, __LINE__, "ss", |
"ssi: unknow ssi-command:", | "ssi: unknown ssi-command:", |
l[1]); |
l[1]); |
break; |
break; |
} |
} |
Line 949 static int process_ssi_stmt(server *srv, connection *c
|
Line 1048 static int process_ssi_stmt(server *srv, connection *c
|
|
|
} |
} |
|
|
static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p) { | static int mod_ssi_parse_ssi_stmt_value(const char * const s, const int len) { |
stream s; | int n; |
#ifdef HAVE_PCRE_H | const int c = (s[0] == '"' ? '"' : s[0] == '\'' ? '\'' : 0); |
int i, n; | if (0 != c) { |
| for (n = 1; n < len; ++n) { |
| if (s[n] == c) return n+1; |
| if (s[n] == '\\') { |
| if (n+1 == len) return 0; /* invalid */ |
| ++n; |
| } |
| } |
| return 0; /* invalid */ |
| } else { |
| for (n = 0; n < len; ++n) { |
| if (isspace(s[n])) return n; |
| if (s[n] == '\\') { |
| if (n+1 == len) return 0; /* invalid */ |
| ++n; |
| } |
| } |
| return n; |
| } |
| } |
|
|
#define N 10 | static int mod_ssi_parse_ssi_stmt_offlen(int o[10], const char * const s, const int len) { |
int ovec[N * 3]; | |
| /** |
| * <!--#element attribute=value attribute=value ... --> |
| */ |
| |
| /* s must begin "<!--#" and must end with "-->" */ |
| int n = 5; |
| o[0] = n; |
| for (; light_isalpha(s[n]); ++n) ; /*(n = 5 to begin after "<!--#")*/ |
| o[1] = n - o[0]; |
| if (0 == o[1]) return -1; /* empty token */ |
| |
| if (n+3 == len) return 2; /* token only; no params */ |
| if (!isspace(s[n])) return -1; |
| do { ++n; } while (isspace(s[n])); /* string ends "-->", so n < len */ |
| if (n+3 == len) return 2; /* token only; no params */ |
| |
| o[2] = n; |
| for (; light_isalpha(s[n]); ++n) ; |
| o[3] = n - o[2]; |
| if (0 == o[3] || s[n++] != '=') return -1; |
| |
| o[4] = n; |
| o[5] = mod_ssi_parse_ssi_stmt_value(s+n, len-n-3); |
| if (0 == o[5]) return -1; /* empty or invalid token */ |
| n += o[5]; |
| |
| if (n+3 == len) return 6; /* token and one param */ |
| if (!isspace(s[n])) return -1; |
| do { ++n; } while (isspace(s[n])); /* string ends "-->", so n < len */ |
| if (n+3 == len) return 6; /* token and one param */ |
| |
| o[6] = n; |
| for (; light_isalpha(s[n]); ++n) ; |
| o[7] = n - o[6]; |
| if (0 == o[7] || s[n++] != '=') return -1; |
| |
| o[8] = n; |
| o[9] = mod_ssi_parse_ssi_stmt_value(s+n, len-n-3); |
| if (0 == o[9]) return -1; /* empty or invalid token */ |
| n += o[9]; |
| |
| if (n+3 == len) return 10; /* token and two params */ |
| if (!isspace(s[n])) return -1; |
| do { ++n; } while (isspace(s[n])); /* string ends "-->", so n < len */ |
| if (n+3 == len) return 10; /* token and two params */ |
| return -1; |
| } |
| |
| static void mod_ssi_parse_ssi_stmt(server *srv, connection *con, plugin_data *p, char *s, int len, struct stat *st) { |
| |
| /** |
| * <!--#element attribute=value attribute=value ... --> |
| */ |
| |
| int o[10]; |
| int m; |
| const int n = mod_ssi_parse_ssi_stmt_offlen(o, s, len); |
| char *l[6] = { s, NULL, NULL, NULL, NULL, NULL }; |
| if (-1 == n) { |
| /* XXX: perhaps emit error comment instead of invalid <!--#...--> code to client */ |
| chunkqueue_append_mem(con->write_queue, s, len); /* append stmt as-is */ |
| return; |
| } |
| |
| #if 0 |
| /* dup s and then modify s */ |
| /*(l[0] is no longer used; was previously used in only one place for error reporting)*/ |
| l[0] = malloc((size_t)(len+1)); |
| memcpy(l[0], s, (size_t)len); |
| (l[0])[len] = '\0'; |
| #endif |
| |
| /* modify s in-place to split string into arg tokens */ |
| for (m = 0; m < n; m += 2) { |
| char *ptr = s+o[m]; |
| switch (*ptr) { |
| case '"': |
| case '\'': (++ptr)[o[m+1]-2] = '\0'; break; |
| default: ptr[o[m+1]] = '\0'; break; |
| } |
| l[1+(m>>1)] = ptr; |
| if (m == 4 || m == 8) { |
| /* XXX: removing '\\' escapes from param value would be |
| * the right thing to do, but would potentially change |
| * current behavior, e.g. <!--#exec cmd=... --> */ |
| } |
| } |
| |
| process_ssi_stmt(srv, con, p, (const char **)l, 1+(n>>1), st); |
| |
| #if 0 |
| free(l[0]); |
| #endif |
| } |
| |
| static int mod_ssi_stmt_len(const char *s, const int len) { |
| /* s must begin "<!--#" */ |
| int n, sq = 0, dq = 0, bs = 0; |
| for (n = 5; n < len; ++n) { /*(n = 5 to begin after "<!--#")*/ |
| switch (s[n]) { |
| default: |
| break; |
| case '-': |
| if (!sq && !dq && n+2 < len && s[n+1] == '-' && s[n+2] == '>') return n+3; /* found end of stmt */ |
| break; |
| case '"': |
| if (!sq && (!dq || !bs)) dq = !dq; |
| break; |
| case '\'': |
| if (!dq && (!sq || !bs)) sq = !sq; |
| break; |
| case '\\': |
| if (sq || dq) bs = !bs; |
| break; |
| } |
| } |
| return 0; /* incomplete directive "<!--#...-->" */ |
| } |
| |
| static void mod_ssi_read_fd(server *srv, connection *con, plugin_data *p, int fd, struct stat *st) { |
| ssize_t rd; |
| size_t offset, pretag; |
| char buf[8192]; |
| |
| offset = 0; |
| pretag = 0; |
| while (0 < (rd = read(fd, buf+offset, sizeof(buf)-offset))) { |
| char *s; |
| size_t prelen = 0, len; |
| offset += (size_t)rd; |
| for (; (s = memchr(buf+prelen, '<', offset-prelen)); ++prelen) { |
| prelen = s - buf; |
| if (prelen + 5 <= offset) { /*("<!--#" is 5 chars)*/ |
| if (0 != memcmp(s+1, CONST_STR_LEN("!--#"))) continue; /* loop to loop for next '<' */ |
| |
| if (prelen - pretag && !p->if_is_false) { |
| chunkqueue_append_mem(con->write_queue, buf+pretag, prelen-pretag); |
| } |
| |
| len = mod_ssi_stmt_len(buf+prelen, offset-prelen); |
| if (len) { /* num of chars to be consumed */ |
| mod_ssi_parse_ssi_stmt(srv, con, p, buf+prelen, len, st); |
| prelen += (len - 1); /* offset to '>' at end of SSI directive; incremented at top of loop */ |
| pretag = prelen + 1; |
| if (pretag == offset) { |
| offset = pretag = 0; |
| break; |
| } |
| } else if (0 == prelen && offset == sizeof(buf)) { /*(full buf)*/ |
| /* SSI statement is way too long |
| * NOTE: skipping this buf will expose *the rest* of this SSI statement */ |
| chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("<!-- [an error occurred: directive too long] ")); |
| /* check if buf ends with "-" or "--" which might be part of "-->" |
| * (buf contains at least 5 chars for "<!--#") */ |
| if (buf[offset-2] == '-' && buf[offset-1] == '-') { |
| chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("--")); |
| } else if (buf[offset-1] == '-') { |
| chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("-")); |
| } |
| offset = pretag = 0; |
| break; |
| } else { /* incomplete directive "<!--#...-->" */ |
| memmove(buf, buf+prelen, (offset -= prelen)); |
| pretag = 0; |
| break; |
| } |
| } else if (prelen + 1 == offset || 0 == memcmp(s+1, "!--", offset - prelen - 1)) { |
| if (prelen - pretag && !p->if_is_false) { |
| chunkqueue_append_mem(con->write_queue, buf+pretag, prelen-pretag); |
| } |
| memcpy(buf, buf+prelen, (offset -= prelen)); |
| pretag = 0; |
| break; |
| } |
| /* loop to look for next '<' */ |
| } |
| if (offset == sizeof(buf)) { |
| if (!p->if_is_false) { |
| chunkqueue_append_mem(con->write_queue, buf+pretag, offset-pretag); |
| } |
| offset = pretag = 0; |
| } |
| } |
| |
| if (0 != rd) { |
| log_error_write(srv, __FILE__, __LINE__, "SsB", "read(): ", strerror(errno), con->physical.path); |
| } |
| |
| if (offset - pretag) { |
| /* copy remaining data in buf */ |
| if (!p->if_is_false) { |
| chunkqueue_append_mem(con->write_queue, buf+pretag, offset-pretag); |
| } |
| } |
| } |
| |
| |
| /* don't want to block when open()ing a fifo */ |
| #if defined(O_NONBLOCK) |
| # define FIFO_NONBLOCK O_NONBLOCK |
| #else |
| # define FIFO_NONBLOCK 0 |
#endif |
#endif |
|
|
|
static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p) { |
|
int fd; |
|
struct stat st; |
|
|
/* get a stream to the file */ |
/* get a stream to the file */ |
|
|
array_reset(p->ssi_vars); |
array_reset(p->ssi_vars); |
Line 970 static int mod_ssi_handle_request(server *srv, connect
|
Line 1294 static int mod_ssi_handle_request(server *srv, connect
|
/* Reset the modified time of included files */ |
/* Reset the modified time of included files */ |
include_file_last_mtime = 0; |
include_file_last_mtime = 0; |
|
|
if (-1 == stream_open(&s, con->physical.path)) { | if (-1 == (fd = open(con->physical.path->ptr, O_RDONLY | FIFO_NONBLOCK))) { |
log_error_write(srv, __FILE__, __LINE__, "sb", |
log_error_write(srv, __FILE__, __LINE__, "sb", |
"stream-open: ", con->physical.path); | "open: ", con->physical.path); |
return -1; |
return -1; |
} |
} |
|
|
| if (0 != fstat(fd, &st)) { |
/** | log_error_write(srv, __FILE__, __LINE__, "SB", "fstat failed: ", con->physical.path); |
* <!--#element attribute=value attribute=value ... --> | close(fd); |
* | return -1; |
* config DONE | |
* errmsg -- missing | |
* sizefmt DONE | |
* timefmt DONE | |
* echo DONE | |
* var DONE | |
* encoding -- missing | |
* exec DONE | |
* cgi -- never | |
* cmd DONE | |
* fsize DONE | |
* file DONE | |
* virtual DONE | |
* flastmod DONE | |
* file DONE | |
* virtual DONE | |
* include DONE | |
* file DONE | |
* virtual DONE | |
* printenv DONE | |
* set DONE | |
* var DONE | |
* value DONE | |
* | |
* if DONE | |
* elif DONE | |
* else DONE | |
* endif DONE | |
* | |
* | |
* expressions | |
* AND, OR DONE | |
* comp DONE | |
* ${...} -- missing | |
* $... DONE | |
* '...' DONE | |
* ( ... ) DONE | |
* | |
* | |
* | |
* ** all DONE ** | |
* DATE_GMT | |
* The current date in Greenwich Mean Time. | |
* DATE_LOCAL | |
* The current date in the local time zone. | |
* DOCUMENT_NAME | |
* The filename (excluding directories) of the document requested by the user. | |
* DOCUMENT_URI | |
* The (%-decoded) URL path of the document requested by the user. Note that in the case of nested include files, this is not then URL for the current document. | |
* LAST_MODIFIED | |
* The last modification date of the document requested by the user. | |
* USER_NAME | |
* Contains the owner of the file which included it. | |
* | |
*/ | |
#ifdef HAVE_PCRE_H | |
for (i = 0; (n = pcre_exec(p->ssi_regex, NULL, s.start, s.size, i, 0, ovec, N * 3)) > 0; i = ovec[1]) { | |
const char **l; | |
/* take everything from last offset to current match pos */ | |
| |
if (!p->if_is_false) chunkqueue_append_file(con->write_queue, con->physical.path, i, ovec[0] - i); | |
| |
pcre_get_substring_list(s.start, ovec, n, &l); | |
process_ssi_stmt(srv, con, p, l, n); | |
pcre_free_substring_list(l); | |
} |
} |
|
|
switch(n) { | mod_ssi_read_fd(srv, con, p, fd, &st); |
case PCRE_ERROR_NOMATCH: | |
/* copy everything/the rest */ | |
chunkqueue_append_file(con->write_queue, con->physical.path, i, s.size - i); | |
|
|
break; | close(fd); |
default: | |
log_error_write(srv, __FILE__, __LINE__, "sd", | |
"execution error while matching: ", n); | |
break; | |
} | |
#endif | |
| |
| |
stream_close(&s); | |
| |
con->file_started = 1; |
con->file_started = 1; |
con->file_finished = 1; |
con->file_finished = 1; |
con->mode = p->id; |
con->mode = p->id; |
|
|
if (p->conf.content_type->used <= 1) { | if (buffer_string_is_empty(p->conf.content_type)) { |
response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); |
response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); |
} else { |
} else { |
response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->conf.content_type)); |
response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->conf.content_type)); |
} |
} |
|
|
{ | if (p->conf.conditional_requests) { |
/* Generate "ETag" & "Last-Modified" headers */ | /* Generate "ETag" & "Last-Modified" headers */ |
| |
stat_cache_entry *sce = NULL; | |
time_t lm_time = 0; | |
buffer *mtime = NULL; |
buffer *mtime = NULL; |
|
|
stat_cache_get_entry(srv, con, con->physical.path, &sce); | /* use most recently modified include file for ETag and Last-Modified */ |
| if (st.st_mtime < include_file_last_mtime) |
| st.st_mtime = include_file_last_mtime; |
|
|
etag_mutate(con->physical.etag, sce->etag); | etag_create(con->physical.etag, &st, con->etag_flags); |
response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); |
response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); |
|
|
if (sce->st.st_mtime > include_file_last_mtime) | mtime = strftime_cache_get(srv, st.st_mtime); |
lm_time = sce->st.st_mtime; | |
else | |
lm_time = include_file_last_mtime; | |
| |
mtime = strftime_cache_get(srv, lm_time); | |
response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); |
response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); |
|
|
|
if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { |
|
/* ok, the client already has our content, |
|
* no need to send it again */ |
|
|
|
chunkqueue_reset(con->write_queue); |
|
} |
} |
} |
|
|
/* Reset the modified time of included files */ |
/* Reset the modified time of included files */ |
Line 1111 static int mod_ssi_patch_connection(server *srv, conne
|
Line 1358 static int mod_ssi_patch_connection(server *srv, conne
|
|
|
PATCH(ssi_extension); |
PATCH(ssi_extension); |
PATCH(content_type); |
PATCH(content_type); |
|
PATCH(conditional_requests); |
|
PATCH(ssi_exec); |
|
|
/* skip the first, the global context */ |
/* skip the first, the global context */ |
for (i = 1; i < srv->config_context->used; i++) { |
for (i = 1; i < srv->config_context->used; i++) { |
Line 1128 static int mod_ssi_patch_connection(server *srv, conne
|
Line 1377 static int mod_ssi_patch_connection(server *srv, conne
|
PATCH(ssi_extension); |
PATCH(ssi_extension); |
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.content-type"))) { |
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.content-type"))) { |
PATCH(content_type); |
PATCH(content_type); |
|
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.conditional-requests"))) { |
|
PATCH(conditional_requests); |
|
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.exec"))) { |
|
PATCH(ssi_exec); |
} |
} |
} |
} |
} |
} |
Line 1142 URIHANDLER_FUNC(mod_ssi_physical_path) {
|
Line 1395 URIHANDLER_FUNC(mod_ssi_physical_path) {
|
|
|
if (con->mode != DIRECT) return HANDLER_GO_ON; |
if (con->mode != DIRECT) return HANDLER_GO_ON; |
|
|
if (con->physical.path->used == 0) return HANDLER_GO_ON; | if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; |
|
|
mod_ssi_patch_connection(srv, con, p); |
mod_ssi_patch_connection(srv, con, p); |
|
|
for (k = 0; k < p->conf.ssi_extension->used; k++) { |
for (k = 0; k < p->conf.ssi_extension->used; k++) { |
data_string *ds = (data_string *)p->conf.ssi_extension->data[k]; |
data_string *ds = (data_string *)p->conf.ssi_extension->data[k]; |
|
|
if (ds->value->used == 0) continue; | if (buffer_is_empty(ds->value)) continue; |
|
|
if (buffer_is_equal_right_len(con->physical.path, ds->value, ds->value->used - 1)) { | if (buffer_is_equal_right_len(con->physical.path, ds->value, buffer_string_length(ds->value))) { |
/* handle ssi-request */ |
/* handle ssi-request */ |
|
|
if (mod_ssi_handle_request(srv, con, p)) { |
if (mod_ssi_handle_request(srv, con, p)) { |