示例#1
0
static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p) {
	stream s;
#ifdef  HAVE_PCRE_H
	int i, n;

#define N 10
	int ovec[N * 3];
#endif

	stat_cache_entry *sce = NULL;


	/* get a stream to the file */

	array_reset(p->ssi_vars);
	array_reset(p->ssi_cgi_env);
	buffer_copy_string_len(p->timefmt, CONST_STR_LEN("%a, %d %b %Y %H:%M:%S %Z"));
	p->sizefmt = 0;
	build_ssi_cgi_vars(srv, con, p);
	p->if_is_false = 0;

	/* Reset the modified time of included files */
	include_file_last_mtime = 0;

	if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
		log_error_write(srv, __FILE__, __LINE__,  "SB", "stat_cache_get_entry failed: ", con->physical.path);
		return -1;
	}

	if (-1 == stream_open(&s, con->physical.path)) {
		log_error_write(srv, __FILE__, __LINE__, "sb",
				"stream-open: ", con->physical.path);
		return -1;
	}


	/**
	 * <!--#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.
	 *
	 */
#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, sce);
		pcre_free_substring_list(l);
	}

	switch(n) {
	case PCRE_ERROR_NOMATCH:
		/* copy everything/the rest */
		chunkqueue_append_file(con->write_queue, con->physical.path, i, s.size - i);

		break;
	default:
		log_error_write(srv, __FILE__, __LINE__, "sd",
				"execution error while matching: ", n);
		break;
	}
#endif


	stream_close(&s);

	con->file_started  = 1;
	con->file_finished = 1;
	con->mode = p->id;

	if (p->conf.content_type->used <= 1) {
		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
	} else {
		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->conf.content_type));
	}

	{
		/* Generate "ETag" & "Last-Modified" headers */
		time_t lm_time = 0;
		buffer *mtime = NULL;

		etag_mutate(con->physical.etag, sce->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)
			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));
	}

	/* Reset the modified time of included files */
	include_file_last_mtime = 0;

	/* reset physical.path */
	buffer_reset(con->physical.path);

	return 0;
}
void http_response_send_file (server *srv, connection *con, buffer *path) {
    stat_cache_entry *sce = NULL;
    buffer *mtime = NULL;
    data_string *ds;
    int allow_caching = (0 == con->http_status || 200 == con->http_status);

    if (HANDLER_ERROR == stat_cache_get_entry(srv, con, path, &sce)) {
        con->http_status = (errno == ENOENT) ? 404 : 403;

        log_error_write(srv, __FILE__, __LINE__, "sbsb",
                        "not a regular file:", con->uri.path,
                        "->", path);

        return;
    }

    /* we only handline regular files */
#ifdef HAVE_LSTAT
    if ((sce->is_symlink == 1) && !con->conf.follow_symlink) {
        con->http_status = 403;

        if (con->conf.log_request_handling) {
            log_error_write(srv, __FILE__, __LINE__,  "s",  "-- access denied due symlink restriction");
            log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", path);
        }

        return;
    }
#endif
    if (!S_ISREG(sce->st.st_mode)) {
        con->http_status = 403;

        if (con->conf.log_file_not_found) {
            log_error_write(srv, __FILE__, __LINE__, "sbsb",
                            "not a regular file:", con->uri.path,
                            "->", sce->name);
        }

        return;
    }

    /* mod_compress might set several data directly, don't overwrite them */

    /* set response content-type, if not set already */

    if (NULL == array_get_element(con->response.headers, "Content-Type")) {
        if (buffer_string_is_empty(sce->content_type)) {
            /* we are setting application/octet-stream, but also announce that
             * this header field might change in the seconds few requests
             *
             * This should fix the aggressive caching of FF and the script download
             * seen by the first installations
             */
            response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream"));

            allow_caching = 0;
        } else {
            response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
        }
    }

    if (con->conf.range_requests) {
        response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes"));
    }

    if (allow_caching) {
        if (con->etag_flags != 0 && !buffer_string_is_empty(sce->etag)) {
            if (NULL == array_get_element(con->response.headers, "ETag")) {
                /* generate e-tag */
                etag_mutate(con->physical.etag, sce->etag);

                response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
            }
        }

        /* prepare header */
        if (NULL == (ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"))) {
            mtime = strftime_cache_get(srv, sce->st.st_mtime);
            response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
        } else {
            mtime = ds->value;
        }

        if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
            return;
        }
    }

    if (con->request.http_range && con->conf.range_requests
            && (200 == con->http_status || 0 == con->http_status)
            && NULL == array_get_element(con->response.headers, "Content-Encoding")) {
        int do_range_request = 1;
        /* check if we have a conditional GET */

        if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If-Range"))) {
            /* if the value is the same as our ETag, we do a Range-request,
             * otherwise a full 200 */

            if (ds->value->ptr[0] == '"') {
                /**
                 * client wants a ETag
                 */
                if (!con->physical.etag) {
                    do_range_request = 0;
                } else if (!buffer_is_equal(ds->value, con->physical.etag)) {
                    do_range_request = 0;
                }
            } else if (!mtime) {
                /**
                 * we don't have a Last-Modified and can match the If-Range:
                 *
                 * sending all
                 */
                do_range_request = 0;
            } else if (!buffer_is_equal(ds->value, mtime)) {
                do_range_request = 0;
            }
        }

        if (do_range_request) {
            /* content prepared, I'm done */
            con->file_finished = 1;

            if (0 == http_response_parse_range(srv, con, path, sce)) {
                con->http_status = 206;
            }
            return;
        }
    }

    /* if we are still here, prepare body */

    /* we add it here for all requests
     * the HEAD request will drop it afterwards again
     */
    if (0 == sce->st.st_size || 0 == http_chunk_append_file(srv, con, path)) {
        con->http_status = 200;
        con->file_finished = 1;
    } else {
        con->http_status = 403;
    }
}