lighttpd 1.4.x https://www.lighttpd.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1320 lines
34 KiB

#include "first.h"
#include "base.h"
#include "log.h"
#include "buffer.h"
#include "plugin.h"
#include "stream.h"
#include "response.h"
#include "mod_ssi.h"
#include "inet_ntop_cache.h"
#include "sys-socket.h"
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
6 years ago
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
#ifdef HAVE_FORK
# include <sys/wait.h>
#endif
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
#include "etag.h"
#include "version.h"
/* The newest modified time of included files for include statement */
static volatile time_t include_file_last_mtime = 0;
/* init the plugin data */
INIT_FUNC(mod_ssi_init) {
plugin_data *p;
p = calloc(1, sizeof(*p));
p->timefmt = buffer_init();
p->stat_fn = buffer_init();
p->ssi_vars = array_init();
p->ssi_cgi_env = array_init();
return p;
}
/* detroy the plugin data */
FREE_FUNC(mod_ssi_free) {
plugin_data *p = p_d;
UNUSED(srv);
if (!p) return HANDLER_GO_ON;
if (p->config_storage) {
size_t i;
for (i = 0; i < srv->config_context->used; i++) {
plugin_config *s = p->config_storage[i];
if (NULL == s) continue;
array_free(s->ssi_extension);
buffer_free(s->content_type);
free(s);
}
free(p->config_storage);
}
array_free(p->ssi_vars);
array_free(p->ssi_cgi_env);
#ifdef HAVE_PCRE_H
pcre_free(p->ssi_regex);
#endif
buffer_free(p->timefmt);
buffer_free(p->stat_fn);
free(p);
return HANDLER_GO_ON;
}
/* handle plugin config and check values */
SETDEFAULTS_FUNC(mod_ssi_set_defaults) {
plugin_data *p = p_d;
size_t i = 0;
#ifdef HAVE_PCRE_H
const char *errptr;
int erroff;
#endif
config_values_t cv[] = {
{ "ssi.extension", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
{ "ssi.content-type", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
[mod_ssi] config ssi.conditional-requests Summary: A new SSI directive, "ssi.conditional-requests", allows to inform lighttpd which SSI pages should be considered as cacheable and which should not. In particular, the "ETag" & "Last-Modified" headers will only be sent for those SSI pages for which the directive is enabled. Long description: "ETag" and "Last-Modified" headers were being sent for all SSI pages, regardless of whether they were cacheable or not. And yet, there was no cache validation at all for any SSI page. This commit fixes these two minor issues by adding a new directive, "ssi.conditional-requests", which allows to specify which SSI pages are cacheable and which are not, and by adding cache validation to those SSI pages which are cacheable. And since sending ETags for non-cacheable documents is not appropriate, they are no longuer computed nor sent for those SSI pages which are not cacheable. Regarding the "Last-Modified" header for non-cacheable documents, the standards allow to either send the current date and time for that header or to simply skip it. The approach chosen is to not send it for non-cacheable SSI pages. "ETag" and "Last-Modified" headers are therefore only sent for an SSI page if ssi.conditional-requests is enabled for that page. The ssi.conditional-requests directive can be enabled or disabled globally and/or in any context. It is disabled by default. An index.shtml which only includes deterministic SSI commands such as: <!--#echo var="LAST_MODIFIED"--> is a trivial example of a dynamic SSI page that is cacheable.
6 years ago
{ "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 }
};
if (!p) return HANDLER_ERROR;
p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
for (i = 0; i < srv->config_context->used; i++) {
data_config const* config = (data_config const*)srv->config_context->data[i];
plugin_config *s;
s = calloc(1, sizeof(plugin_config));
s->ssi_extension = array_init();
s->content_type = buffer_init();
[mod_ssi] config ssi.conditional-requests Summary: A new SSI directive, "ssi.conditional-requests", allows to inform lighttpd which SSI pages should be considered as cacheable and which should not. In particular, the "ETag" & "Last-Modified" headers will only be sent for those SSI pages for which the directive is enabled. Long description: "ETag" and "Last-Modified" headers were being sent for all SSI pages, regardless of whether they were cacheable or not. And yet, there was no cache validation at all for any SSI page. This commit fixes these two minor issues by adding a new directive, "ssi.conditional-requests", which allows to specify which SSI pages are cacheable and which are not, and by adding cache validation to those SSI pages which are cacheable. And since sending ETags for non-cacheable documents is not appropriate, they are no longuer computed nor sent for those SSI pages which are not cacheable. Regarding the "Last-Modified" header for non-cacheable documents, the standards allow to either send the current date and time for that header or to simply skip it. The approach chosen is to not send it for non-cacheable SSI pages. "ETag" and "Last-Modified" headers are therefore only sent for an SSI page if ssi.conditional-requests is enabled for that page. The ssi.conditional-requests directive can be enabled or disabled globally and/or in any context. It is disabled by default. An index.shtml which only includes deterministic SSI commands such as: <!--#echo var="LAST_MODIFIED"--> is a trivial example of a dynamic SSI page that is cacheable.
6 years ago
s->conditional_requests = 0;
s->ssi_exec = 1;
cv[0].destination = s->ssi_extension;
cv[1].destination = s->content_type;
[mod_ssi] config ssi.conditional-requests Summary: A new SSI directive, "ssi.conditional-requests", allows to inform lighttpd which SSI pages should be considered as cacheable and which should not. In particular, the "ETag" & "Last-Modified" headers will only be sent for those SSI pages for which the directive is enabled. Long description: "ETag" and "Last-Modified" headers were being sent for all SSI pages, regardless of whether they were cacheable or not. And yet, there was no cache validation at all for any SSI page. This commit fixes these two minor issues by adding a new directive, "ssi.conditional-requests", which allows to specify which SSI pages are cacheable and which are not, and by adding cache validation to those SSI pages which are cacheable. And since sending ETags for non-cacheable documents is not appropriate, they are no longuer computed nor sent for those SSI pages which are not cacheable. Regarding the "Last-Modified" header for non-cacheable documents, the standards allow to either send the current date and time for that header or to simply skip it. The approach chosen is to not send it for non-cacheable SSI pages. "ETag" and "Last-Modified" headers are therefore only sent for an SSI page if ssi.conditional-requests is enabled for that page. The ssi.conditional-requests directive can be enabled or disabled globally and/or in any context. It is disabled by default. An index.shtml which only includes deterministic SSI commands such as: <!--#echo var="LAST_MODIFIED"--> is a trivial example of a dynamic SSI page that is cacheable.
6 years ago
cv[2].destination = &(s->conditional_requests);
cv[3].destination = &(s->ssi_exec);
p->config_storage[i] = s;
if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
return HANDLER_ERROR;
}
}
#ifdef HAVE_PCRE_H
/* allow 2 params */
6 years ago
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;
}
6 years ago
#ifdef HAVE_PCRE_H
static int ssi_env_add(array *env, const char *key, const char *val) {
data_string *ds;
if (NULL == (ds = (data_string *)array_get_unused_element(env, TYPE_STRING))) {
ds = data_string_init();
}
buffer_copy_string(ds->key, key);
buffer_copy_string(ds->value, val);
array_insert_unique(env, (data_unset *)ds);
return 0;
}
/**
*
* the next two functions are take from fcgi.c
*
*/
static int ssi_env_add_request_headers(server *srv, connection *con, plugin_data *p) {
size_t i;
for (i = 0; i < con->request.headers->used; i++) {
data_string *ds;
ds = (data_string *)con->request.headers->data[i];
if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
/* don't forward the Authorization: Header */
if (0 == strcasecmp(ds->key->ptr, "AUTHORIZATION")) {
continue;
}
buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, CONST_BUF_LEN(ds->key), 1);
ssi_env_add(p->ssi_cgi_env, srv->tmp_buf->ptr, ds->value->ptr);
}
}
for (i = 0; i < con->environment->used; i++) {
data_string *ds;
ds = (data_string *)con->environment->data[i];
if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, CONST_BUF_LEN(ds->key), 0);
ssi_env_add(p->ssi_cgi_env, srv->tmp_buf->ptr, ds->value->ptr);
}
}
return 0;
}
static int build_ssi_cgi_vars(server *srv, connection *con, plugin_data *p) {
fix buffer, chunk and http_chunk API * remove unused structs and functions (buffer_array, read_buffer) * change return type from int to void for many functions, as the return value (indicating error/success) was never checked, and the function would only fail on programming errors and not on invalid input; changed functions to use force_assert instead of returning an error. * all "len" parameters now are the real size of the memory to be read. the length of strings is given always without the terminating 0. * the "buffer" struct still counts the terminating 0 in ->used, provide buffer_string_length() to get the length of a string in a buffer. unset config "strings" have used == 0, which is used in some places to distinguish unset values from "" (empty string) values. * most buffer usages should now use it as string container. * optimise some buffer copying by "moving" data to other buffers * use (u)intmax_t for generic int-to-string functions * remove unused enum values: UNUSED_CHUNK, ENCODING_UNSET * converted BUFFER_APPEND_SLASH to inline function (no macro feature needed) * refactor: create chunkqueue_steal: moving (partial) chunks into another queue * http_chunk: added separate function to terminate chunked body instead of magic handling in http_chunk_append_mem(). http_chunk_append_* now handle empty chunks, and never terminate the chunked body. From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2975 152afb58-edef-0310-8abb-c4023f1b3aa9
7 years ago
char buf[LI_ITOSTRING_LENGTH];
server_socket *srv_sock = con->srv_socket;
#ifdef HAVE_IPV6
char b2[INET6_ADDRSTRLEN + 1];
#endif
#define CONST_STRING(x) \
x
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_NAME"),
#ifdef HAVE_IPV6
inet_ntop(srv_sock->addr.plain.sa_family,
srv_sock->addr.plain.sa_family == AF_INET6 ?
(const void *) &(srv_sock->addr.ipv6.sin6_addr) :
(const void *) &(srv_sock->addr.ipv4.sin_addr),
b2, sizeof(b2)-1)
#else
inet_ntoa(srv_sock->addr.ipv4.sin_addr)
#endif
);
ssi_env_add(p->ssi_cgi_env, CONST_STRING("GATEWAY_INTERFACE"), "CGI/1.1");
li_utostrn(buf, sizeof(buf),
#ifdef HAVE_IPV6
ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
#else
ntohs(srv_sock->addr.ipv4.sin_port)
#endif
);
ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_PORT"), buf);
ssi_env_add(p->ssi_cgi_env, CONST_STRING("REMOTE_ADDR"),
inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
if (con->request.content_length > 0) {
/* CGI-SPEC 6.1.2 and FastCGI spec 6.3 */
li_itostrn(buf, sizeof(buf), con->request.content_length);
ssi_env_add(p->ssi_cgi_env, CONST_STRING("CONTENT_LENGTH"), buf);
}
/*
* SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED according to
* http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html
* (6.1.14, 6.1.6, 6.1.7)
*/
ssi_env_add(p->ssi_cgi_env, CONST_STRING("SCRIPT_NAME"), con->uri.path->ptr);
ssi_env_add(p->ssi_cgi_env, CONST_STRING("PATH_INFO"), "");
/*
* SCRIPT_FILENAME and DOCUMENT_ROOT for php. The PHP manual
* http://www.php.net/manual/en/reserved.variables.php
* treatment of PATH_TRANSLATED is different from the one of CGI specs.
* TODO: this code should be checked against cgi.fix_pathinfo php
* parameter.
*/
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("SCRIPT_FILENAME"), con->physical.path->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);
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("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);
return 0;
}
6 years ago
static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const char **l, size_t n, struct stat *st) {
/**
* <!--#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;
char buf[255];
buffer *b = NULL;
struct {
const char *var;
enum { SSI_UNSET, SSI_ECHO, SSI_FSIZE, SSI_INCLUDE, SSI_FLASTMOD,
SSI_CONFIG, SSI_PRINTENV, SSI_SET, SSI_IF, SSI_ELIF,
SSI_ELSE, SSI_ENDIF, SSI_EXEC } type;
} ssicmds[] = {
{ "echo", SSI_ECHO },
{ "include", SSI_INCLUDE },
{ "flastmod", SSI_FLASTMOD },
{ "fsize", SSI_FSIZE },
{ "config", SSI_CONFIG },
{ "printenv", SSI_PRINTENV },
{ "set", SSI_SET },
{ "if", SSI_IF },
{ "elif", SSI_ELIF },
{ "endif", SSI_ENDIF },
{ "else", SSI_ELSE },
{ "exec", SSI_EXEC },
{ NULL, SSI_UNSET }
};
for (i = 0; ssicmds[i].var; i++) {
if (0 == strcmp(l[1], ssicmds[i].var)) {
ssicmd = ssicmds[i].type;
break;
}
}
switch(ssicmd) {
case SSI_ECHO: {
/* echo */
int var = 0;
/* int enc = 0; */
const char *var_val = NULL;
struct {
const char *var;
enum {
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[] = {
{ "DATE_GMT", SSI_ECHO_DATE_GMT },
{ "DATE_LOCAL", SSI_ECHO_DATE_LOCAL },
{ "DOCUMENT_NAME", SSI_ECHO_DOCUMENT_NAME },
{ "DOCUMENT_URI", SSI_ECHO_DOCUMENT_URI },
{ "LAST_MODIFIED", SSI_ECHO_LAST_MODIFIED },
{ "USER_NAME", SSI_ECHO_USER_NAME },
{ "SCRIPT_URI", SSI_ECHO_SCRIPT_URI },
{ "SCRIPT_URL", SSI_ECHO_SCRIPT_URL },
{ NULL, SSI_ECHO_UNSET }
};
/*
struct {
const char *var;
enum { SSI_ENC_UNSET, SSI_ENC_URL, SSI_ENC_NONE, SSI_ENC_ENTITY } type;
} encvars[] = {
{ "url", SSI_ENC_URL },
{ "none", SSI_ENC_NONE },
{ "entity", SSI_ENC_ENTITY },
{ NULL, SSI_ENC_UNSET }
};
*/
for (i = 2; i < n; i += 2) {
if (0 == strcmp(l[i], "var")) {
int j;
var_val = l[i+1];
for (j = 0; echovars[j].var; j++) {
if (0 == strcmp(l[i+1], echovars[j].var)) {
var = echovars[j].type;
break;
}
}
} else if (0 == strcmp(l[i], "encoding")) {
/*
int j;
for (j = 0; encvars[j].var; j++) {
if (0 == strcmp(l[i+1], encvars[j].var)) {
enc = encvars[j].type;
break;
}
}
*/
} else {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: unknow attribute for ",
l[1], l[i]);
}
}
if (p->if_is_false) break;
if (!var_val) {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: ",
l[1], "var is missing");
break;
}
switch(var) {
case SSI_ECHO_USER_NAME: {
struct passwd *pw;
b = buffer_init();
#ifdef HAVE_PWD_H
6 years ago
if (NULL == (pw = getpwuid(st->st_uid))) {
buffer_copy_int(b, st->st_uid);
} else {
buffer_copy_string(b, pw->pw_name);
}
#else
6 years ago
buffer_copy_int(b, st->st_uid);
#endif
chunkqueue_append_buffer(con->write_queue, b);
buffer_free(b);
break;
}
case SSI_ECHO_LAST_MODIFIED: {
6 years ago
time_t t = st->st_mtime;
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) {
chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)"));
} else {
chunkqueue_append_mem(con->write_queue, buf, strlen(buf));
}
break;
}
case SSI_ECHO_DATE_LOCAL: {
time_t t = time(NULL);
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) {
chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)"));
} else {
chunkqueue_append_mem(con->write_queue, buf, strlen(buf));
}
break;
}
case SSI_ECHO_DATE_GMT: {
time_t t = time(NULL);
if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, gmtime(&t))) {
chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)"));
} else {
chunkqueue_append_mem(con->write_queue, buf, strlen(buf));
}
break;
}
case SSI_ECHO_DOCUMENT_NAME: {
char *sl;
if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) {
chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->physical.path));
} else {
chunkqueue_append_mem(con->write_queue, sl + 1, strlen(sl + 1));
}
break;
}
case SSI_ECHO_DOCUMENT_URI: {
chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.path));
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: {
data_string *ds;
/* check if it is a cgi-var or a ssi-var */
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))) {
chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(ds->value));
} else {
chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)"));
}
break;
}
}
break;
}
case SSI_INCLUDE:
case SSI_FLASTMOD:
case SSI_FSIZE: {
const char * file_path = NULL, *virt_path = NULL;
6 years ago
struct stat stb;
char *sl;
for (i = 2; i < n; i += 2) {
if (0 == strcmp(l[i], "file")) {
file_path = l[i+1];
} else if (0 == strcmp(l[i], "virtual")) {
virt_path = l[i+1];
} else {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: unknow attribute for ",
l[1], l[i]);
}
}
if (!file_path && !virt_path) {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: ",
l[1], "file or virtual are missing");
break;
}
if (file_path && virt_path) {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: ",
l[1], "only one of file and virtual is allowed here");
break;
}
if (p->if_is_false) break;
if (file_path) {
/* current doc-root */
if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) {
buffer_copy_string_len(p->stat_fn, CONST_STR_LEN("/"));
} else {
buffer_copy_string_len(p->stat_fn, con->physical.path->ptr, sl - con->physical.path->ptr + 1);
}
buffer_copy_string(srv->tmp_buf, file_path);
buffer_urldecode_path(srv->tmp_buf);
buffer_path_simplify(srv->tmp_buf, srv->tmp_buf);
buffer_append_string_buffer(p->stat_fn, srv->tmp_buf);
} else {
/* virtual */
if (virt_path[0] == '/') {
buffer_copy_string(p->stat_fn, virt_path);
} else {
/* there is always a / */
sl = strrchr(con->uri.path->ptr, '/');
buffer_copy_string_len(p->stat_fn, con->uri.path->ptr, sl - con->uri.path->ptr + 1);
buffer_append_string(p->stat_fn, virt_path);
}
buffer_urldecode_path(p->stat_fn);