From 569e9ab04d516cfbb5f997b9614e20e31e3f8b56 Mon Sep 17 00:00:00 2001 From: Marcin Krol Date: Tue, 7 Apr 2015 18:12:29 +0000 Subject: [PATCH] - from PLD --- TODO | 29 + branch.sh | 11 + env-documentroot.patch | 69 + lighttpd-branch.diff | 1957 ++++++++++++++ lighttpd-branding.patch | 11 + lighttpd-mime.types.sh | 63 + lighttpd-mod_access.conf | 20 + lighttpd-mod_accesslog.conf | 24 + lighttpd-mod_alias.conf | 12 + lighttpd-mod_auth.conf | 43 + lighttpd-mod_cgi.conf | 30 + lighttpd-mod_cgi_php.conf | 8 + lighttpd-mod_cml.conf | 21 + lighttpd-mod_compress.conf | 25 + lighttpd-mod_compress.tmpwatch | 2 + lighttpd-mod_deflate.conf | 7 + lighttpd-mod_deflate.patch | 2170 +++++++++++++++ lighttpd-mod_dirlisting.conf | 9 + lighttpd-mod_evasive-status_code.patch | 97 + lighttpd-mod_evasive.conf | 12 + lighttpd-mod_evhost.conf | 17 + lighttpd-mod_expire.conf | 12 + lighttpd-mod_extforward.conf | 42 + lighttpd-mod_fastcgi.conf | 17 + lighttpd-mod_flv_streaming.conf | 7 + lighttpd-mod_h264_streaming.conf | 11 + lighttpd-mod_h264_streaming.patch | 3440 ++++++++++++++++++++++++ lighttpd-mod_indexfile.conf | 13 + lighttpd-mod_magnet.conf | 10 + lighttpd-mod_mysql_vhost.conf | 7 + lighttpd-mod_proxy.conf | 31 + lighttpd-mod_redirect.conf | 15 + lighttpd-mod_rewrite.conf | 12 + lighttpd-mod_rrdtool.conf | 11 + lighttpd-mod_scgi.conf | 7 + lighttpd-mod_secdownload.conf | 7 + lighttpd-mod_setenv.conf | 19 + lighttpd-mod_simple_vhost.conf | 15 + lighttpd-mod_ssi.conf | 11 + lighttpd-mod_staticfile.conf | 11 + lighttpd-mod_status.conf | 13 + lighttpd-mod_trigger_b4_dl.conf | 24 + lighttpd-mod_userdir.conf | 18 + lighttpd-mod_usertrack.conf | 7 + lighttpd-mod_webdav.conf | 7 + lighttpd-modinit-before-fork.patch | 187 ++ lighttpd-php-external.conf | 48 + lighttpd-php-spawned.conf | 34 + lighttpd-pld.html | 29 + lighttpd-ssl.conf | 52 + lighttpd-use_bin_sh.patch | 20 + lighttpd.conf | 187 ++ lighttpd.init | 226 ++ lighttpd.logrotate | 12 + lighttpd.monitrc | 31 + lighttpd.service | 12 + lighttpd.spec | 1346 +++++++++ lighttpd.sysconfig | 15 + lighttpd.tmpfiles | 1 + lighttpd.upstart | 16 + lighttpd.user | 1 + test-port-setup.patch | 11 + 62 files changed, 10632 insertions(+) create mode 100644 TODO create mode 100644 branch.sh create mode 100644 env-documentroot.patch create mode 100644 lighttpd-branch.diff create mode 100644 lighttpd-branding.patch create mode 100644 lighttpd-mime.types.sh create mode 100644 lighttpd-mod_access.conf create mode 100644 lighttpd-mod_accesslog.conf create mode 100644 lighttpd-mod_alias.conf create mode 100644 lighttpd-mod_auth.conf create mode 100644 lighttpd-mod_cgi.conf create mode 100644 lighttpd-mod_cgi_php.conf create mode 100644 lighttpd-mod_cml.conf create mode 100644 lighttpd-mod_compress.conf create mode 100644 lighttpd-mod_compress.tmpwatch create mode 100644 lighttpd-mod_deflate.conf create mode 100644 lighttpd-mod_deflate.patch create mode 100644 lighttpd-mod_dirlisting.conf create mode 100644 lighttpd-mod_evasive-status_code.patch create mode 100644 lighttpd-mod_evasive.conf create mode 100644 lighttpd-mod_evhost.conf create mode 100644 lighttpd-mod_expire.conf create mode 100644 lighttpd-mod_extforward.conf create mode 100644 lighttpd-mod_fastcgi.conf create mode 100644 lighttpd-mod_flv_streaming.conf create mode 100644 lighttpd-mod_h264_streaming.conf create mode 100644 lighttpd-mod_h264_streaming.patch create mode 100644 lighttpd-mod_indexfile.conf create mode 100644 lighttpd-mod_magnet.conf create mode 100644 lighttpd-mod_mysql_vhost.conf create mode 100644 lighttpd-mod_proxy.conf create mode 100644 lighttpd-mod_redirect.conf create mode 100644 lighttpd-mod_rewrite.conf create mode 100644 lighttpd-mod_rrdtool.conf create mode 100644 lighttpd-mod_scgi.conf create mode 100644 lighttpd-mod_secdownload.conf create mode 100644 lighttpd-mod_setenv.conf create mode 100644 lighttpd-mod_simple_vhost.conf create mode 100644 lighttpd-mod_ssi.conf create mode 100644 lighttpd-mod_staticfile.conf create mode 100644 lighttpd-mod_status.conf create mode 100644 lighttpd-mod_trigger_b4_dl.conf create mode 100644 lighttpd-mod_userdir.conf create mode 100644 lighttpd-mod_usertrack.conf create mode 100644 lighttpd-mod_webdav.conf create mode 100644 lighttpd-modinit-before-fork.patch create mode 100644 lighttpd-php-external.conf create mode 100644 lighttpd-php-spawned.conf create mode 100644 lighttpd-pld.html create mode 100644 lighttpd-ssl.conf create mode 100644 lighttpd-use_bin_sh.patch create mode 100644 lighttpd.conf create mode 100755 lighttpd.init create mode 100644 lighttpd.logrotate create mode 100644 lighttpd.monitrc create mode 100644 lighttpd.service create mode 100644 lighttpd.spec create mode 100644 lighttpd.sysconfig create mode 100644 lighttpd.tmpfiles create mode 100644 lighttpd.upstart create mode 100644 lighttpd.user create mode 100644 test-port-setup.patch diff --git a/TODO b/TODO new file mode 100644 index 0000000..ec6495d --- /dev/null +++ b/TODO @@ -0,0 +1,29 @@ +# TODO: +- test mysql (failed at this time) +- mysql issue: http://www.freebsd.org/cgi/query-pr.cgi?pr=76866 +- fam over gamin is possible, just configure doesn't check other than gamin +- feature stat-cache-fam (doesn't work) +- lighttpd writes early startup messages to stderr, and if started from + rc-scripts the stderr is closed which causes lighttpd to abort(): + 2006-07-20 21:05:52: (server.c.1233) WARNING: unknown config-key: url.rewrite-final (ignored) + +# NOTES: +- disable largefile, if you have 2.4 kernel to get sendfile() support, and don't need > 2GB file requests, + see http://article.gmane.org/gmane.comp.web.lighttpd:722 + + +01:08:45 stbuehler> glen: http://redmine.lighttpd.net/projects/lighttpd/repository/revisions/2505 +01:09:57 glen> stbuehler: ah sorry, i didn't notice as you didn't reply (hilite) me here.. +01:13:52 glen> stbuehler: btw, what you think of this patch: + http://cvs.pld-linux.org/cgi-bin/cvsweb.cgi/packages/lighttpd/lighttpd-mod_evasive-status_code.patch +01:17:52 stbuehler> the buffer_prepare_copy isn't really needed imho (and 255 seems to much for a long anyway) +01:18:11 stbuehler> and wasn't there a srv->tmp_buf for such things? +01:18:58 glen> ok i see it changes default 403 -> 503, that's probably bad too? +01:19:26 stbuehler> apart from that it looks good to me, although it changes the default status code to 503; and perhaps retry-after should + only be returned if it is not zero (i don't know what the rfc says) +01:19:51 >> OpenURL 9 +01:19:51 Tobsn> http://government.zdnet.com/?p=4792 +01:20:25 stbuehler> i think 503 is a sane default and better than 403, so it should be ok to change the default +01:21:54 stbuehler> gn8 + + diff --git a/branch.sh b/branch.sh new file mode 100644 index 0000000..4a0e8f8 --- /dev/null +++ b/branch.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -e +svn=svn://svn.lighttpd.net/lighttpd +p=lighttpd +v=1.5 + +svn co $svn/trunk $p-$v +r=$(svnversion $p-$v) +t=$p-r$r.tar.bz2 +tar -cjf $t --exclude-vcs $p-$v +../dropin $t & diff --git a/env-documentroot.patch b/env-documentroot.patch new file mode 100644 index 0000000..eea738e --- /dev/null +++ b/env-documentroot.patch @@ -0,0 +1,69 @@ +revert: + +- * [*cgi] Use physical base dir (alias, userdir) as DOCUMENT_ROOT in cgi environments (fixes #2216) + +Index: src/mod_fastcgi.c +=================================================================== +--- src/mod_fastcgi.c (revision 2794) ++++ src/mod_fastcgi.c (revision 2793) +@@ -1968,7 +1968,7 @@ + if (!buffer_is_empty(host->docroot)) { + buffer_copy_string_buffer(p->path, host->docroot); + } else { +- buffer_copy_string_buffer(p->path, con->physical.basedir); ++ buffer_copy_string_buffer(p->path, con->physical.doc_root); + } + buffer_append_string_buffer(p->path, con->request.pathinfo); + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_TRANSLATED"), CONST_BUF_LEN(p->path)),con) +@@ -2008,7 +2008,7 @@ + } + + FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path)),con) +- FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir)),con) ++ FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)),con) + } + + if (host->strip_request_uri->used > 1) { +@@ -3273,7 +3273,6 @@ + */ + + buffer_copy_string_buffer(con->physical.doc_root, host->docroot); +- buffer_copy_string_buffer(con->physical.basedir, host->docroot); + + buffer_copy_string_buffer(con->physical.path, host->docroot); + buffer_append_string_buffer(con->physical.path, con->uri.path); +Index: src/mod_scgi.c +=================================================================== +--- src/mod_scgi.c (revision 2794) ++++ src/mod_scgi.c (revision 2793) +@@ -1558,7 +1558,7 @@ + if (!buffer_is_empty(host->docroot)) { + buffer_copy_string_buffer(p->path, host->docroot); + } else { +- buffer_copy_string_buffer(p->path, con->physical.basedir); ++ buffer_copy_string_buffer(p->path, con->physical.doc_root); + } + buffer_append_string_buffer(p->path, con->request.pathinfo); + scgi_env_add(p->scgi_env, CONST_STR_LEN("PATH_TRANSLATED"), CONST_BUF_LEN(p->path)); +@@ -1589,7 +1589,7 @@ + buffer_copy_string_buffer(p->path, con->physical.path); + + scgi_env_add(p->scgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path)); +- scgi_env_add(p->scgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir)); ++ scgi_env_add(p->scgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)); + } + scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); + if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) { +Index: src/mod_cgi.c +=================================================================== +--- src/mod_cgi.c (revision 2794) ++++ src/mod_cgi.c (revision 2793) +@@ -928,7 +928,7 @@ + cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); + cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); + cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); +- cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir)); ++ cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)); + + /* for valgrind */ + if (NULL != (s = getenv("LD_PRELOAD"))) { diff --git a/lighttpd-branch.diff b/lighttpd-branch.diff new file mode 100644 index 0000000..76f06ce --- /dev/null +++ b/lighttpd-branch.diff @@ -0,0 +1,1957 @@ +# Revision 2815 +Index: src/http_auth_digest.c +=================================================================== +--- src/http_auth_digest.c (.../tags/lighttpd-1.4.29) ++++ src/http_auth_digest.c (.../branches/lighttpd-1.4.x) +@@ -1,26 +0,0 @@ +-#include "buffer.h" +- +-#include "http_auth_digest.h" +- +-#include +- +-#ifndef USE_OPENSSL +-# include "md5.h" +- +-typedef li_MD5_CTX MD5_CTX; +-#define MD5_Init li_MD5_Init +-#define MD5_Update li_MD5_Update +-#define MD5_Final li_MD5_Final +- +-#endif +- +-void CvtHex(IN HASH Bin, OUT HASHHEX Hex) { +- unsigned short i; +- +- for (i = 0; i < HASHLEN; i++) { +- Hex[i*2] = int2hex((Bin[i] >> 4) & 0xf); +- Hex[i*2+1] = int2hex(Bin[i] & 0xf); +- } +- Hex[HASHHEXLEN] = '\0'; +-} +- +Index: src/http_auth_digest.h +=================================================================== +--- src/http_auth_digest.h (.../tags/lighttpd-1.4.29) ++++ src/http_auth_digest.h (.../branches/lighttpd-1.4.x) +@@ -1,24 +0,0 @@ +-#ifndef _DIGCALC_H_ +-#define _DIGCALC_H_ +- +-#ifdef HAVE_CONFIG_H +-# include "config.h" +-#endif +- +-#define HASHLEN 16 +-typedef unsigned char HASH[HASHLEN]; +-#define HASHHEXLEN 32 +-typedef char HASHHEX[HASHHEXLEN+1]; +-#ifdef USE_OPENSSL +-#define IN const +-#else +-#define IN +-#endif +-#define OUT +- +-void CvtHex( +- IN HASH Bin, +- OUT HASHHEX Hex +- ); +- +-#endif +Index: src/network_write.c +=================================================================== +--- src/network_write.c (.../tags/lighttpd-1.4.29) ++++ src/network_write.c (.../branches/lighttpd-1.4.x) +@@ -24,17 +24,16 @@ + # include + #endif + +-int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqueue *cq) { ++int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { + chunk *c; +- size_t chunks_written = 0; + +- for(c = cq->first; c; c = c->next) { ++ for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { + int chunk_finished = 0; + + switch(c->type) { + case MEM_CHUNK: { + char * offset; +- size_t toSend; ++ off_t toSend; + ssize_t r; + + if (c->mem->used == 0) { +@@ -44,6 +43,8 @@ + + offset = c->mem->ptr + c->offset; + toSend = c->mem->used - 1 - c->offset; ++ if (toSend > max_bytes) toSend = max_bytes; ++ + #ifdef __WIN32 + if ((r = send(fd, offset, toSend, 0)) < 0) { + /* no error handling for windows... */ +@@ -72,6 +73,7 @@ + + c->offset += r; + cq->bytes_out += r; ++ max_bytes -= r; + + if (c->offset == (off_t)c->mem->used - 1) { + chunk_finished = 1; +@@ -85,7 +87,7 @@ + #endif + ssize_t r; + off_t offset; +- size_t toSend; ++ off_t toSend; + stat_cache_entry *sce = NULL; + int ifd; + +@@ -98,6 +100,8 @@ + offset = c->file.start + c->offset; + toSend = c->file.length - c->offset; + ++ if (toSend > max_bytes) toSend = max_bytes; ++ + if (offset > sce->st.st_size) { + log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); + +@@ -181,6 +185,7 @@ + + c->offset += r; + cq->bytes_out += r; ++ max_bytes -= r; + + if (c->offset == c->file.length) { + chunk_finished = 1; +@@ -200,11 +205,9 @@ + + break; + } +- +- chunks_written++; + } + +- return chunks_written; ++ return 0; + } + + #if 0 +Index: src/mod_secure_download.c +=================================================================== +--- src/mod_secure_download.c (.../tags/lighttpd-1.4.29) ++++ src/mod_secure_download.c (.../branches/lighttpd-1.4.x) +@@ -8,18 +8,8 @@ + #include + #include + +-#ifdef USE_OPENSSL +-# include +-#else +-# include "md5.h" ++#include "md5.h" + +-typedef li_MD5_CTX MD5_CTX; +-#define MD5_Init li_MD5_Init +-#define MD5_Update li_MD5_Update +-#define MD5_Final li_MD5_Final +- +-#endif +- + #define HASHLEN 16 + typedef unsigned char HASH[HASHLEN]; + #define HASHHEXLEN 32 +@@ -200,7 +190,7 @@ + + URIHANDLER_FUNC(mod_secdownload_uri_handler) { + plugin_data *p = p_d; +- MD5_CTX Md5Ctx; ++ li_MD5_CTX Md5Ctx; + HASH HA1; + const char *rel_uri, *ts_str, *md5_str; + time_t ts = 0; +@@ -266,9 +256,9 @@ + buffer_append_string(p->md5, rel_uri); + buffer_append_string_len(p->md5, ts_str, 8); + +- MD5_Init(&Md5Ctx); +- MD5_Update(&Md5Ctx, (unsigned char *)p->md5->ptr, p->md5->used - 1); +- MD5_Final(HA1, &Md5Ctx); ++ li_MD5_Init(&Md5Ctx); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)p->md5->ptr, p->md5->used - 1); ++ li_MD5_Final(HA1, &Md5Ctx); + + buffer_copy_string_hex(p->md5, (char *)HA1, 16); + +Index: src/base.h +=================================================================== +--- src/base.h (.../tags/lighttpd-1.4.29) ++++ src/base.h (.../branches/lighttpd-1.4.x) +@@ -277,6 +277,7 @@ + buffer *ssl_cipher_list; + buffer *ssl_dh_file; + buffer *ssl_ec_curve; ++ unsigned short ssl_honor_cipher_order; /* determine SSL cipher in server-preferred order, not client-order */ + unsigned short ssl_use_sslv2; + unsigned short ssl_use_sslv3; + unsigned short ssl_verifyclient; +@@ -284,6 +285,7 @@ + unsigned short ssl_verifyclient_depth; + buffer *ssl_verifyclient_username; + unsigned short ssl_verifyclient_export_cert; ++ unsigned short ssl_disable_client_renegotiation; + + unsigned short use_ipv6, set_v6only; /* set_v6only is only a temporary option */ + unsigned short defer_accept; +@@ -437,6 +439,7 @@ + # ifndef OPENSSL_NO_TLSEXT + buffer *tlsext_server_name; + # endif ++ unsigned int renegotiations; /* count of SSL_CB_HANDSHAKE_START */ + #endif + /* etag handling */ + etag_flags_t etag_flags; +@@ -647,11 +650,9 @@ + + fdevent_handler_t event_handler; + +- int (* network_backend_write)(struct server *srv, connection *con, int fd, chunkqueue *cq); +- int (* network_backend_read)(struct server *srv, connection *con, int fd, chunkqueue *cq); ++ int (* network_backend_write)(struct server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); + #ifdef USE_OPENSSL +- int (* network_ssl_backend_write)(struct server *srv, connection *con, SSL *ssl, chunkqueue *cq); +- int (* network_ssl_backend_read)(struct server *srv, connection *con, SSL *ssl, chunkqueue *cq); ++ int (* network_ssl_backend_write)(struct server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes); + #endif + + uid_t uid; +Index: src/connections.c +=================================================================== +--- src/connections.c (.../tags/lighttpd-1.4.29) ++++ src/connections.c (.../branches/lighttpd-1.4.x) +@@ -223,6 +223,12 @@ + + len = SSL_read(con->ssl, b->ptr + read_offset, toread); + ++ if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { ++ connection_set_state(srv, con, CON_STATE_ERROR); ++ log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client"); ++ return -1; ++ } ++ + if (len > 0) { + if (b->used > 0) b->used--; + b->used += len; +@@ -445,6 +451,7 @@ + default: + switch(con->http_status) { + case 400: /* bad request */ ++ case 401: /* authorization required */ + case 414: /* overload request header */ + case 505: /* unknown protocol */ + case 207: /* this was webdav */ +@@ -617,8 +624,9 @@ + } + + static int connection_handle_write(server *srv, connection *con) { +- switch(network_write_chunkqueue(srv, con, con->write_queue)) { ++ switch(network_write_chunkqueue(srv, con, con->write_queue, MAX_WRITE_LIMIT)) { + case 0: ++ con->write_request_ts = srv->cur_ts; + if (con->file_finished) { + connection_set_state(srv, con, CON_STATE_RESPONSE_END); + joblist_append(srv, con); +@@ -635,6 +643,7 @@ + joblist_append(srv, con); + break; + case 1: ++ con->write_request_ts = srv->cur_ts; + con->is_writable = 0; + + /* not finished yet -> WRITE */ +@@ -1251,8 +1260,6 @@ + log_error_write(srv, __FILE__, __LINE__, "ds", + con->fd, + "handle write failed."); +- } else if (con->state == CON_STATE_WRITE) { +- con->write_request_ts = srv->cur_ts; + } + } + +@@ -1352,6 +1359,7 @@ + return NULL; + } + ++ con->renegotiations = 0; + #ifndef OPENSSL_NO_TLSEXT + SSL_set_app_data(con->ssl, con); + #endif +@@ -1667,8 +1675,6 @@ + con->fd, + "handle write failed."); + connection_set_state(srv, con, CON_STATE_ERROR); +- } else if (con->state == CON_STATE_WRITE) { +- con->write_request_ts = srv->cur_ts; + } + } + +Index: src/mod_staticfile.c +=================================================================== +--- src/mod_staticfile.c (.../tags/lighttpd-1.4.29) ++++ src/mod_staticfile.c (.../branches/lighttpd-1.4.x) +@@ -26,6 +26,7 @@ + typedef struct { + array *exclude_ext; + unsigned short etags_used; ++ unsigned short disable_pathinfo; + } plugin_config; + + typedef struct { +@@ -84,6 +85,7 @@ + config_values_t cv[] = { + { "static-file.exclude-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "static-file.etags", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ ++ { "static-file.disable-pathinfo", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + +@@ -97,9 +99,11 @@ + s = calloc(1, sizeof(plugin_config)); + s->exclude_ext = array_init(); + s->etags_used = 1; ++ s->disable_pathinfo = 0; + + cv[0].destination = s->exclude_ext; + cv[1].destination = &(s->etags_used); ++ cv[2].destination = &(s->disable_pathinfo); + + p->config_storage[i] = s; + +@@ -119,6 +123,7 @@ + + PATCH(exclude_ext); + PATCH(etags_used); ++ PATCH(disable_pathinfo); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { +@@ -136,7 +141,9 @@ + PATCH(exclude_ext); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.etags"))) { + PATCH(etags_used); +- } ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.disable-pathinfo"))) { ++ PATCH(disable_pathinfo); ++ } + } + } + +@@ -350,7 +357,6 @@ + URIHANDLER_FUNC(mod_staticfile_subrequest) { + plugin_data *p = p_d; + size_t k; +- int s_len; + stat_cache_entry *sce = NULL; + buffer *mtime = NULL; + data_string *ds; +@@ -376,7 +382,12 @@ + + mod_staticfile_patch_connection(srv, con, p); + +- s_len = con->uri.path->used - 1; ++ if (p->conf.disable_pathinfo && 0 != con->request.pathinfo->used) { ++ if (con->conf.log_request_handling) { ++ log_error_write(srv, __FILE__, __LINE__, "s", "-- NOT handling file as static file, pathinfo forbidden"); ++ } ++ return HANDLER_GO_ON; ++ } + + /* ignore certain extensions */ + for (k = 0; k < p->conf.exclude_ext->used; k++) { +Index: src/network.c +=================================================================== +--- src/network.c (.../tags/lighttpd-1.4.29) ++++ src/network.c (.../branches/lighttpd-1.4.x) +@@ -27,6 +27,19 @@ + # include + #endif + ++#ifdef USE_OPENSSL ++static void ssl_info_callback(const SSL *ssl, int where, int ret) { ++ UNUSED(ret); ++ ++ if (0 != (where & SSL_CB_HANDSHAKE_START)) { ++ connection *con = SSL_get_app_data(ssl); ++ ++con->renegotiations; ++ } else if (0 != (where & SSL_CB_HANDSHAKE_DONE)) { ++ ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS; ++ } ++} ++#endif ++ + static handler_t network_server_handle_fdevent(server *srv, void *context, int revents) { + server_socket *srv_socket = (server_socket *)context; + connection *con; +@@ -480,9 +493,11 @@ + network_backend_t backend; + + #if OPENSSL_VERSION_NUMBER >= 0x0090800fL ++#ifndef OPENSSL_NO_ECDH + EC_KEY *ecdh; + int nid; + #endif ++#endif + + #ifdef USE_OPENSSL + DH *dh; +@@ -553,6 +568,11 @@ + /* load SSL certificates */ + for (i = 0; i < srv->config_context->used; i++) { + specific_config *s = srv->config_storage[i]; ++#ifndef SSL_OP_NO_COMPRESSION ++# define SSL_OP_NO_COMPRESSION 0 ++#endif ++ long ssloptions = ++ SSL_OP_ALL | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_NO_COMPRESSION; + + if (buffer_is_empty(s->ssl_pemfile)) continue; + +@@ -586,6 +606,9 @@ + return -1; + } + ++ SSL_CTX_set_options(s->ssl_ctx, ssloptions); ++ SSL_CTX_set_info_callback(s->ssl_ctx, ssl_info_callback); ++ + if (!s->ssl_use_sslv2) { + /* disable SSLv2 */ + if (!(SSL_OP_NO_SSLv2 & SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv2))) { +@@ -611,6 +634,10 @@ + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } ++ ++ if (s->ssl_honor_cipher_order) { ++ SSL_CTX_set_options(s->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); ++ } + } + + /* Support for Diffie-Hellman key exchange */ +@@ -847,7 +874,7 @@ + return 0; + } + +-int network_write_chunkqueue(server *srv, connection *con, chunkqueue *cq) { ++int network_write_chunkqueue(server *srv, connection *con, chunkqueue *cq, off_t max_bytes) { + int ret = -1; + off_t written = 0; + #ifdef TCP_CORK +@@ -855,16 +882,34 @@ + #endif + server_socket *srv_socket = con->srv_socket; + +- if (con->conf.global_kbytes_per_second && +- *(con->conf.global_bytes_per_second_cnt_ptr) > con->conf.global_kbytes_per_second * 1024) { +- /* we reached the global traffic limit */ ++ if (con->conf.global_kbytes_per_second) { ++ off_t limit = con->conf.global_kbytes_per_second * 1024 - *(con->conf.global_bytes_per_second_cnt_ptr); ++ if (limit <= 0) { ++ /* we reached the global traffic limit */ + +- con->traffic_limit_reached = 1; +- joblist_append(srv, con); ++ con->traffic_limit_reached = 1; ++ joblist_append(srv, con); + +- return 1; ++ return 1; ++ } else { ++ if (max_bytes > limit) max_bytes = limit; ++ } + } + ++ if (con->conf.kbytes_per_second) { ++ off_t limit = con->conf.kbytes_per_second * 1024 - con->bytes_written_cur_second; ++ if (limit <= 0) { ++ /* we reached the traffic limit */ ++ ++ con->traffic_limit_reached = 1; ++ joblist_append(srv, con); ++ ++ return 1; ++ } else { ++ if (max_bytes > limit) max_bytes = limit; ++ } ++ } ++ + written = cq->bytes_out; + + #ifdef TCP_CORK +@@ -879,10 +924,10 @@ + + if (srv_socket->is_ssl) { + #ifdef USE_OPENSSL +- ret = srv->network_ssl_backend_write(srv, con, con->ssl, cq); ++ ret = srv->network_ssl_backend_write(srv, con, con->ssl, cq, max_bytes); + #endif + } else { +- ret = srv->network_backend_write(srv, con, con->fd, cq); ++ ret = srv->network_backend_write(srv, con, con->fd, cq, max_bytes); + } + + if (ret >= 0) { +@@ -903,12 +948,5 @@ + + *(con->conf.global_bytes_per_second_cnt_ptr) += written; + +- if (con->conf.kbytes_per_second && +- (con->bytes_written_cur_second > con->conf.kbytes_per_second * 1024)) { +- /* we reached the traffic limit */ +- +- con->traffic_limit_reached = 1; +- joblist_append(srv, con); +- } + return ret; + } +Index: src/network.h +=================================================================== +--- src/network.h (.../tags/lighttpd-1.4.29) ++++ src/network.h (.../branches/lighttpd-1.4.x) +@@ -3,7 +3,7 @@ + + #include "server.h" + +-int network_write_chunkqueue(server *srv, connection *con, chunkqueue *c); ++int network_write_chunkqueue(server *srv, connection *con, chunkqueue *c, off_t max_bytes); + + int network_init(server *srv); + int network_close(server *srv); +Index: src/configfile.c +=================================================================== +--- src/configfile.c (.../tags/lighttpd-1.4.29) ++++ src/configfile.c (.../branches/lighttpd-1.4.x) +@@ -105,6 +105,8 @@ + { "ssl.use-sslv3", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 62 */ + { "ssl.dh-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 63 */ + { "ssl.ec-curve", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 64 */ ++ { "ssl.disable-client-renegotiation", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER },/* 65 */ ++ { "ssl.honor-cipher-order", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 66 */ + + { "server.host", "use server.bind instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, + { "server.docroot", "use server.document-root instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, +@@ -176,6 +178,7 @@ + s->max_write_idle = 360; + s->use_xattr = 0; + s->is_ssl = 0; ++ s->ssl_honor_cipher_order = 1; + s->ssl_use_sslv2 = 0; + s->ssl_use_sslv3 = 1; + s->use_ipv6 = 0; +@@ -199,6 +202,7 @@ + s->ssl_verifyclient_username = buffer_init(); + s->ssl_verifyclient_depth = 9; + s->ssl_verifyclient_export_cert = 0; ++ s->ssl_disable_client_renegotiation = 1; + + cv[2].destination = s->errorfile_prefix; + +@@ -245,6 +249,8 @@ + cv[62].destination = &(s->ssl_use_sslv3); + cv[63].destination = s->ssl_dh_file; + cv[64].destination = s->ssl_ec_curve; ++ cv[66].destination = &(s->ssl_honor_cipher_order); ++ + cv[49].destination = &(s->etag_use_inode); + cv[50].destination = &(s->etag_use_mtime); + cv[51].destination = &(s->etag_use_size); +@@ -255,6 +261,7 @@ + cv[58].destination = &(s->ssl_verifyclient_depth); + cv[59].destination = s->ssl_verifyclient_username; + cv[60].destination = &(s->ssl_verifyclient_export_cert); ++ cv[65].destination = &(s->ssl_disable_client_renegotiation); + + srv->config_storage[i] = s; + +@@ -335,6 +342,7 @@ + PATCH(ssl_cipher_list); + PATCH(ssl_dh_file); + PATCH(ssl_ec_curve); ++ PATCH(ssl_honor_cipher_order); + PATCH(ssl_use_sslv2); + PATCH(ssl_use_sslv3); + PATCH(etag_use_inode); +@@ -346,6 +354,7 @@ + PATCH(ssl_verifyclient_depth); + PATCH(ssl_verifyclient_username); + PATCH(ssl_verifyclient_export_cert); ++ PATCH(ssl_disable_client_renegotiation); + + return 0; + } +@@ -400,6 +409,8 @@ + #endif + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.ca-file"))) { + PATCH(ssl_ca_file); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.honor-cipher-order"))) { ++ PATCH(ssl_honor_cipher_order); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.use-sslv2"))) { + PATCH(ssl_use_sslv2); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.use-sslv3"))) { +@@ -454,6 +465,8 @@ + PATCH(ssl_verifyclient_username); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.exportcert"))) { + PATCH(ssl_verifyclient_export_cert); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.disable-client-renegotiation"))) { ++ PATCH(ssl_disable_client_renegotiation); + } + } + } +Index: src/mod_scgi.c +=================================================================== +--- src/mod_scgi.c (.../tags/lighttpd-1.4.29) ++++ src/mod_scgi.c (.../branches/lighttpd-1.4.x) +@@ -2296,7 +2296,7 @@ + + /* fall through */ + case FCGI_STATE_WRITE: +- ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb); ++ ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT); + + chunkqueue_remove_finished_chunks(hctx->wb); + +Index: src/request.c +=================================================================== +--- src/request.c (.../tags/lighttpd-1.4.29) ++++ src/request.c (.../branches/lighttpd-1.4.x) +@@ -49,7 +49,7 @@ + if (++colon_cnt > 7) { + return -1; + } +- } else if (!light_isxdigit(*c)) { ++ } else if (!light_isxdigit(*c) && '.' != *c) { + return -1; + } + } +Index: src/network_backends.h +=================================================================== +--- src/network_backends.h (.../tags/lighttpd-1.4.29) ++++ src/network_backends.h (.../branches/lighttpd-1.4.x) +@@ -47,18 +47,18 @@ + #include "base.h" + + /* return values: +- * >= 0 : chunks completed ++ * >= 0 : no error + * -1 : error (on our side) + * -2 : remote close + */ + +-int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqueue *cq); +-int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq); +-int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, chunkqueue *cq); +-int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int fd, chunkqueue *cq); +-int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fd, chunkqueue *cq); ++int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); ++int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); ++int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); ++int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); ++int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); + #ifdef USE_OPENSSL +-int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq); ++int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes); + #endif + + #endif +Index: src/SConscript +=================================================================== +--- src/SConscript (.../tags/lighttpd-1.4.29) ++++ src/SConscript (.../branches/lighttpd-1.4.x) +@@ -12,7 +12,8 @@ + data_integer.c md5.c data_fastcgi.c \ + fdevent_select.c fdevent_libev.c \ + fdevent_poll.c fdevent_linux_sysepoll.c \ +- fdevent_solaris_devpoll.c fdevent_freebsd_kqueue.c \ ++ fdevent_solaris_devpoll.c fdevent_solaris_port.c \ ++ fdevent_freebsd_kqueue.c \ + data_config.c bitset.c \ + inet_ntop_cache.c crc32.c \ + connections-glue.c \ +@@ -62,7 +63,7 @@ + 'mod_redirect' : { 'src' : [ 'mod_redirect.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_rewrite' : { 'src' : [ 'mod_rewrite.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_auth' : { +- 'src' : [ 'mod_auth.c', 'http_auth_digest.c', 'http_auth.c' ], ++ 'src' : [ 'mod_auth.c', 'http_auth.c' ], + 'lib' : [ env['LIBCRYPT'], env['LIBLDAP'], env['LIBLBER'] ] }, + 'mod_webdav' : { 'src' : [ 'mod_webdav.c' ], 'lib' : [ env['LIBXML2'], env['LIBSQLITE3'], env['LIBUUID'] ] }, + 'mod_mysql_vhost' : { 'src' : [ 'mod_mysql_vhost.c' ], 'lib' : [ env['LIBMYSQL'] ] }, +Index: src/mod_cml_funcs.c +=================================================================== +--- src/mod_cml_funcs.c (.../tags/lighttpd-1.4.29) ++++ src/mod_cml_funcs.c (.../branches/lighttpd-1.4.x) +@@ -17,18 +17,8 @@ + #include + #include + +-#ifdef USE_OPENSSL +-# include +-#else +-# include "md5.h" ++#include "md5.h" + +-typedef li_MD5_CTX MD5_CTX; +-#define MD5_Init li_MD5_Init +-#define MD5_Update li_MD5_Update +-#define MD5_Final li_MD5_Final +- +-#endif +- + #define HASHLEN 16 + typedef unsigned char HASH[HASHLEN]; + #define HASHHEXLEN 32 +@@ -43,7 +33,7 @@ + #ifdef HAVE_LUA_H + + int f_crypto_md5(lua_State *L) { +- MD5_CTX Md5Ctx; ++ li_MD5_CTX Md5Ctx; + HASH HA1; + buffer b; + char hex[33]; +@@ -63,9 +53,9 @@ + lua_error(L); + } + +- MD5_Init(&Md5Ctx); +- MD5_Update(&Md5Ctx, (unsigned char *)lua_tostring(L, 1), lua_strlen(L, 1)); +- MD5_Final(HA1, &Md5Ctx); ++ li_MD5_Init(&Md5Ctx); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)lua_tostring(L, 1), lua_strlen(L, 1)); ++ li_MD5_Final(HA1, &Md5Ctx); + + buffer_copy_string_hex(&b, (char *)HA1, 16); + +Index: src/mod_userdir.c +=================================================================== +--- src/mod_userdir.c (.../tags/lighttpd-1.4.29) ++++ src/mod_userdir.c (.../branches/lighttpd-1.4.x) +@@ -166,7 +166,6 @@ + + URIHANDLER_FUNC(mod_userdir_docroot_handler) { + plugin_data *p = p_d; +- int uri_len; + size_t k; + char *rel_url; + #ifdef HAVE_PWD_H +@@ -182,8 +181,6 @@ + */ + if (p->conf.path->used == 0) return HANDLER_GO_ON; + +- uri_len = con->uri.path->used - 1; +- + /* /~user/foo.html -> /home/user/public_html/foo.html */ + + if (con->uri.path->ptr[0] != '/' || +Index: src/mod_proxy.c +=================================================================== +--- src/mod_proxy.c (.../tags/lighttpd-1.4.29) ++++ src/mod_proxy.c (.../branches/lighttpd-1.4.x) +@@ -825,7 +825,7 @@ + + /* fall through */ + case PROXY_STATE_WRITE:; +- ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb); ++ ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT); + + chunkqueue_remove_finished_chunks(hctx->wb); + +Index: src/Makefile.am +=================================================================== +--- src/Makefile.am (.../tags/lighttpd-1.4.29) ++++ src/Makefile.am (.../branches/lighttpd-1.4.x) +@@ -241,7 +241,7 @@ + mod_compress_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd) + + lib_LTLIBRARIES += mod_auth.la +-mod_auth_la_SOURCES = mod_auth.c http_auth_digest.c http_auth.c ++mod_auth_la_SOURCES = mod_auth.c http_auth.c + mod_auth_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined + mod_auth_la_LIBADD = $(CRYPT_LIB) $(LDAP_LIB) $(LBER_LIB) $(common_libadd) + +@@ -268,7 +268,7 @@ + + hdr = server.h buffer.h network.h log.h keyvalue.h \ + response.h request.h fastcgi.h chunk.h \ +- settings.h http_chunk.h http_auth_digest.h \ ++ settings.h http_chunk.h \ + md5.h http_auth.h stream.h \ + fdevent.h connections.h base.h stat_cache.h \ + plugin.h mod_auth.h \ +Index: src/network_writev.c +=================================================================== +--- src/network_writev.c (.../tags/lighttpd-1.4.29) ++++ src/network_writev.c (.../branches/lighttpd-1.4.x) +@@ -30,17 +30,16 @@ + #define LOCAL_BUFFERING 1 + #endif + +-int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq) { ++int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { + chunk *c; +- size_t chunks_written = 0; + +- for(c = cq->first; c; c = c->next) { ++ for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { + int chunk_finished = 0; + + switch(c->type) { + case MEM_CHUNK: { + char * offset; +- size_t toSend; ++ off_t toSend; + ssize_t r; + + size_t num_chunks, i; +@@ -65,12 +64,10 @@ + #error "sysconf() doesnt return _SC_IOV_MAX ..., check the output of 'man writev' for the EINVAL error and send the output to jan@kneschke.de" + #endif + +- /* we can't send more then SSIZE_MAX bytes in one chunk */ +- + /* build writev list + * + * 1. limit: num_chunks < max_chunks +- * 2. limit: num_bytes < SSIZE_MAX ++ * 2. limit: num_bytes < max_bytes + */ + for (num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < max_chunks; num_chunks++, tc = tc->next); + +@@ -87,9 +84,9 @@ + chunks[i].iov_base = offset; + + /* protect the return value of writev() */ +- if (toSend > SSIZE_MAX || +- num_bytes + toSend > SSIZE_MAX) { +- chunks[i].iov_len = SSIZE_MAX - num_bytes; ++ if (toSend > max_bytes || ++ (off_t) num_bytes + toSend > max_bytes) { ++ chunks[i].iov_len = max_bytes - num_bytes; + + num_chunks = i + 1; + break; +@@ -121,6 +118,7 @@ + } + + cq->bytes_out += r; ++ max_bytes -= r; + + /* check which chunks have been written */ + +@@ -132,11 +130,10 @@ + + if (chunk_finished) { + /* skip the chunks from further touches */ +- chunks_written++; + c = c->next; + } else { + /* chunks_written + c = c->next is done in the for()*/ +- chunk_finished++; ++ chunk_finished = 1; + } + } else { + /* partially written */ +@@ -284,6 +281,8 @@ + assert(toSend < 0); + } + ++ if (toSend > max_bytes) toSend = max_bytes; ++ + #ifdef LOCAL_BUFFERING + start = c->mem->ptr; + #else +@@ -309,6 +308,7 @@ + + c->offset += r; + cq->bytes_out += r; ++ max_bytes -= r; + + if (c->offset == c->file.length) { + chunk_finished = 1; +@@ -334,11 +334,9 @@ + + break; + } +- +- chunks_written++; + } + +- return chunks_written; ++ return 0; + } + + #endif +Index: src/network_freebsd_sendfile.c +=================================================================== +--- src/network_freebsd_sendfile.c (.../tags/lighttpd-1.4.29) ++++ src/network_freebsd_sendfile.c (.../branches/lighttpd-1.4.x) +@@ -31,17 +31,16 @@ + # endif + #endif + +-int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int fd, chunkqueue *cq) { ++int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { + chunk *c; +- size_t chunks_written = 0; + +- for(c = cq->first; c; c = c->next, chunks_written++) { ++ for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { + int chunk_finished = 0; + + switch(c->type) { + case MEM_CHUNK: { + char * offset; +- size_t toSend; ++ off_t toSend; + ssize_t r; + + size_t num_chunks, i; +@@ -49,12 +48,10 @@ + chunk *tc; + size_t num_bytes = 0; + +- /* we can't send more then SSIZE_MAX bytes in one chunk */ +- + /* build writev list + * + * 1. limit: num_chunks < UIO_MAXIOV +- * 2. limit: num_bytes < SSIZE_MAX ++ * 2. limit: num_bytes < max_bytes + */ + for(num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; num_chunks++, tc = tc->next); + +@@ -69,9 +66,9 @@ + chunks[i].iov_base = offset; + + /* protect the return value of writev() */ +- if (toSend > SSIZE_MAX || +- num_bytes + toSend > SSIZE_MAX) { +- chunks[i].iov_len = SSIZE_MAX - num_bytes; ++ if (toSend > max_bytes || ++ (off_t) num_bytes + toSend > max_bytes) { ++ chunks[i].iov_len = max_bytes - num_bytes; + + num_chunks = i + 1; + break; +@@ -105,6 +102,7 @@ + + /* check which chunks have been written */ + cq->bytes_out += r; ++ max_bytes -= r; + + for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) { + if (r >= (ssize_t)chunks[i].iov_len) { +@@ -114,11 +112,10 @@ + + if (chunk_finished) { + /* skip the chunks from further touches */ +- chunks_written++; + c = c->next; + } else { + /* chunks_written + c = c->next is done in the for()*/ +- chunk_finished++; ++ chunk_finished = 1; + } + } else { + /* partially written */ +@@ -134,7 +131,7 @@ + } + case FILE_CHUNK: { + off_t offset, r; +- size_t toSend; ++ off_t toSend; + stat_cache_entry *sce = NULL; + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { +@@ -144,9 +141,8 @@ + } + + offset = c->file.start + c->offset; +- /* limit the toSend to 2^31-1 bytes in a chunk */ +- toSend = c->file.length - c->offset > ((1 << 30) - 1) ? +- ((1 << 30) - 1) : c->file.length - c->offset; ++ toSend = c->file.length - c->offset; ++ if (toSend > max_bytes) toSend = max_bytes; + + if (-1 == c->file.fd) { + if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { +@@ -197,6 +193,7 @@ + + c->offset += r; + cq->bytes_out += r; ++ max_bytes -= r; + + if (c->offset == c->file.length) { + chunk_finished = 1; +@@ -218,7 +215,7 @@ + } + } + +- return chunks_written; ++ return 0; + } + + #endif +Index: src/network_openssl.c +=================================================================== +--- src/network_openssl.c (.../tags/lighttpd-1.4.29) ++++ src/network_openssl.c (.../branches/lighttpd-1.4.x) +@@ -27,10 +27,9 @@ + # include + # include + +-int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq) { ++int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes) { + int ssl_r; + chunk *c; +- size_t chunks_written = 0; + + /* this is a 64k sendbuffer + * +@@ -59,13 +58,13 @@ + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + } + +- for(c = cq->first; c; c = c->next) { ++ for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { + int chunk_finished = 0; + + switch(c->type) { + case MEM_CHUNK: { + char * offset; +- size_t toSend; ++ off_t toSend; + ssize_t r; + + if (c->mem->used == 0 || c->mem->used == 1) { +@@ -75,6 +74,7 @@ + + offset = c->mem->ptr + c->offset; + toSend = c->mem->used - 1 - c->offset; ++ if (toSend > max_bytes) toSend = max_bytes; + + /** + * SSL_write man-page +@@ -87,7 +87,14 @@ + */ + + ERR_clear_error(); +- if ((r = SSL_write(ssl, offset, toSend)) <= 0) { ++ r = SSL_write(ssl, offset, toSend); ++ ++ if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { ++ log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client"); ++ return -1; ++ } ++ ++ if (r <= 0) { + unsigned long err; + + switch ((ssl_r = SSL_get_error(ssl, r))) { +@@ -139,6 +146,7 @@ + } else { + c->offset += r; + cq->bytes_out += r; ++ max_bytes -= r; + } + + if (c->offset == (off_t)c->mem->used - 1) { +@@ -168,6 +176,7 @@ + do { + off_t offset = c->file.start + c->offset; + off_t toSend = c->file.length - c->offset; ++ if (toSend > max_bytes) toSend = max_bytes; + + if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE; + +@@ -190,7 +199,14 @@ + close(ifd); + + ERR_clear_error(); +- if ((r = SSL_write(ssl, s, toSend)) <= 0) { ++ r = SSL_write(ssl, s, toSend); ++ ++ if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { ++ log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client"); ++ return -1; ++ } ++ ++ if (r <= 0) { + unsigned long err; + + switch ((ssl_r = SSL_get_error(ssl, r))) { +@@ -243,12 +259,13 @@ + } else { + c->offset += r; + cq->bytes_out += r; ++ max_bytes -= r; + } + + if (c->offset == c->file.length) { + chunk_finished = 1; + } +- } while(!chunk_finished && !write_wait); ++ } while (!chunk_finished && !write_wait && max_bytes > 0); + + break; + } +@@ -263,11 +280,9 @@ + + break; + } +- +- chunks_written++; + } + +- return chunks_written; ++ return 0; + } + #endif + +Index: src/http_auth.c +=================================================================== +--- src/http_auth.c (.../tags/lighttpd-1.4.29) ++++ src/http_auth.c (.../branches/lighttpd-1.4.x) +@@ -1,7 +1,6 @@ + #include "server.h" + #include "log.h" + #include "http_auth.h" +-#include "http_auth_digest.h" + #include "inet_ntop_cache.h" + #include "stream.h" + +@@ -28,18 +27,23 @@ + #include + #include + +-#ifdef USE_OPENSSL +-# include +-#else +-# include "md5.h" ++#include "md5.h" + +-typedef li_MD5_CTX MD5_CTX; +-#define MD5_Init li_MD5_Init +-#define MD5_Update li_MD5_Update +-#define MD5_Final li_MD5_Final ++#define HASHLEN 16 ++#define HASHHEXLEN 32 ++typedef unsigned char HASH[HASHLEN]; ++typedef char HASHHEX[HASHHEXLEN+1]; + +-#endif ++static void CvtHex(const HASH Bin, char Hex[33]) { ++ unsigned short i; + ++ for (i = 0; i < 16; i++) { ++ Hex[i*2] = int2hex((Bin[i] >> 4) & 0xf); ++ Hex[i*2+1] = int2hex(Bin[i] & 0xf); ++ } ++ Hex[32] = '\0'; ++} ++ + /** + * the $apr1$ handling is taken from apache 1.3.x + */ +@@ -95,7 +99,7 @@ + ch = in[0]; + /* run through the whole string, converting as we go */ + for (i = 0; i < in_len; i++) { +- ch = in[i]; ++ ch = (unsigned char) in[i]; + + if (ch == '\0') break; + +@@ -435,7 +439,7 @@ + + static void to64(char *s, unsigned long v, int n) + { +- static unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */ ++ static const unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + while (--n >= 0) { +@@ -455,7 +459,7 @@ + const char *sp, *ep; + unsigned char final[APR_MD5_DIGESTSIZE]; + ssize_t sl, pl, i; +- MD5_CTX ctx, ctx1; ++ li_MD5_CTX ctx, ctx1; + unsigned long l; + + /* +@@ -487,33 +491,33 @@ + /* + * 'Time to make the doughnuts..' + */ +- MD5_Init(&ctx); ++ li_MD5_Init(&ctx); + + /* + * The password first, since that is what is most unknown + */ +- MD5_Update(&ctx, pw, strlen(pw)); ++ li_MD5_Update(&ctx, pw, strlen(pw)); + + /* + * Then our magic string + */ +- MD5_Update(&ctx, APR1_ID, strlen(APR1_ID)); ++ li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID)); + + /* + * Then the raw salt + */ +- MD5_Update(&ctx, sp, sl); ++ li_MD5_Update(&ctx, sp, sl); + + /* + * Then just as many characters of the MD5(pw, salt, pw) + */ +- MD5_Init(&ctx1); +- MD5_Update(&ctx1, pw, strlen(pw)); +- MD5_Update(&ctx1, sp, sl); +- MD5_Update(&ctx1, pw, strlen(pw)); +- MD5_Final(final, &ctx1); ++ li_MD5_Init(&ctx1); ++ li_MD5_Update(&ctx1, pw, strlen(pw)); ++ li_MD5_Update(&ctx1, sp, sl); ++ li_MD5_Update(&ctx1, pw, strlen(pw)); ++ li_MD5_Final(final, &ctx1); + for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) { +- MD5_Update(&ctx, final, ++ li_MD5_Update(&ctx, final, + (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl); + } + +@@ -527,10 +531,10 @@ + */ + for (i = strlen(pw); i != 0; i >>= 1) { + if (i & 1) { +- MD5_Update(&ctx, final, 1); ++ li_MD5_Update(&ctx, final, 1); + } + else { +- MD5_Update(&ctx, pw, 1); ++ li_MD5_Update(&ctx, pw, 1); + } + } + +@@ -542,7 +546,7 @@ + strncat(passwd, sp, sl); + strcat(passwd, "$"); + +- MD5_Final(final, &ctx); ++ li_MD5_Final(final, &ctx); + + /* + * And now, just to make sure things don't run too fast.. +@@ -550,28 +554,28 @@ + * need 30 seconds to build a 1000 entry dictionary... + */ + for (i = 0; i < 1000; i++) { +- MD5_Init(&ctx1); ++ li_MD5_Init(&ctx1); + if (i & 1) { +- MD5_Update(&ctx1, pw, strlen(pw)); ++ li_MD5_Update(&ctx1, pw, strlen(pw)); + } + else { +- MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); ++ li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); + } + if (i % 3) { +- MD5_Update(&ctx1, sp, sl); ++ li_MD5_Update(&ctx1, sp, sl); + } + + if (i % 7) { +- MD5_Update(&ctx1, pw, strlen(pw)); ++ li_MD5_Update(&ctx1, pw, strlen(pw)); + } + + if (i & 1) { +- MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); ++ li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); + } + else { +- MD5_Update(&ctx1, pw, strlen(pw)); ++ li_MD5_Update(&ctx1, pw, strlen(pw)); + } +- MD5_Final(final,&ctx1); ++ li_MD5_Final(final,&ctx1); + } + + p = passwd + strlen(passwd); +@@ -614,17 +618,17 @@ + * user:realm:md5(user:realm:password) + */ + +- MD5_CTX Md5Ctx; ++ li_MD5_CTX Md5Ctx; + HASH HA1; + char a1[256]; + +- MD5_Init(&Md5Ctx); +- MD5_Update(&Md5Ctx, (unsigned char *)username->ptr, username->used - 1); +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); +- MD5_Update(&Md5Ctx, (unsigned char *)realm->ptr, realm->used - 1); +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); +- MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw)); +- MD5_Final(HA1, &Md5Ctx); ++ li_MD5_Init(&Md5Ctx); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)username->ptr, username->used - 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)realm->ptr, realm->used - 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw)); ++ li_MD5_Final(HA1, &Md5Ctx); + + CvtHex(HA1, a1); + +@@ -930,7 +934,7 @@ + int i; + buffer *password, *b, *username_buf, *realm_buf; + +- MD5_CTX Md5Ctx; ++ li_MD5_CTX Md5Ctx; + HASH HA1; + HASH HA2; + HASH RespHash; +@@ -1067,13 +1071,13 @@ + + if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) { + /* generate password from plain-text */ +- MD5_Init(&Md5Ctx); +- MD5_Update(&Md5Ctx, (unsigned char *)username, strlen(username)); +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); +- MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm)); +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); +- MD5_Update(&Md5Ctx, (unsigned char *)password->ptr, password->used - 1); +- MD5_Final(HA1, &Md5Ctx); ++ li_MD5_Init(&Md5Ctx); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)username, strlen(username)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)password->ptr, password->used - 1); ++ li_MD5_Final(HA1, &Md5Ctx); + } else if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) { + /* HA1 */ + /* transform the 32-byte-hex-md5 to a 16-byte-md5 */ +@@ -1090,45 +1094,45 @@ + + if (algorithm && + strcasecmp(algorithm, "md5-sess") == 0) { +- MD5_Init(&Md5Ctx); +- MD5_Update(&Md5Ctx, (unsigned char *)HA1, 16); +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); +- MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); +- MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); +- MD5_Final(HA1, &Md5Ctx); ++ li_MD5_Init(&Md5Ctx); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)HA1, 16); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); ++ li_MD5_Final(HA1, &Md5Ctx); + } + + CvtHex(HA1, a1); + + /* calculate H(A2) */ +- MD5_Init(&Md5Ctx); +- MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m)); +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); +- MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri)); ++ li_MD5_Init(&Md5Ctx); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri)); + if (qop && strcasecmp(qop, "auth-int") == 0) { +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); +- MD5_Update(&Md5Ctx, (unsigned char *)"", HASHHEXLEN); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)"", HASHHEXLEN); + } +- MD5_Final(HA2, &Md5Ctx); ++ li_MD5_Final(HA2, &Md5Ctx); + CvtHex(HA2, HA2Hex); + + /* calculate response */ +- MD5_Init(&Md5Ctx); +- MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); +- MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Init(&Md5Ctx); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + if (qop && *qop) { +- MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc)); +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); +- MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); +- MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop)); +- MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1); + }; +- MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN); +- MD5_Final(RespHash, &Md5Ctx); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN); ++ li_MD5_Final(RespHash, &Md5Ctx); + CvtHex(RespHash, a2); + + if (0 != strcmp(a2, respons)) { +@@ -1171,24 +1175,24 @@ + + int http_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char out[33]) { + HASH h; +- MD5_CTX Md5Ctx; ++ li_MD5_CTX Md5Ctx; + char hh[32]; + + UNUSED(p); + + /* generate shared-secret */ +- MD5_Init(&Md5Ctx); +- MD5_Update(&Md5Ctx, (unsigned char *)fn->ptr, fn->used - 1); +- MD5_Update(&Md5Ctx, (unsigned char *)"+", 1); ++ li_MD5_Init(&Md5Ctx); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)fn->ptr, fn->used - 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)"+", 1); + + /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */ + LI_ltostr(hh, srv->cur_ts); +- MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); +- MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy)); + LI_ltostr(hh, rand()); +- MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); + +- MD5_Final(h, &Md5Ctx); ++ li_MD5_Final(h, &Md5Ctx); + + CvtHex(h, out); + +Index: src/mod_usertrack.c +=================================================================== +--- src/mod_usertrack.c (.../tags/lighttpd-1.4.29) ++++ src/mod_usertrack.c (.../branches/lighttpd-1.4.x) +@@ -8,18 +8,8 @@ + #include + #include + +-#ifdef USE_OPENSSL +-# include +-#else +-# include "md5.h" ++#include "md5.h" + +-typedef li_MD5_CTX MD5_CTX; +-#define MD5_Init li_MD5_Init +-#define MD5_Update li_MD5_Update +-#define MD5_Final li_MD5_Final +- +-#endif +- + /* plugin config for all request/connections */ + + typedef struct { +@@ -182,7 +172,7 @@ + plugin_data *p = p_d; + data_string *ds; + unsigned char h[16]; +- MD5_CTX Md5Ctx; ++ li_MD5_CTX Md5Ctx; + char hh[32]; + + if (con->uri.path->used == 0) return HANDLER_GO_ON; +@@ -228,18 +218,18 @@ + /* taken from mod_auth.c */ + + /* generate shared-secret */ +- MD5_Init(&Md5Ctx); +- MD5_Update(&Md5Ctx, (unsigned char *)con->uri.path->ptr, con->uri.path->used - 1); +- MD5_Update(&Md5Ctx, (unsigned char *)"+", 1); ++ li_MD5_Init(&Md5Ctx); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)con->uri.path->ptr, con->uri.path->used - 1); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)"+", 1); + + /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */ + LI_ltostr(hh, srv->cur_ts); +- MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); +- MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy)); + LI_ltostr(hh, rand()); +- MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); ++ li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); + +- MD5_Final(h, &Md5Ctx); ++ li_MD5_Final(h, &Md5Ctx); + + buffer_append_string_encoded(ds->value, (char *)h, 16, ENCODING_HEX); + buffer_append_string_len(ds->value, CONST_STR_LEN("; Path=/")); +Index: src/mod_status.c +=================================================================== +--- src/mod_status.c (.../tags/lighttpd-1.4.29) ++++ src/mod_status.c (.../branches/lighttpd-1.4.x) +@@ -487,7 +487,7 @@ + + buffer_append_string_len(b, CONST_STR_LEN("")); + +- if (con->request.content_length) { ++ if (c->request.content_length) { + buffer_append_long(b, c->request_content_queue->bytes_in); + buffer_append_string_len(b, CONST_STR_LEN("/")); + buffer_append_long(b, c->request.content_length); +Index: src/settings.h +=================================================================== +--- src/settings.h (.../tags/lighttpd-1.4.29) ++++ src/settings.h (.../branches/lighttpd-1.4.x) +@@ -21,8 +21,11 @@ + * 64kB (no real reason, just a guess) + */ + #define BUFFER_MAX_REUSE_SIZE (4 * 1024) +-#define MAX_READ_LIMIT (4*1024*1024) + ++/* both should be way smaller than SSIZE_MAX :) */ ++#define MAX_READ_LIMIT (256*1024) ++#define MAX_WRITE_LIMIT (256*1024) ++ + /** + * max size of the HTTP request header + * +Index: src/mod_cml_lua.c +=================================================================== +--- src/mod_cml_lua.c (.../tags/lighttpd-1.4.29) ++++ src/mod_cml_lua.c (.../branches/lighttpd-1.4.x) +@@ -11,18 +11,6 @@ + #include + #include + +-#ifdef USE_OPENSSL +-# include +-#else +-# include "md5.h" +- +-typedef li_MD5_CTX MD5_CTX; +-#define MD5_Init li_MD5_Init +-#define MD5_Update li_MD5_Update +-#define MD5_Final li_MD5_Final +- +-#endif +- + #define HASHLEN 16 + typedef unsigned char HASH[HASHLEN]; + #define HASHHEXLEN 32 +Index: src/mod_fastcgi.c +=================================================================== +--- src/mod_fastcgi.c (.../tags/lighttpd-1.4.29) ++++ src/mod_fastcgi.c (.../branches/lighttpd-1.4.x) +@@ -3075,7 +3075,7 @@ + fcgi_set_state(srv, hctx, FCGI_STATE_WRITE); + /* fall through */ + case FCGI_STATE_WRITE: +- ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb); ++ ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT); + + chunkqueue_remove_finished_chunks(hctx->wb); + +@@ -3132,7 +3132,6 @@ + plugin_data *p = p_d; + + handler_ctx *hctx = con->plugin_ctx[p->id]; +- fcgi_proc *proc; + fcgi_extension_host *host; + + if (NULL == hctx) return HANDLER_GO_ON; +@@ -3201,7 +3200,6 @@ + /* ok, create the request */ + switch(fcgi_write_request(srv, hctx)) { + case HANDLER_ERROR: +- proc = hctx->proc; + host = hctx->host; + + if (hctx->state == FCGI_STATE_INIT || +Index: src/network_solaris_sendfilev.c +=================================================================== +--- src/network_solaris_sendfilev.c (.../tags/lighttpd-1.4.29) ++++ src/network_solaris_sendfilev.c (.../branches/lighttpd-1.4.x) +@@ -38,17 +38,16 @@ + */ + + +-int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fd, chunkqueue *cq) { ++int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { + chunk *c; +- size_t chunks_written = 0; + +- for(c = cq->first; c; c = c->next, chunks_written++) { ++ for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { + int chunk_finished = 0; + + switch(c->type) { + case MEM_CHUNK: { + char * offset; +- size_t toSend; ++ off_t toSend; + ssize_t r; + + size_t num_chunks, i; +@@ -77,9 +76,9 @@ + chunks[i].iov_base = offset; + + /* protect the return value of writev() */ +- if (toSend > SSIZE_MAX || +- num_bytes + toSend > SSIZE_MAX) { +- chunks[i].iov_len = SSIZE_MAX - num_bytes; ++ if (toSend > max_bytes || ++ (off_t) num_bytes + toSend > max_bytes) { ++ chunks[i].iov_len = max_bytes - num_bytes; + + num_chunks = i + 1; + break; +@@ -119,11 +118,10 @@ + + if (chunk_finished) { + /* skip the chunks from further touches */ +- chunks_written++; + c = c->next; + } else { + /* chunks_written + c = c->next is done in the for()*/ +- chunk_finished++; ++ chunk_finished = 1; + } + } else { + /* partially written */ +@@ -139,8 +137,8 @@ + } + case FILE_CHUNK: { + ssize_t r; +- off_t offset; +- size_t toSend, written; ++ off_t offset, toSend; ++ size_t written; + sendfilevec_t fvec; + stat_cache_entry *sce = NULL; + int ifd; +@@ -153,6 +151,7 @@ + + offset = c->file.start + c->offset; + toSend = c->file.length - c->offset; ++ if (toSend > max_bytes) toSend = max_bytes; + + if (offset > sce->st.st_size) { + log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); +@@ -186,6 +185,7 @@ + close(ifd); + c->offset += written; + cq->bytes_out += written; ++ max_bytes -= written; + + if (c->offset == c->file.length) { + chunk_finished = 1; +@@ -207,7 +207,7 @@ + } + } + +- return chunks_written; ++ return 0; + } + + #endif +Index: src/CMakeLists.txt +=================================================================== +Index: src/mod_dirlisting.c +=================================================================== +--- src/mod_dirlisting.c (.../tags/lighttpd-1.4.29) ++++ src/mod_dirlisting.c (.../branches/lighttpd-1.4.x) +@@ -657,7 +657,8 @@ + i = dir->used - 1; + + #ifdef HAVE_PATHCONF +- if (-1 == (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) { ++ if (0 >= (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) { ++ /* some broken fs (fuse) return 0 instead of -1 */ + #ifdef NAME_MAX + name_max = NAME_MAX; + #else +Index: src/network_linux_sendfile.c +=================================================================== +--- src/network_linux_sendfile.c (.../tags/lighttpd-1.4.29) ++++ src/network_linux_sendfile.c (.../branches/lighttpd-1.4.x) +@@ -27,17 +27,16 @@ + /* on linux 2.4.29 + debian/ubuntu we have crashes if this is enabled */ + #undef HAVE_POSIX_FADVISE + +-int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, chunkqueue *cq) { ++int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { + chunk *c; +- size_t chunks_written = 0; + +- for(c = cq->first; c; c = c->next, chunks_written++) { ++ for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { + int chunk_finished = 0; + + switch(c->type) { + case MEM_CHUNK: { + char * offset; +- size_t toSend; ++ off_t toSend; + ssize_t r; + + size_t num_chunks, i; +@@ -45,12 +44,10 @@ + chunk *tc; + size_t num_bytes = 0; + +- /* we can't send more then SSIZE_MAX bytes in one chunk */ +- + /* build writev list + * + * 1. limit: num_chunks < UIO_MAXIOV +- * 2. limit: num_bytes < SSIZE_MAX ++ * 2. limit: num_bytes < max_bytes + */ + for (num_chunks = 0, tc = c; + tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; +@@ -67,9 +64,9 @@ + chunks[i].iov_base = offset; + + /* protect the return value of writev() */ +- if (toSend > SSIZE_MAX || +- num_bytes + toSend > SSIZE_MAX) { +- chunks[i].iov_len = SSIZE_MAX - num_bytes; ++ if (toSend > max_bytes || ++ (off_t) num_bytes + toSend > max_bytes) { ++ chunks[i].iov_len = max_bytes - num_bytes; + + num_chunks = i + 1; + break; +@@ -100,6 +97,7 @@ + + /* check which chunks have been written */ + cq->bytes_out += r; ++ max_bytes -= r; + + for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) { + if (r >= (ssize_t)chunks[i].iov_len) { +@@ -109,11 +107,10 @@ + + if (chunk_finished) { + /* skip the chunks from further touches */ +- chunks_written++; + c = c->next; + } else { + /* chunks_written + c = c->next is done in the for()*/ +- chunk_finished++; ++ chunk_finished = 1; + } + } else { + /* partially written */ +@@ -130,13 +127,12 @@ + case FILE_CHUNK: { + ssize_t r; + off_t offset; +- size_t toSend; ++ off_t toSend; + stat_cache_entry *sce = NULL; + + offset = c->file.start + c->offset; +- /* limit the toSend to 2^31-1 bytes in a chunk */ +- toSend = c->file.length - c->offset > ((1 << 30) - 1) ? +- ((1 << 30) - 1) : c->file.length - c->offset; ++ toSend = c->file.length - c->offset; ++ if (toSend > max_bytes) toSend = max_bytes; + + /* open file if not already opened */ + if (-1 == c->file.fd) { +@@ -215,6 +211,7 @@ + + c->offset += r; + cq->bytes_out += r; ++ max_bytes -= r; + + if (c->offset == c->file.length) { + chunk_finished = 1; +@@ -243,7 +240,7 @@ + } + } + +- return chunks_written; ++ return 0; + } + + #endif +Index: tests/mod-auth.t +=================================================================== +--- tests/mod-auth.t (.../tags/lighttpd-1.4.29) ++++ tests/mod-auth.t (.../branches/lighttpd-1.4.x) +@@ -8,7 +8,7 @@ + + use strict; + use IO::Socket; +-use Test::More tests => 14; ++use Test::More tests => 15; + use LightyTest; + + my $tf = LightyTest->new(); +@@ -25,6 +25,14 @@ + + $t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ]; ++ok($tf->handle_http($t) == 0, 'Basic-Auth: Invalid base64 Auth-token'); ++ ++$t->{REQUEST} = ( < 44; ++use Test::More tests => 46; + use LightyTest; + + my $tf = LightyTest->new(); +@@ -413,5 +413,21 @@ + $t->{SLOWREQUEST} = 1; + ok($tf->handle_http($t) == 0, 'GET, slow \\r\\n\\r\\n (#2105)'); + ++print "\nPathinfo for static files\n"; ++$t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'Content-Type' => 'image/jpeg' } ]; ++ok($tf->handle_http($t) == 0, 'static file accepting pathinfo by default'); ++ ++$t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ]; ++ok($tf->handle_http($t) == 0, 'static file with forbidden pathinfo'); ++ + ok($tf->stop_proc == 0, "Stopping lighttpd"); + +Index: tests/wrapper.sh +=================================================================== +--- tests/wrapper.sh (.../tags/lighttpd-1.4.29) ++++ tests/wrapper.sh (.../branches/lighttpd-1.4.x) +@@ -6,4 +6,4 @@ + top_builddir=$2 + export SHELL srcdir top_builddir + +-$3 ++exec $3 +Index: tests/lighttpd.conf +=================================================================== +--- tests/lighttpd.conf (.../tags/lighttpd-1.4.29) ++++ tests/lighttpd.conf (.../branches/lighttpd-1.4.x) +@@ -149,6 +149,7 @@ + $HTTP["host"] == "zzz.example.org" { + server.document-root = env.SRCDIR + "/tmp/lighttpd/servers/www.example.org/pages/" + server.name = "zzz.example.org" ++ static-file.disable-pathinfo = "enable" + } + + $HTTP["host"] == "symlink.example.org" { +Index: configure.ac +=================================================================== +Index: doc/config/lighttpd.conf +=================================================================== +--- doc/config/lighttpd.conf (.../tags/lighttpd-1.4.29) ++++ doc/config/lighttpd.conf (.../branches/lighttpd-1.4.x) +@@ -394,6 +394,25 @@ + ## $SERVER["socket"] == "10.0.0.1:443" { + ## ssl.engine = "enable" + ## ssl.pemfile = "/etc/ssl/private/www.example.com.pem" ++## # ++## # Mitigate BEAST attack: ++## # ++## # A stricter base cipher suite. For details see: ++## # http://blog.ivanristic.com/2011/10/mitigating-the-beast-attack-on-tls.html ++## # ++## ssl.ciphers = "ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM" ++## # ++## # Make the server prefer the order of the server side cipher suite instead of the client suite. ++## # This is necessary to mitigate the BEAST attack (unless you disable all non RC4 algorithms). ++## # This option is enabled by default, but only used if ssl.ciphers is set. ++## # ++## # ssl.honor-cipher-order = "enable" ++## # ++## # Mitigate CVE-2009-3555 by disabling client triggered renegotation ++## # This is enabled by default. ++## # ++## # ssl.disable-client-renegotiation = "enable" ++## # + ## server.name = "www.example.com" + ## + ## server.document-root = "/srv/www/vhosts/example.com/www/" +Index: SConstruct +=================================================================== +Index: NEWS +=================================================================== +--- NEWS (.../tags/lighttpd-1.4.29) ++++ NEWS (.../branches/lighttpd-1.4.x) +@@ -3,7 +3,20 @@ + NEWS + ==== + +-- 1.4.29 - ++- 1.4.30 - ++ * Always use our 'own' md5 implementation, fixes linking issues on MacOS (fixes #2331) ++ * Limit amount of bytes we send in one go; fixes stalling in one connection and timeouts on slow systems. ++ * [ssl] fix build errors when Elliptic-Curve Diffie-Hellman is disabled ++ * Add static-file.disable-pathinfo option to prevent handling of urls like .../secret.php/image.jpg as static file ++ * Don't overwrite 401 (auth required) with 501 (unknown method) (fixes #2341) ++ * Fix mod_status bug: always showed "0/0" in the "Read" column for uploads (fixes #2351) ++ * [mod_auth] Fix signedness error in http_auth (fixes #2370, CVE-2011-4362) ++ * [ssl] count renegotiations to prevent client renegotiations ++ * [ssl] add option to honor server cipher order (fixes #2364, BEAST attack) ++ * [core] accept dots in ipv6 addresses in host header (fixes #2359) ++ * [ssl] fix ssl connection aborts if files are larger than the MAX_WRITE_LIMIT (256kb) ++ ++- 1.4.29 - 2011-07-03 + * Fix mod_proxy waiting for response even if content-length is 0 (fixes #2259) + * Silence annoying "connection closed: poll() -> ERR" error.log message (fixes #2257) + * mod_cgi: make read buffer as big as incoming data block +Index: CMakeLists.txt +=================================================================== diff --git a/lighttpd-branding.patch b/lighttpd-branding.patch new file mode 100644 index 0000000..9fdf8a5 --- /dev/null +++ b/lighttpd-branding.patch @@ -0,0 +1,11 @@ +--- lighttpd-1.4.22/src/response.c~ 2009-04-17 00:50:21.000000000 +0300 ++++ lighttpd-1.4.22/src/response.c 2009-04-17 00:51:22.174367972 +0300 +@@ -105,7 +105,7 @@ + + if (!have_server) { + if (buffer_is_empty(con->conf.server_tag)) { +- buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: " PACKAGE_DESC)); ++ buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: " PACKAGE_DESC " (PLD Linux)")); + } else if (con->conf.server_tag->used > 1) { + buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: ")); + buffer_append_string_encoded(b, CONST_BUF_LEN(con->conf.server_tag), ENCODING_HTTP_HEADER); diff --git a/lighttpd-mime.types.sh b/lighttpd-mime.types.sh new file mode 100644 index 0000000..5252638 --- /dev/null +++ b/lighttpd-mime.types.sh @@ -0,0 +1,63 @@ +#!/bin/sh +# Parse /etc/mime.types into lighttpd config format. +# Copyright (c) 2005 Elan Ruusamäe + +mimetypes="$1" + +# build mime.types from system mime.types +# get ones with extension +awk '!/^#/ && $2 { print } ' $mimetypes | \ +# lay out mime types with multiple extension as separate lines \ +awk '{for (a=2; a <= NF; a++) {printf("%s\t%s\n", $1, $a)}}' | \ +# sort it \ +LC_ALL=C sort -u > mime.types + +# build lighttpd.conf fragment +awk '{ + ext = $2; + type = $1; + charset = ""; + # add charset for "text/*" types + if (type ~ "text/") { + type = "\"" type "\" + mimetype_textcharsetheader" + } else { + type = "\"" type "\"" + } + + printf("\t\".%s\"%s=> %s,\n", ext, (length(ext) > 4 ? "\t" : "\t\t"), type); +}' \ + < mime.types | LC_ALL=C sort -r > mime.types.conf + +# sanity check. there can't be more than one mime type mapping for same extension +dup=$(awk -F'"' '{print $2}' mime.types.conf | sort | uniq -c | grep -v '1' | awk '{print $NF}') +if [ "$dup" ]; then + echo >&2 Found $(echo "$dup" | wc -w) extensions which have non-unique mime-type mapping: + echo "$dup" | sed -e 's,^, ,' >&2 + exit 1 +fi + +mv -f mime.types.conf mime.types.conf.tmp + +# header +cat >> mime.types.conf <> mime.types.conf + +# footer +cat >> mime.types.conf <s %b \"%{Referer}i\" \"%{User-Agent}i\"" + +# CLF +accesslog.format = "%h %l %u %t \"%r\" %>s %b" + +# Combined Log +#accesslog.format = "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" + +# CLF + virtualhost +#accesslog.format = "%h %l %u %t \"%r\" %>s %b %V" diff --git a/lighttpd-mod_alias.conf b/lighttpd-mod_alias.conf new file mode 100644 index 0000000..b790e29 --- /dev/null +++ b/lighttpd-mod_alias.conf @@ -0,0 +1,12 @@ +# Alias module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModAlias + +server.modules += ( + "mod_alias" +) + +#### alias module +#alias.url = ( +# "/cgi-bin/" => "/usr/lib/cgi-bin", +#) diff --git a/lighttpd-mod_auth.conf b/lighttpd-mod_auth.conf new file mode 100644 index 0000000..d8af909 --- /dev/null +++ b/lighttpd-mod_auth.conf @@ -0,0 +1,43 @@ +# Auth module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModAuth + +server.modules += ( + "mod_auth" +) + +#### auth module +## read authentication.txt for more info +## debugging +# 0 for off, 1 for 'auth-ok' messages, 2 for verbose debugging +#auth.debug = 1 +## type of backend +# plain, htpasswd, ldap or htdigest +#auth.backend = "plain" +# filename of the password storage +## for plain +#auth.backend.plain.userfile = "/etc/lighttpd/lighttpd.user" +#auth.backend.plain.groupfile = "/etc/lighttpd/lighttpd.group" +## for htpasswd +#auth.backend.htpasswd.userfile = "/etc/lighttpd/lighttpd-htpasswd.user" +## for htdigest +#auth.backend.htdigest.userfile = "/etc/lighttpd/lighttpd-htdigest.user" +## for ldap +# the $ in auth.backend.ldap.filter is replaced by the +# 'username' from the login dialog +#auth.backend.ldap.hostname = "localhost" +#auth.backend.ldap.base-dn = "dc=my-domain,dc=com" +#auth.backend.ldap.filter = "(uid=$)" + +#auth.require = ( +# "/server-status" => ( +# "method" => "digest", +# "realm" => "download archiv", +# "require" => "user=jan" +# ), +# "/server-info" => ( +# "method" => "digest", +# "realm" => "download archiv", +# "require" => "valid-user" +# ) +#) diff --git a/lighttpd-mod_cgi.conf b/lighttpd-mod_cgi.conf new file mode 100644 index 0000000..75ed293 --- /dev/null +++ b/lighttpd-mod_cgi.conf @@ -0,0 +1,30 @@ +# CGI module +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModCGI + +server.modules += ( + "mod_cgi" +) + +$HTTP["url"] =~ "/cgi-bin/" { + cgi.assign = ( "" => "" ) +} + +alias.url += ( + "/cgi-bin/" => "/home/services/lighttpd/cgi-bin/", + # FIXME: or use system shared cgi-bin here? +# "/cgi-bin/" => "/usr/lib/cgi-bin/", +) + +cgi.assign = ( + ".cgi" => "", +# ".pl" => "/usr/bin/perl", +# ".php" => "/usr/bin/php.cgi", +# ".py" => "/usr/bin/python", +) + +#static-file.exclude-extensions = ( +# ".pl", +# ".php", +# ".py", +#) diff --git a/lighttpd-mod_cgi_php.conf b/lighttpd-mod_cgi_php.conf new file mode 100644 index 0000000..32c1147 --- /dev/null +++ b/lighttpd-mod_cgi_php.conf @@ -0,0 +1,8 @@ +# Map PHP via CGI module + +cgi.assign += ( + ".php" => "/usr/bin/php.cgi", + ".php4" => "/usr/bin/php4.cgi", +) + +static-file.exclude-extensions += (".php") diff --git a/lighttpd-mod_cml.conf b/lighttpd-mod_cml.conf new file mode 100644 index 0000000..844a069 --- /dev/null +++ b/lighttpd-mod_cml.conf @@ -0,0 +1,21 @@ +# CML is a Meta language to describe the dependencies of a page +# at one side and building a page from its fragments on the +# other side using LUA. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModCML + +server.modules += ( + "mod_cml" +) + +## the extension for file with cache information. With .cml, +## the cache info file for index.html is index.cml +cml.extension = ".cml" + +index-file.names += ( "index" + cml.extension ) + +## the memcached used by mod_cml +#cml.memcache-hosts = ( "127.0.0.1:11211" ) + +## a cml file that is executed for each request +#cml.power-magnet = "/home/services/lighttpd/html/power-magnet.cml" diff --git a/lighttpd-mod_compress.conf b/lighttpd-mod_compress.conf new file mode 100644 index 0000000..7bbd6f1 --- /dev/null +++ b/lighttpd-mod_compress.conf @@ -0,0 +1,25 @@ +# Compress module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModCompress + +server.modules += ( + "mod_compress" +) + +# Default: "bzip2", "gzip", "deflate" +# Safe list is 'gzip, deflate'. bzip2 is problematic for some browsers (MSIE) +#compress.allowed-encodings = ( "gzip", "deflate" ) + +# Default: not set, compress the file for every request +# Filenames saved to this dir have ETag appended, so theoretically you can use +# common dir across domains for this. +compress.cache-dir = "/var/cache/lighttpd/mod_compress" + +# Keep in mind that compressed JavaScript and CSS files are broken in some browsers. +# Not setting any filetypes will result in no files being compressed. +#compress.filetype = ( +# "application/x-javascript", +# "text/css", +# "text/html", +# "text/xml", +#) diff --git a/lighttpd-mod_compress.tmpwatch b/lighttpd-mod_compress.tmpwatch new file mode 100644 index 0000000..73c4172 --- /dev/null +++ b/lighttpd-mod_compress.tmpwatch @@ -0,0 +1,2 @@ +# Cleanup lighttpd-mod_compress cache files: +/var/cache/lighttpd/mod_compress -u 10d diff --git a/lighttpd-mod_deflate.conf b/lighttpd-mod_deflate.conf new file mode 100644 index 0000000..ff5a0c0 --- /dev/null +++ b/lighttpd-mod_deflate.conf @@ -0,0 +1,7 @@ +# Deflate module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModDeflate + +server.modules += ( + "mod_deflate" +) diff --git a/lighttpd-mod_deflate.patch b/lighttpd-mod_deflate.patch new file mode 100644 index 0000000..fd2f545 --- /dev/null +++ b/lighttpd-mod_deflate.patch @@ -0,0 +1,2170 @@ +diff -Naur lighttpd-1.4.19/configure.in lighttpd-1.4.19.mod_deflate.jz/configure.in +--- lighttpd-1.4.19/configure.in ++++ lighttpd-1.4.19.mod_deflate.jz/configure.in +@@ -558,7 +558,7 @@ + AC_OUTPUT + + +-do_build="mod_cgi mod_fastcgi mod_extforward mod_proxy mod_evhost mod_simple_vhost mod_access mod_alias mod_setenv mod_usertrack mod_auth mod_status mod_accesslog mod_rrdtool mod_secdownload mod_expire mod_compress mod_dirlisting mod_indexfiles mod_userdir mod_webdav mod_staticfile mod_scgi mod_flv_streaming" ++do_build="mod_cgi mod_fastcgi mod_extforward mod_proxy mod_evhost mod_simple_vhost mod_access mod_alias mod_setenv mod_usertrack mod_auth mod_status mod_accesslog mod_rrdtool mod_secdownload mod_expire mod_compress mod_dirlisting mod_indexfiles mod_userdir mod_webdav mod_staticfile mod_scgi mod_flv_streaming mod_deflate" + + plugins="mod_rewrite mod_redirect mod_ssi mod_trigger_b4_dl" + features="regex-conditionals" +diff -Naur lighttpd-1.4.19/src/base.h lighttpd-1.4.19.mod_deflate.jz/src/base.h +--- lighttpd-1.4.19/src/base.h ++++ lighttpd-1.4.19.mod_deflate.jz/src/base.h +@@ -149,6 +149,7 @@ + + http_method_t http_method; + http_version_t http_version; ++ int true_http_10_client; + + buffer *request_line; + +@@ -360,8 +361,10 @@ + + int file_started; + int file_finished; ++ int end_chunk; /* used for chunked transfer encoding. */ + +- chunkqueue *write_queue; /* a large queue for low-level write ( HTTP response ) [ file, mem ] */ ++ chunkqueue *write_queue; /* a large queue for HTTP response content [ file, mem ] */ ++ chunkqueue *output_queue; /* a large queue for low-level write ( HTTP response ) [ file, mem ] */ + chunkqueue *read_queue; /* a small queue for low-level read ( HTTP request ) [ mem ] */ + chunkqueue *request_content_queue; /* takes request-content into tempfile if necessary [ tempfile, mem ]*/ + +@@ -596,6 +599,7 @@ + + connections *conns; + connections *joblist; ++ connections *joblist_prev; + connections *fdwaitqueue; + + stat_cache *stat_cache; +diff -Naur lighttpd-1.4.19/src/chunk.c lighttpd-1.4.19.mod_deflate.jz/src/chunk.c +--- lighttpd-1.4.19/src/chunk.c ++++ lighttpd-1.4.19.mod_deflate.jz/src/chunk.c +@@ -16,7 +16,9 @@ + #include + #include + ++#include "server.h" + #include "chunk.h" ++#include "log.h" + + chunkqueue *chunkqueue_init(void) { + chunkqueue *cq; +@@ -241,6 +243,16 @@ + return 0; + } + ++int chunkqueue_append_chunkqueue(chunkqueue *cq, chunkqueue *src) { ++ if(src == NULL) return 0; ++ chunkqueue_append_chunk(cq, src->first); ++ cq->last = src->last; ++ src->first = NULL; ++ src->last = NULL; ++ ++ return 0; ++} ++ + buffer * chunkqueue_get_prepend_buffer(chunkqueue *cq) { + chunk *c; + +@@ -400,3 +412,105 @@ + + return 0; + } ++ ++/** ++ * the HTTP chunk-API ++ * ++ * ++ */ ++ ++static int chunk_encode_append_len(chunkqueue *cq, size_t len) { ++ size_t i, olen = len, j; ++ buffer *b; ++ ++ /*b = srv->tmp_chunk_len;*/ ++ /*b = buffer_init();*/ ++ b = chunkqueue_get_append_buffer(cq); ++ ++ if (len == 0) { ++ buffer_copy_string(b, "0"); ++ } else { ++ for (i = 0; i < 8 && len; i++) { ++ len >>= 4; ++ } ++ ++ /* i is the number of hex digits we have */ ++ buffer_prepare_copy(b, i + 1); ++ ++ for (j = i-1, len = olen; j+1 > 0; j--) { ++ b->ptr[j] = (len & 0xf) + (((len & 0xf) <= 9) ? '0' : 'a' - 10); ++ len >>= 4; ++ } ++ b->used = i; ++ b->ptr[b->used++] = '\0'; ++ } ++ ++ buffer_append_string(b, "\r\n"); ++ /* ++ chunkqueue_append_buffer(cq, b); ++ buffer_free(b); ++ */ ++ ++ return 0; ++} ++ ++ ++int chunk_encode_append_file(chunkqueue *cq, buffer *fn, off_t offset, off_t len) { ++ if (!cq) return -1; ++ if (len == 0) return 0; ++ ++ chunk_encode_append_len(cq, len); ++ ++ chunkqueue_append_file(cq, fn, offset, len); ++ ++ chunkqueue_append_mem(cq, "\r\n", 2 + 1); ++ ++ return 0; ++} ++ ++int chunk_encode_append_buffer(chunkqueue *cq, buffer *mem) { ++ if (!cq) return -1; ++ if (mem->used <= 1) return 0; ++ ++ chunk_encode_append_len(cq, mem->used - 1); ++ ++ chunkqueue_append_buffer(cq, mem); ++ ++ chunkqueue_append_mem(cq, "\r\n", 2 + 1); ++ ++ return 0; ++} ++ ++int chunk_encode_append_mem(chunkqueue *cq, const char * mem, size_t len) { ++ if (!cq) return -1; ++ if (len <= 1) return 0; ++ ++ chunk_encode_append_len(cq, len - 1); ++ ++ chunkqueue_append_mem(cq, mem, len); ++ ++ chunkqueue_append_mem(cq, "\r\n", 2 + 1); ++ ++ return 0; ++} ++ ++int chunk_encode_append_queue(chunkqueue *cq, chunkqueue *src) { ++ int len = chunkqueue_length(src); ++ if (!cq) return -1; ++ if (len == 0) return 0; ++ ++ chunk_encode_append_len(cq, len); ++ ++ chunkqueue_append_chunkqueue(cq, src); ++ ++ chunkqueue_append_mem(cq, "\r\n", 2 + 1); ++ ++ return 0; ++} ++ ++int chunk_encode_end(chunkqueue *cq) { ++ chunk_encode_append_len(cq, 0); ++ chunkqueue_append_mem(cq, "\r\n", 2 + 1); ++ return 0; ++} ++ +diff -Naur lighttpd-1.4.19/src/chunk.h lighttpd-1.4.19.mod_deflate.jz/src/chunk.h +--- lighttpd-1.4.19/src/chunk.h ++++ lighttpd-1.4.19.mod_deflate.jz/src/chunk.h +@@ -52,6 +52,7 @@ + int chunkqueue_append_mem(chunkqueue *c, const char *mem, size_t len); + int chunkqueue_append_buffer(chunkqueue *c, buffer *mem); + int chunkqueue_append_buffer_weak(chunkqueue *c, buffer *mem); ++int chunkqueue_append_chunkqueue(chunkqueue *cq, chunkqueue *src); + int chunkqueue_prepend_buffer(chunkqueue *c, buffer *mem); + + buffer * chunkqueue_get_append_buffer(chunkqueue *c); +@@ -67,4 +68,10 @@ + + int chunkqueue_is_empty(chunkqueue *c); + ++int chunk_encode_append_mem(chunkqueue *cq, const char * mem, size_t len); ++int chunk_encode_append_buffer(chunkqueue *cq, buffer *mem); ++int chunk_encode_append_file(chunkqueue *cq, buffer *fn, off_t offset, off_t len); ++int chunk_encode_append_queue(chunkqueue *cq, chunkqueue *src); ++int chunk_encode_end(chunkqueue *cq); ++ + #endif +diff -Naur lighttpd-1.4.19/src/connections.c lighttpd-1.4.19.mod_deflate.jz/src/connections.c +--- lighttpd-1.4.19/src/connections.c ++++ lighttpd-1.4.19.mod_deflate.jz/src/connections.c +@@ -18,6 +18,7 @@ + #include "response.h" + #include "network.h" + #include "http_chunk.h" ++#include "chunk.h" + #include "stat_cache.h" + #include "joblist.h" + +@@ -146,6 +147,12 @@ + return 0; + } + ++int connection_queue_is_empty(connection *con) { ++ if(!chunkqueue_is_empty(con->write_queue)) return 0; ++ if(!chunkqueue_is_empty(con->output_queue)) return 0; ++ return 1; ++} ++ + #if 0 + static void dump_packet(const unsigned char *data, size_t len) { + size_t i, j; +@@ -405,6 +412,7 @@ + con->file_finished = 1; + + chunkqueue_reset(con->write_queue); ++ chunkqueue_reset(con->output_queue); + } + break; + default: +@@ -517,12 +525,27 @@ + /* disable chunked encoding again as we have no body */ + con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; + con->parsed_response &= ~HTTP_CONTENT_LENGTH; + chunkqueue_reset(con->write_queue); ++ chunkqueue_reset(con->output_queue); + + con->file_finished = 1; + break; + } + ++ /* Allow filter plugins to change response headers before they are written. */ ++ switch(plugins_call_handle_response_start(srv, con)) { ++ case HANDLER_GO_ON: ++ case HANDLER_FINISHED: ++ /* response start is finished */ ++ break; ++ default: ++ /* something strange happend */ ++ log_error_write(srv, __FILE__, __LINE__, "s", "Filter plugin failed."); ++ connection_set_state(srv, con, CON_STATE_ERROR); ++ joblist_append(srv, con); ++ break; ++ } ++ + if (con->file_finished) { + /* we have all the content and chunked encoding is not used, set a content-length */ + +@@ -592,8 +615,9 @@ + * without the content + */ + con->file_finished = 1; + + chunkqueue_reset(con->write_queue); ++ chunkqueue_reset(con->output_queue); + con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; + } + +@@ -603,11 +627,57 @@ + } + + static int connection_handle_write(server *srv, connection *con) { +- switch(network_write_chunkqueue(srv, con, con->write_queue)) { ++ int finished = 0; ++ int len; ++ ++ /* Allow filter plugins to modify response conent */ ++ switch(plugins_call_handle_response_filter(srv, con)) { ++ case HANDLER_GO_ON: ++ finished = con->file_finished; ++ /* response content not changed */ ++ break; ++ case HANDLER_COMEBACK: ++ /* response filter has more work */ ++ finished = 0; ++ break; ++ case HANDLER_FINISHED: ++ /* response filter is finished */ ++ finished = 1; ++ break; ++ default: ++ /* something strange happend */ ++ log_error_write(srv, __FILE__, __LINE__, "s", "Filter plugin failed."); ++ connection_set_state(srv, con, CON_STATE_ERROR); ++ joblist_append(srv, con); ++ finished = 1; ++ break; ++ } ++ ++ /* move chunks from write_queue to output_queue. */ ++ if (con->request.http_method == HTTP_METHOD_HEAD) { ++ chunkqueue_reset(con->write_queue); ++ } else { ++ len = chunkqueue_length(con->write_queue); ++ if(con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { ++ chunk_encode_append_queue(con->output_queue, con->write_queue); ++ if(finished && !con->end_chunk) { ++ con->end_chunk = 1; ++ chunk_encode_end(con->output_queue); ++ } ++ } else { ++ chunkqueue_append_chunkqueue(con->output_queue, con->write_queue); ++ } ++ con->write_queue->bytes_out += len; ++ } ++ /* write chunks from output_queue to network */ ++ switch(network_write_chunkqueue(srv, con, con->output_queue)) { + case 0: +- if (con->file_finished) { ++ if (finished) { + connection_set_state(srv, con, CON_STATE_RESPONSE_END); + joblist_append(srv, con); ++ } else { ++ /* not finished yet -> WRITE */ ++ con->is_writable = 1; + } + break; + case -1: /* error on our side */ +@@ -678,6 +747,7 @@ + + #undef CLEAN + con->write_queue = chunkqueue_init(); ++ con->output_queue = chunkqueue_init(); + con->read_queue = chunkqueue_init(); + con->request_content_queue = chunkqueue_init(); + chunkqueue_set_tempdirs(con->request_content_queue, srv->srvconf.upload_tempdirs); +@@ -705,6 +776,7 @@ + connection_reset(srv, con); + + chunkqueue_free(con->write_queue); ++ chunkqueue_free(con->output_queue); + chunkqueue_free(con->read_queue); + chunkqueue_free(con->request_content_queue); + array_free(con->request.headers); +@@ -759,7 +831,10 @@ + con->http_status = 0; + con->file_finished = 0; + con->file_started = 0; ++ con->end_chunk = 0; + con->got_response = 0; ++// con->use_cache_file = 0; ++// con->write_cache_file = 0; + + con->parsed_response = 0; + +@@ -829,6 +904,7 @@ + array_reset(con->environment); + + chunkqueue_reset(con->write_queue); ++ chunkqueue_reset(con->output_queue); + chunkqueue_reset(con->request_content_queue); + + /* the plugins should cleanup themself */ +@@ -1223,7 +1299,6 @@ + } + + if (con->state == CON_STATE_WRITE && +- !chunkqueue_is_empty(con->write_queue) && + con->is_writable) { + + if (-1 == connection_handle_write(srv, con)) { +@@ -1640,15 +1715,15 @@ + } + + /* only try to write if we have something in the queue */ +- if (!chunkqueue_is_empty(con->write_queue)) { + #if 0 ++ if (!connection_queue_is_empty(con)) { + log_error_write(srv, __FILE__, __LINE__, "dsd", + con->fd, + "packets to write:", +- con->write_queue->used); +-#endif ++ con->output_queue->used); + } +- if (!chunkqueue_is_empty(con->write_queue) && con->is_writable) { ++#endif ++ if (con->is_writable) { + if (-1 == connection_handle_write(srv, con)) { + log_error_write(srv, __FILE__, __LINE__, "ds", + con->fd, +@@ -1758,9 +1833,9 @@ + * - if we have data to write + * - if the socket is not writable yet + */ +- if (!chunkqueue_is_empty(con->write_queue) && +- (con->is_writable == 0) && +- (con->traffic_limit_reached == 0)) { ++ if ((con->is_writable == 0) && ++ (con->traffic_limit_reached == 0) && ++ !connection_queue_is_empty(con)) { + fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_OUT); + } else { + fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); +diff -Naur lighttpd-1.4.19/src/http_chunk.c lighttpd-1.4.19.mod_deflate.jz/src/http_chunk.c +--- lighttpd-1.4.19/src/http_chunk.c ++++ lighttpd-1.4.19.mod_deflate.jz/src/http_chunk.c +@@ -58,16 +58,9 @@ + + cq = con->write_queue; + +- if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { +- http_chunk_append_len(srv, con, len); +- } + + chunkqueue_append_file(cq, fn, offset, len); + +- if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED && len > 0) { +- chunkqueue_append_mem(cq, "\r\n", 2 + 1); +- } +- + return 0; + } + +@@ -78,16 +71,9 @@ + + cq = con->write_queue; + +- if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { +- http_chunk_append_len(srv, con, mem->used - 1); +- } + + chunkqueue_append_buffer(cq, mem); + +- if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED && mem->used > 0) { +- chunkqueue_append_mem(cq, "\r\n", 2 + 1); +- } +- + return 0; + } + +@@ -99,24 +85,11 @@ + cq = con->write_queue; + + if (len == 0) { +- if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { +- chunkqueue_append_mem(cq, "0\r\n\r\n", 5 + 1); +- } else { +- chunkqueue_append_mem(cq, "", 1); +- } + return 0; + } + +- if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { +- http_chunk_append_len(srv, con, len - 1); +- } +- + chunkqueue_append_mem(cq, mem, len); + +- if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { +- chunkqueue_append_mem(cq, "\r\n", 2 + 1); +- } +- + return 0; + } + +diff -Naur lighttpd-1.4.19/src/joblist.c lighttpd-1.4.19.mod_deflate.jz/src/joblist.c +--- lighttpd-1.4.19/src/joblist.c ++++ lighttpd-1.4.19.mod_deflate.jz/src/joblist.c +@@ -7,6 +7,7 @@ + + int joblist_append(server *srv, connection *con) { + if (con->in_joblist) return 0; ++ con->in_joblist = 1; + + if (srv->joblist->size == 0) { + srv->joblist->size = 16; +diff -Naur lighttpd-1.4.19/src/Makefile.am lighttpd-1.4.19.mod_deflate.jz/src/Makefile.am +--- lighttpd-1.4.19/src/Makefile.am ++++ lighttpd-1.4.19.mod_deflate.jz/src/Makefile.am +@@ -241,6 +241,11 @@ + mod_accesslog_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined + mod_accesslog_la_LIBADD = $(common_libadd) + ++lib_LTLIBRARIES += mod_deflate.la ++mod_deflate_la_SOURCES = mod_deflate.c ++mod_deflate_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined ++mod_deflate_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd) ++ + + hdr = server.h buffer.h network.h log.h keyvalue.h \ + response.h request.h fastcgi.h chunk.h \ +diff -Naur lighttpd-1.4.19/src/Makefile.in lighttpd-1.4.19.mod_deflate.jz/src/Makefile.in +--- lighttpd-1.4.19/src/Makefile.in ++++ lighttpd-1.4.19.mod_deflate.jz/src/Makefile.in +@@ -158,8 +158,15 @@ + am_mod_compress_la_OBJECTS = mod_compress.lo + mod_compress_la_OBJECTS = $(am_mod_compress_la_OBJECTS) + mod_compress_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_compress_la_LDFLAGS) $(LDFLAGS) -o $@ ++mod_deflate_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ ++ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) ++am_mod_deflate_la_OBJECTS = mod_deflate.lo ++mod_deflate_la_OBJECTS = $(am_mod_deflate_la_OBJECTS) ++mod_deflate_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ ++ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ ++ $(mod_deflate_la_LDFLAGS) $(LDFLAGS) -o $@ + mod_dirlisting_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_1) + am_mod_dirlisting_la_OBJECTS = mod_dirlisting.lo +@@ -399,6 +404,7 @@ + $(mod_accesslog_la_SOURCES) $(mod_alias_la_SOURCES) \ + $(mod_auth_la_SOURCES) $(mod_cgi_la_SOURCES) \ + $(mod_cml_la_SOURCES) $(mod_compress_la_SOURCES) \ ++ $(mod_deflate_la_SOURCES) \ + $(mod_dirlisting_la_SOURCES) $(mod_evasive_la_SOURCES) \ + $(mod_evhost_la_SOURCES) $(mod_expire_la_SOURCES) \ + $(mod_fastcgi_la_SOURCES) $(mod_flv_streaming_la_SOURCES) \ +@@ -614,7 +619,7 @@ + mod_ssi.la mod_secdownload.la mod_expire.la mod_evhost.la \ + mod_simple_vhost.la mod_fastcgi.la mod_extforward.la \ + mod_access.la mod_compress.la mod_auth.la mod_rewrite.la \ +- mod_redirect.la mod_status.la mod_accesslog.la ++ mod_redirect.la mod_status.la mod_accesslog.la mod_deflate.la + @NO_RDYNAMIC_TRUE@liblightcomp_la_SOURCES = $(common_src) + @NO_RDYNAMIC_TRUE@liblightcomp_la_CFLAGS = $(AM_CFLAGS) + @NO_RDYNAMIC_TRUE@liblightcomp_la_LDFLAGS = -avoid-version -no-undefined +@@ -721,6 +726,9 @@ + mod_accesslog_la_SOURCES = mod_accesslog.c + mod_accesslog_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined + mod_accesslog_la_LIBADD = $(common_libadd) ++mod_deflate_la_SOURCES = mod_deflate.c ++mod_deflate_la_LDFLAGS = -module -export-dynamic -no-undefined ++mod_deflate_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd) + hdr = server.h buffer.h network.h log.h keyvalue.h \ + response.h request.h fastcgi.h chunk.h \ + settings.h http_chunk.h http_auth_digest.h \ +@@ -832,6 +840,8 @@ + $(mod_cml_la_LINK) -rpath $(libdir) $(mod_cml_la_OBJECTS) $(mod_cml_la_LIBADD) $(LIBS) + mod_compress.la: $(mod_compress_la_OBJECTS) $(mod_compress_la_DEPENDENCIES) + $(mod_compress_la_LINK) -rpath $(libdir) $(mod_compress_la_OBJECTS) $(mod_compress_la_LIBADD) $(LIBS) ++mod_deflate.la: $(mod_deflate_la_OBJECTS) $(mod_deflate_la_DEPENDENCIES) ++ $(mod_deflate_la_LINK) -rpath $(libdir) $(mod_deflate_la_LDFLAGS) $(mod_deflate_la_OBJECTS) $(mod_deflate_la_LIBADD) $(LIBS) + mod_dirlisting.la: $(mod_dirlisting_la_OBJECTS) $(mod_dirlisting_la_DEPENDENCIES) + $(mod_dirlisting_la_LINK) -rpath $(libdir) $(mod_dirlisting_la_OBJECTS) $(mod_dirlisting_la_LIBADD) $(LIBS) + mod_evasive.la: $(mod_evasive_la_OBJECTS) $(mod_evasive_la_DEPENDENCIES) +@@ -1050,6 +1060,7 @@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_cml_la-mod_cml_funcs.Plo@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_cml_la-mod_cml_lua.Plo@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_compress.Plo@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_deflate.Plo@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_dirlisting.Plo@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_evasive.Plo@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_evhost.Plo@am__quote@ +diff -Naur lighttpd-1.4.19/src/mod_deflate.c lighttpd-1.4.19.mod_deflate.jz/src/mod_deflate.c +--- lighttpd-1.4.19/src/mod_deflate.c ++++ lighttpd-1.4.19.mod_deflate.jz/src/mod_deflate.c +@@ -0,0 +1,1420 @@ ++/* bug fix on Robert Jakabosky from alphatrade.com's lighttp 1.4.10 mod_deflate patch ++ * ++ * new module option: ++ * deflate.nocompress-url = "^/nocompressurl/" # pcre regex which don't compress ++ * ++ * Bug fix and new features: ++ * 1) fix loop bug when content-length is bigger than work-block-size*k ++ * 2) prevent compress on buggy http 1.0 client with Accept Encoding: gzip, deflate ++ * 3) fix bug with chunk transfer encoding (under mod_fastcgi+php environment) ++ * ++ * deflate.sync-flush = "enable" is buggy on chunk encoding transfer. Use it carefully, ++ */ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if defined(HAVE_PCRE_H) ++#include ++#endif ++ ++#include "base.h" ++#include "log.h" ++#include "buffer.h" ++#include "response.h" ++#include "joblist.h" ++#include "stat_cache.h" ++ ++#include "plugin.h" ++ ++#include "crc32.h" ++#include "etag.h" ++#include "inet_ntop_cache.h" ++ ++#if defined HAVE_ZLIB_H && defined HAVE_LIBZ ++# define USE_ZLIB ++# include ++#else ++# define Z_DEFAULT_COMPRESSION 1 ++#endif ++ ++#if defined HAVE_BZLIB_H && defined HAVE_LIBBZ2 ++# define USE_BZ2LIB ++/* we don't need stdio interface */ ++# define BZ_NO_STDIO ++# include ++#endif ++ ++#include "sys-mmap.h" ++ ++/* request: accept-encoding */ ++#define HTTP_ACCEPT_ENCODING_IDENTITY BV(0) ++#define HTTP_ACCEPT_ENCODING_GZIP BV(1) ++#define HTTP_ACCEPT_ENCODING_DEFLATE BV(2) ++#define HTTP_ACCEPT_ENCODING_COMPRESS BV(3) ++#define HTTP_ACCEPT_ENCODING_BZIP2 BV(4) ++ ++#define KByte * 1024 ++#define MByte * 1024 KByte ++#define GByte * 1024 MByte ++ ++typedef struct { ++ unsigned short debug; ++ unsigned short enabled; ++ unsigned short bzip2; ++ unsigned short sync_flush; ++ unsigned short output_buffer_size; ++ unsigned short min_compress_size; ++ unsigned short work_block_size; ++ short mem_level; ++ short compression_level; ++ short window_size; ++ array *mimetypes; ++ buffer *nocompress_url; ++#if defined(HAVE_PCRE_H) ++ pcre *nocompress_regex; ++#endif ++} plugin_config; ++ ++typedef struct { ++ PLUGIN_DATA; ++ buffer *tmp_buf; ++ ++ plugin_config **config_storage; ++ plugin_config conf; ++} plugin_data; ++ ++typedef struct { ++ int bytes_in; ++ int bytes_out; ++ chunkqueue *in_queue; ++ buffer *output; ++ /* compression type & state */ ++ int compression_type; ++ int stream_open; ++#ifdef USE_ZLIB ++ unsigned long crc; ++ z_stream z; ++ unsigned short gzip_header; ++#endif ++#ifdef USE_BZ2LIB ++ bz_stream bz; ++#endif ++ plugin_data *plugin_data; ++} handler_ctx; ++ ++static handler_ctx *handler_ctx_init() { ++ handler_ctx *hctx; ++ ++ hctx = calloc(1, sizeof(*hctx)); ++ hctx->in_queue = chunkqueue_init(); ++ ++ return hctx; ++} ++ ++static void handler_ctx_free(handler_ctx *hctx) { ++ chunkqueue_free(hctx->in_queue); ++ free(hctx); ++} ++ ++INIT_FUNC(mod_deflate_init) { ++ plugin_data *p; ++ ++ p = calloc(1, sizeof(*p)); ++ ++ p->tmp_buf = buffer_init(); ++ ++ return p; ++} ++ ++FREE_FUNC(mod_deflate_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 (!s) continue; ++ ++ array_free(s->mimetypes); ++ buffer_free(s->nocompress_url); ++#if defined(HAVE_PCRE_H) ++ if (s->nocompress_regex) pcre_free(s->nocompress_regex); ++#endif ++ free(s); ++ } ++ free(p->config_storage); ++ } ++ ++ buffer_free(p->tmp_buf); ++ ++ free(p); ++ ++ return HANDLER_GO_ON; ++} ++ ++SETDEFAULTS_FUNC(mod_deflate_setdefaults) { ++ plugin_data *p = p_d; ++ size_t i = 0; ++ ++ config_values_t cv[] = { ++ { "deflate.output-buffer-size", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, ++ { "deflate.mimetypes", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, ++ { "deflate.compression-level", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, ++ { "deflate.mem-level", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, ++ { "deflate.window-size", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, ++ { "deflate.min-compress-size", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, ++ { "deflate.work-block-size", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, ++ { "deflate.enabled", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, ++ { "deflate.debug", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, ++ { "deflate.bzip2", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, ++ { "deflate.sync-flush", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, ++ { "deflate.nocompress-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, ++ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } ++ }; ++ ++ p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); ++ ++ for (i = 0; i < srv->config_context->used; i++) { ++ plugin_config *s; ++#if defined(HAVE_PCRE_H) ++ const char *errptr; ++ int erroff; ++#endif ++ ++ s = calloc(1, sizeof(plugin_config)); ++ s->enabled = 1; ++ s->bzip2 = 1; ++ s->sync_flush = 0; ++ s->debug = 0; ++ s->output_buffer_size = 0; ++ s->mem_level = 9; ++ s->window_size = 15; ++ s->min_compress_size = 0; ++ s->work_block_size = 2048; ++ s->compression_level = Z_DEFAULT_COMPRESSION; ++ s->mimetypes = array_init(); ++ s->nocompress_url = buffer_init(); ++#if defined(HAVE_PCRE_H) ++ s->nocompress_regex = NULL; ++#endif ++ ++ cv[0].destination = &(s->output_buffer_size); ++ cv[1].destination = s->mimetypes; ++ cv[2].destination = &(s->compression_level); ++ cv[3].destination = &(s->mem_level); ++ cv[4].destination = &(s->window_size); ++ cv[5].destination = &(s->min_compress_size); ++ cv[6].destination = &(s->work_block_size); ++ cv[7].destination = &(s->enabled); ++ cv[8].destination = &(s->debug); ++ cv[9].destination = &(s->bzip2); ++ cv[10].destination = &(s->sync_flush); ++ cv[11].destination = s->nocompress_url; ++ ++ p->config_storage[i] = s; ++ ++ if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { ++ return HANDLER_ERROR; ++ } ++ ++#if defined(HAVE_PCRE_H) ++ if (!buffer_is_empty(s->nocompress_url)) { ++ if (NULL == (s->nocompress_regex = pcre_compile(s->nocompress_url->ptr, ++ 0, &errptr, &erroff, NULL))) { ++ ++ log_error_write(srv, __FILE__, __LINE__, "sbss", ++ "compiling regex for nocompress-url failed:", ++ s->nocompress_url, "pos:", erroff); ++ return HANDLER_ERROR; ++ } ++ } ++#endif ++ if((s->compression_level < 1 || s->compression_level > 9) && ++ s->compression_level != Z_DEFAULT_COMPRESSION) { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "compression-level must be between 1 and 9:", s->compression_level); ++ return HANDLER_ERROR; ++ } ++ ++ if(s->mem_level < 1 || s->mem_level > 9) { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "mem-level must be between 1 and 9:", s->mem_level); ++ return HANDLER_ERROR; ++ } ++ ++ if(s->window_size < 1 || s->window_size > 15) { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "window-size must be between 1 and 15:", s->window_size); ++ return HANDLER_ERROR; ++ } ++ s->window_size = 0 - s->window_size; ++ ++ if(s->sync_flush) { ++ s->output_buffer_size = 0; ++ } ++ } ++ ++ return HANDLER_GO_ON; ++ ++} ++ ++#ifdef USE_ZLIB ++/* Copied gzip_header from apache 2.2's mod_deflate.c */ ++/* RFC 1952 Section 2.3 defines the gzip header: ++ * ++ * +---+---+---+---+---+---+---+---+---+---+ ++ * |ID1|ID2|CM |FLG| MTIME |XFL|OS | ++ * +---+---+---+---+---+---+---+---+---+---+ ++ */ ++static const char gzip_header[10] = ++{ '\037', '\213', Z_DEFLATED, 0, ++ 0, 0, 0, 0, /* mtime */ ++ 0, 0x03 /* Unix OS_CODE */ ++}; ++static int stream_deflate_init(server *srv, connection *con, handler_ctx *hctx) { ++ plugin_data *p = hctx->plugin_data; ++ z_stream *z; ++ ++ UNUSED(srv); ++ UNUSED(con); ++ ++ z = &(hctx->z); ++ z->zalloc = Z_NULL; ++ z->zfree = Z_NULL; ++ z->opaque = Z_NULL; ++ z->total_in = 0; ++ z->total_out = 0; ++ z->next_out = NULL; ++ z->avail_out = 0; ++ ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "output-buffer-size:", p->conf.output_buffer_size); ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "compression-level:", p->conf.compression_level); ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "mem-level:", p->conf.mem_level); ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "window-size:", p->conf.window_size); ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "min-compress-size:", p->conf.min_compress_size); ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "work-block-size:", p->conf.work_block_size); ++ } ++ if (Z_OK != deflateInit2(z, ++ p->conf.compression_level, ++ Z_DEFLATED, ++ p->conf.window_size, /* supress zlib-header */ ++ p->conf.mem_level, ++ Z_DEFAULT_STRATEGY)) { ++ return -1; ++ } ++ hctx->stream_open = 1; ++ ++ return 0; ++} ++ ++static int stream_deflate_compress(server *srv, connection *con, handler_ctx *hctx, unsigned char *start, off_t st_size) { ++ plugin_data *p = hctx->plugin_data; ++ z_stream *z; ++ int len; ++ int in = 0, out = 0; ++ ++ UNUSED(srv); ++ z = &(hctx->z); ++ ++ if(z->next_out == NULL) { ++ z->next_out = (unsigned char *)hctx->output->ptr; ++ z->avail_out = hctx->output->size; ++ } ++ ++ if(hctx->compression_type == HTTP_ACCEPT_ENCODING_GZIP) { ++ if(hctx->gzip_header == 0) { ++ hctx->gzip_header = 1; ++ /* copy gzip header into output buffer */ ++ buffer_copy_memory(hctx->output, gzip_header, sizeof(gzip_header)); ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "gzip_header len=", sizeof(gzip_header)); ++ } ++ /* initialize crc32 */ ++ hctx->crc = crc32(0L, Z_NULL, 0); ++ z->next_out = (unsigned char *)(hctx->output->ptr + sizeof(gzip_header)); ++ z->avail_out = hctx->output->size - sizeof(gzip_header); ++ } ++ hctx->crc = crc32(hctx->crc, start, st_size); ++ } ++ ++ z->next_in = start; ++ z->avail_in = st_size; ++ hctx->bytes_in += st_size; ++ ++ /* compress data */ ++ in = z->avail_in; ++ do { ++ if (Z_OK != deflate(z, Z_NO_FLUSH)) { ++ deflateEnd(z); ++ hctx->stream_open = 0; ++ return -1; ++ } ++ ++ if(z->avail_out == 0 || z->avail_in > 0) { ++ len = hctx->output->size - z->avail_out; ++ hctx->bytes_out += len; ++ out += len; ++ chunkqueue_append_mem(con->write_queue, hctx->output->ptr, len+1); ++ z->next_out = (unsigned char *)hctx->output->ptr; ++ z->avail_out = hctx->output->size; ++ } ++ } while (z->avail_in > 0); ++ ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sdsd", ++ "compress: in=", in, ", out=", out); ++ } ++ return 0; ++} ++ ++static int stream_deflate_flush(server *srv, connection *con, handler_ctx *hctx, int end) { ++ plugin_data *p = hctx->plugin_data; ++ z_stream *z; ++ int len; ++ int rc = 0; ++ int done; ++ int flush = 1; ++ int in = 0, out = 0; ++ ++ UNUSED(srv); ++ ++ z = &(hctx->z); ++ ++ if(z->next_out == NULL) { ++ z->next_out = (unsigned char *)hctx->output->ptr; ++ z->avail_out = hctx->output->size; ++ } ++ /* compress data */ ++ in = z->avail_in; ++ do { ++ done = 1; ++ if(end) { ++ rc = deflate(z, Z_FINISH); ++ if (rc == Z_OK) { ++ done = 0; ++ } else if (rc != Z_STREAM_END) { ++ deflateEnd(z); ++ hctx->stream_open = 0; ++ return -1; ++ } ++ } else { ++ if(p->conf.sync_flush) { ++ rc = deflate(z, Z_SYNC_FLUSH); ++ } else if(z->avail_in > 0) { ++ if(p->conf.output_buffer_size > 0) flush = 0; ++ rc = deflate(z, Z_NO_FLUSH); ++ } else { ++ if(p->conf.output_buffer_size > 0) flush = 0; ++ rc = Z_OK; ++ } ++ if (rc != Z_OK) { ++ deflateEnd(z); ++ hctx->stream_open = 0; ++ return -1; ++ } ++ } ++ ++ len = hctx->output->size - z->avail_out; ++ if(z->avail_out == 0 || (flush && len > 0)) { ++ hctx->bytes_out += len; ++ out += len; ++ chunkqueue_append_mem(con->write_queue, hctx->output->ptr, len+1); ++ z->next_out = (unsigned char *)hctx->output->ptr; ++ z->avail_out = hctx->output->size; ++ } ++ } while (z->avail_in != 0 || !done); ++ ++ ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sdsd", ++ "flush: in=", in, ", out=", out); ++ } ++ if(p->conf.sync_flush) { ++ z->next_out = NULL; ++ z->avail_out = 0; ++ } ++ return 0; ++} ++ ++static int stream_deflate_end(server *srv, connection *con, handler_ctx *hctx) { ++ plugin_data *p = hctx->plugin_data; ++ z_stream *z; ++ int rc; ++ ++ UNUSED(srv); ++ ++ z = &(hctx->z); ++ if(!hctx->stream_open) return 0; ++ hctx->stream_open = 0; ++ ++ if(hctx->compression_type == HTTP_ACCEPT_ENCODING_GZIP && hctx->bytes_out > 0 && ++ (unsigned int )hctx->bytes_out >= sizeof(gzip_header)) { ++ /* write gzip footer */ ++ unsigned char c[8]; ++ ++ c[0] = (hctx->crc >> 0) & 0xff; ++ c[1] = (hctx->crc >> 8) & 0xff; ++ c[2] = (hctx->crc >> 16) & 0xff; ++ c[3] = (hctx->crc >> 24) & 0xff; ++ c[4] = (z->total_in >> 0) & 0xff; ++ c[5] = (z->total_in >> 8) & 0xff; ++ c[6] = (z->total_in >> 16) & 0xff; ++ c[7] = (z->total_in >> 24) & 0xff; ++ /* append footer to write_queue */ ++ chunkqueue_append_mem(con->write_queue, (char *)c, 9); ++ hctx->bytes_out += 8; ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "gzip_footer len=", 8); ++ } ++ } ++ ++ if ((rc = deflateEnd(z)) != Z_OK) { ++ if(rc == Z_DATA_ERROR) return 0; ++ if(z->msg != NULL) { ++ log_error_write(srv, __FILE__, __LINE__, "sdss", ++ "deflateEnd error ret=", rc, ", msg=", z->msg); ++ } else { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "deflateEnd error ret=", rc); ++ } ++ return -1; ++ } ++ return 0; ++} ++ ++#endif ++ ++#ifdef USE_BZ2LIB ++static int stream_bzip2_init(server *srv, connection *con, handler_ctx *hctx) { ++ plugin_data *p = hctx->plugin_data; ++ bz_stream *bz; ++ ++ UNUSED(srv); ++ UNUSED(con); ++ ++ bz = &(hctx->bz); ++ bz->bzalloc = NULL; ++ bz->bzfree = NULL; ++ bz->opaque = NULL; ++ bz->total_in_lo32 = 0; ++ bz->total_in_hi32 = 0; ++ bz->total_out_lo32 = 0; ++ bz->total_out_hi32 = 0; ++ ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "output-buffer-size:", p->conf.output_buffer_size); ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "compression-level:", p->conf.compression_level); ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "mem-level:", p->conf.mem_level); ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "window-size:", p->conf.window_size); ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "min-compress-size:", p->conf.min_compress_size); ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "work-block-size:", p->conf.work_block_size); ++ } ++ if (BZ_OK != BZ2_bzCompressInit(bz, ++ p->conf.compression_level, /* blocksize = 900k */ ++ 0, /* no output */ ++ 30)) { /* workFactor: default */ ++ return -1; ++ } ++ hctx->stream_open = 1; ++ ++ return 0; ++} ++ ++static int stream_bzip2_compress(server *srv, connection *con, handler_ctx *hctx, unsigned char *start, off_t st_size) { ++ plugin_data *p = hctx->plugin_data; ++ bz_stream *bz; ++ int len; ++ int rc; ++ int in = 0, out = 0; ++ ++ UNUSED(srv); ++ ++ bz = &(hctx->bz); ++ ++ if(bz->next_out == NULL) { ++ bz->next_out = hctx->output->ptr; ++ bz->avail_out = hctx->output->size; ++ } ++ ++ bz->next_in = (char *)start; ++ bz->avail_in = st_size; ++ hctx->bytes_in += st_size; ++ ++ /* compress data */ ++ in = bz->avail_in; ++ do { ++ rc = BZ2_bzCompress(bz, BZ_RUN); ++ if (rc != BZ_RUN_OK) { ++ BZ2_bzCompressEnd(bz); ++ hctx->stream_open = 0; ++ return -1; ++ } ++ ++ if(bz->avail_out == 0 || bz->avail_in > 0) { ++ len = hctx->output->size - bz->avail_out; ++ hctx->bytes_out += len; ++ out += len; ++ chunkqueue_append_mem(con->write_queue, hctx->output->ptr, len+1); ++ bz->next_out = hctx->output->ptr; ++ bz->avail_out = hctx->output->size; ++ } ++ } while (bz->avail_in > 0); ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sdsd", ++ "compress: in=", in, ", out=", out); ++ } ++ return 0; ++} ++ ++static int stream_bzip2_flush(server *srv, connection *con, handler_ctx *hctx, int end) { ++ plugin_data *p = hctx->plugin_data; ++ bz_stream *bz; ++ int len; ++ int rc; ++ int done; ++ int flush = 1; ++ int in = 0, out = 0; ++ ++ UNUSED(srv); ++ ++ bz = &(hctx->bz); ++ ++ if(bz->next_out == NULL) { ++ bz->next_out = hctx->output->ptr; ++ bz->avail_out = hctx->output->size; ++ } ++ /* compress data */ ++ in = bz->avail_in; ++ do { ++ done = 1; ++ if(end) { ++ rc = BZ2_bzCompress(bz, BZ_FINISH); ++ if (rc == BZ_FINISH_OK) { ++ done = 0; ++ } else if (rc != BZ_STREAM_END) { ++ BZ2_bzCompressEnd(bz); ++ hctx->stream_open = 0; ++ return -1; ++ } ++ } else if(bz->avail_in > 0) { ++ rc = BZ2_bzCompress(bz, BZ_RUN); ++ if (rc != BZ_RUN_OK) { ++ BZ2_bzCompressEnd(bz); ++ hctx->stream_open = 0; ++ return -1; ++ } ++ if(p->conf.output_buffer_size > 0) flush = 0; ++ } ++ ++ len = hctx->output->size - bz->avail_out; ++ if(bz->avail_out == 0 || (flush && len > 0)) { ++ hctx->bytes_out += len; ++ out += len; ++ chunkqueue_append_mem(con->write_queue, hctx->output->ptr, len+1); ++ bz->next_out = hctx->output->ptr; ++ bz->avail_out = hctx->output->size; ++ } ++ } while (bz->avail_in != 0 || !done); ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sdsd", ++ "flush: in=", in, ", out=", out); ++ } ++ if(p->conf.sync_flush) { ++ bz->next_out = NULL; ++ bz->avail_out = 0; ++ } ++ return 0; ++} ++ ++static int stream_bzip2_end(server *srv, connection *con, handler_ctx *hctx) { ++ plugin_data *p = hctx->plugin_data; ++ bz_stream *bz; ++ int rc; ++ ++ UNUSED(p); ++ UNUSED(con); ++ ++ bz = &(hctx->bz); ++ if(!hctx->stream_open) return 0; ++ hctx->stream_open = 0; ++ ++ if ((rc = BZ2_bzCompressEnd(bz)) != BZ_OK) { ++ if(rc == BZ_DATA_ERROR) return 0; ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "BZ2_bzCompressEnd error ret=", rc); ++ return -1; ++ } ++ return 0; ++} ++ ++#endif ++ ++static int mod_deflate_compress(server *srv, connection *con, handler_ctx *hctx, unsigned char *start, off_t st_size) { ++ int ret = -1; ++ if(st_size == 0) return 0; ++ switch(hctx->compression_type) { ++#ifdef USE_ZLIB ++ case HTTP_ACCEPT_ENCODING_GZIP: ++ case HTTP_ACCEPT_ENCODING_DEFLATE: ++ ret = stream_deflate_compress(srv, con, hctx, start, st_size); ++ break; ++#endif ++#ifdef USE_BZ2LIB ++ case HTTP_ACCEPT_ENCODING_BZIP2: ++ ret = stream_bzip2_compress(srv, con, hctx, start, st_size); ++ break; ++#endif ++ default: ++ ret = -1; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int mod_deflate_stream_flush(server *srv, connection *con, handler_ctx *hctx, int end) { ++ int ret = -1; ++ if(hctx->bytes_in == 0) return 0; ++ switch(hctx->compression_type) { ++#ifdef USE_ZLIB ++ case HTTP_ACCEPT_ENCODING_GZIP: ++ case HTTP_ACCEPT_ENCODING_DEFLATE: ++ ret = stream_deflate_flush(srv, con, hctx, end); ++ break; ++#endif ++#ifdef USE_BZ2LIB ++ case HTTP_ACCEPT_ENCODING_BZIP2: ++ ret = stream_bzip2_flush(srv, con, hctx, end); ++ break; ++#endif ++ default: ++ ret = -1; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int mod_deflate_stream_end(server *srv, connection *con, handler_ctx *hctx) { ++ int ret = -1; ++ switch(hctx->compression_type) { ++#ifdef USE_ZLIB ++ case HTTP_ACCEPT_ENCODING_GZIP: ++ case HTTP_ACCEPT_ENCODING_DEFLATE: ++ ret = stream_deflate_end(srv, con, hctx); ++ break; ++#endif ++#ifdef USE_BZ2LIB ++ case HTTP_ACCEPT_ENCODING_BZIP2: ++ ret = stream_bzip2_end(srv, con, hctx); ++ break; ++#endif ++ default: ++ ret = -1; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int mod_deflate_file_chunk(server *srv, connection *con, handler_ctx *hctx, chunk *c, off_t st_size) { ++ plugin_data *p = hctx->plugin_data; ++ off_t abs_offset; ++ off_t toSend; ++ stat_cache_entry *sce = NULL; ++ off_t we_want_to_mmap = 2 MByte; ++ off_t we_want_to_send = st_size; ++ char *start = NULL; ++ ++ if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { ++ log_error_write(srv, __FILE__, __LINE__, "sb", ++ strerror(errno), c->file.name); ++ return -1; ++ } ++ ++ abs_offset = c->file.start + c->offset; ++ ++ if (abs_offset > sce->st.st_size) { ++ log_error_write(srv, __FILE__, __LINE__, "sb", ++ "file was shrinked:", c->file.name); ++ ++ return -1; ++ } ++ ++ we_want_to_send = st_size; ++ /* mmap the buffer ++ * - first mmap ++ * - new mmap as the we are at the end of the last one */ ++ if (c->file.mmap.start == MAP_FAILED || ++ abs_offset == (off_t)(c->file.mmap.offset + c->file.mmap.length)) { ++ ++ /* Optimizations for the future: ++ * ++ * adaptive mem-mapping ++ * the problem: ++ * we mmap() the whole file. If someone has alot large files and 32bit ++ * machine the virtual address area will be unrun and we will have a failing ++ * mmap() call. ++ * solution: ++ * only mmap 16M in one chunk and move the window as soon as we have finished ++ * the first 8M ++ * ++ * read-ahead buffering ++ * the problem: ++ * sending out several large files in parallel trashes the read-ahead of the ++ * kernel leading to long wait-for-seek times. ++ * solutions: (increasing complexity) ++ * 1. use madvise ++ * 2. use a internal read-ahead buffer in the chunk-structure ++ * 3. use non-blocking IO for file-transfers ++ * */ ++ ++ /* all mmap()ed areas are 512kb expect the last which might be smaller */ ++ size_t to_mmap; ++ ++ /* this is a remap, move the mmap-offset */ ++ if (c->file.mmap.start != MAP_FAILED) { ++ munmap(c->file.mmap.start, c->file.mmap.length); ++ c->file.mmap.offset += we_want_to_mmap; ++ } else { ++ /* in case the range-offset is after the first mmap()ed area we skip the area */ ++ c->file.mmap.offset = 0; ++ ++ while (c->file.mmap.offset + we_want_to_mmap < c->file.start) { ++ c->file.mmap.offset += we_want_to_mmap; ++ } ++ } ++ ++ /* length is rel, c->offset too, assume there is no limit at the mmap-boundaries */ ++ to_mmap = (c->file.start + c->file.length) - c->file.mmap.offset; ++ if(to_mmap > we_want_to_mmap) to_mmap = we_want_to_mmap; ++ /* we have more to send than we can mmap() at once */ ++ if(we_want_to_send > to_mmap) we_want_to_send = to_mmap; ++ ++ if (-1 == c->file.fd) { /* open the file if not already open */ ++ if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { ++ log_error_write(srv, __FILE__, __LINE__, "sbs", "open failed for:", c->file.name, strerror(errno)); ++ ++ return -1; ++ } ++#ifdef FD_CLOEXEC ++ fcntl(c->file.fd, F_SETFD, FD_CLOEXEC); ++#endif ++ } ++ ++ if (MAP_FAILED == (c->file.mmap.start = mmap(0, to_mmap, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) { ++ /* close it here, otherwise we'd have to set FD_CLOEXEC */ ++ ++ log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed:", ++ strerror(errno), c->file.name, c->file.fd); ++ ++ return -1; ++ } ++ ++ c->file.mmap.length = to_mmap; ++#ifdef LOCAL_BUFFERING ++ buffer_copy_string_len(c->mem, c->file.mmap.start, c->file.mmap.length); ++#else ++#ifdef HAVE_MADVISE ++ /* don't advise files < 64Kb */ ++ if (c->file.mmap.length > (64 KByte) && ++ 0 != madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED)) { ++ log_error_write(srv, __FILE__, __LINE__, "ssbd", "madvise failed:", ++ strerror(errno), c->file.name, c->file.fd); ++ } ++#endif ++#endif ++ ++ /* chunk_reset() or chunk_free() will cleanup for us */ ++ } ++ ++ /* to_send = abs_mmap_end - abs_offset */ ++ toSend = (c->file.mmap.offset + c->file.mmap.length) - (abs_offset); ++ if(toSend > we_want_to_send) toSend = we_want_to_send; ++ ++ if (toSend < 0) { ++ log_error_write(srv, __FILE__, __LINE__, "soooo", ++ "toSend is negative:", ++ toSend, ++ c->file.mmap.length, ++ abs_offset, ++ c->file.mmap.offset); ++ assert(toSend < 0); ++ } ++ ++#ifdef LOCAL_BUFFERING ++ start = c->mem->ptr; ++#else ++ start = c->file.mmap.start; ++#endif ++ ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sdsd", ++ "compress file chunk: offset=", (int)c->offset, ++ ", toSend=", (int)toSend); ++ } ++ if (mod_deflate_compress(srv, con, hctx, ++ (unsigned char *)start + (abs_offset - c->file.mmap.offset), toSend) < 0) { ++ log_error_write(srv, __FILE__, __LINE__, "s", ++ "compress failed."); ++ return -1; ++ } ++ ++ c->offset += toSend; ++ if (c->offset == c->file.length) { ++ /* we don't need the mmaping anymore */ ++ if (c->file.mmap.start != MAP_FAILED) { ++ munmap(c->file.mmap.start, c->file.mmap.length); ++ c->file.mmap.start = MAP_FAILED; ++ } ++ } ++ ++ return toSend; ++} ++ ++static int deflate_compress_cleanup(server *srv, connection *con, handler_ctx *hctx) { ++ plugin_data *p = hctx->plugin_data; ++ int rc; ++ ++ rc = mod_deflate_stream_end(srv, con, hctx); ++ if(rc < 0) { ++ log_error_write(srv, __FILE__, __LINE__, "s", "error closing stream"); ++ } ++ ++ if (hctx->bytes_in < hctx->bytes_out) { ++ log_error_write(srv, __FILE__, __LINE__, "sbsdsd", ++ "uri ", con->uri.path_raw, " in=", hctx->bytes_in, " smaller than out=", hctx->bytes_out); ++ } ++ ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sdsd", ++ " in:", hctx->bytes_in, ++ " out:", hctx->bytes_out); ++ } ++ ++ /* cleanup compression state */ ++ if(hctx->output != p->tmp_buf) { ++ buffer_free(hctx->output); ++ } ++ handler_ctx_free(hctx); ++ con->plugin_ctx[p->id] = NULL; ++ ++ return 0; ++} ++ ++static handler_t deflate_compress_response(server *srv, connection *con, handler_ctx *hctx, int end) { ++ plugin_data *p = hctx->plugin_data; ++ chunk *c; ++ size_t chunks_written = 0; ++ int chunk_finished = 0; ++ int rc=-1; ++ int close_stream = 0, len = 0; ++ unsigned int out = 0, max = 0; ++ ++ /* move all chunk from write_queue into our in_queue */ ++ chunkqueue_append_chunkqueue(hctx->in_queue, con->write_queue); ++ ++ len = chunkqueue_length(hctx->in_queue); ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "compress: in_queue len=", len); ++ } ++ /* calculate max bytes to compress for this call. */ ++ if(!end) { ++ max = p->conf.work_block_size * 1024; ++ if(max == 0 || max > len) max = len; ++ } else { ++ max = len; ++ } ++ ++ /* Compress chunks from in_queue into chunks for write_queue */ ++ for(c = hctx->in_queue->first; c && out < max; c = c->next) { ++ chunk_finished = 0; ++ len = 0; ++ ++ switch(c->type) { ++ case MEM_CHUNK: ++ len = c->mem->used - 1; ++ if(len > (max - out)) len = max - out; ++ if (mod_deflate_compress(srv, con, hctx, (unsigned char *)c->mem->ptr, len) < 0) { ++ log_error_write(srv, __FILE__, __LINE__, "s", ++ "compress failed."); ++ return HANDLER_ERROR; ++ } ++ c->offset += len; ++ out += len; ++ if (c->offset == c->mem->used - 1) { ++ chunk_finished = 1; ++ chunks_written++; ++ } ++ break; ++ case FILE_CHUNK: ++ len = c->file.length - c->offset; ++ if(len > (max - out)) len = max - out; ++ if ((len = mod_deflate_file_chunk(srv, con, hctx, c, len)) < 0) { ++ log_error_write(srv, __FILE__, __LINE__, "s", ++ "compress file chunk failed."); ++ return HANDLER_ERROR; ++ } ++ out += len; ++ if (c->offset == c->file.length) { ++ chunk_finished = 1; ++ chunks_written++; ++ } ++ break; ++ default: ++ ++ log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); ++ ++ return HANDLER_ERROR; ++ } ++ if(!chunk_finished) break; ++ } ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "compressed bytes:", out); ++ } ++ hctx->in_queue->bytes_out += out; ++ ++ if(chunks_written > 0) { ++ chunkqueue_remove_finished_chunks(hctx->in_queue); ++ } ++ ++ close_stream = (con->file_finished && chunkqueue_is_empty(hctx->in_queue)); ++ rc = mod_deflate_stream_flush(srv, con, hctx, close_stream); ++ if(rc < 0) { ++ log_error_write(srv, __FILE__, __LINE__, "s", "flush error"); ++ } ++ if(close_stream || end) { ++ deflate_compress_cleanup(srv, con, hctx); ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sbsb", ++ "finished uri:", con->uri.path_raw, ", query:", con->uri.query); ++ } ++ return HANDLER_FINISHED; ++ } else { ++ if(!chunkqueue_is_empty(hctx->in_queue)) { ++ /* We have more data to compress. */ ++ joblist_append(srv, con); ++ } ++ return HANDLER_COMEBACK; ++ } ++} ++ ++#define PATCH(x) \ ++ p->conf.x = s->x; ++static int mod_deflate_patch_connection(server *srv, connection *con, plugin_data *p) { ++ size_t i, j; ++ plugin_config *s = p->config_storage[0]; ++ ++ PATCH(output_buffer_size); ++ PATCH(mimetypes); ++ PATCH(compression_level); ++ PATCH(mem_level); ++ PATCH(window_size); ++ PATCH(min_compress_size); ++ PATCH(work_block_size); ++ PATCH(enabled); ++ PATCH(debug); ++ PATCH(bzip2); ++ PATCH(sync_flush); ++#if defined(HAVE_PCRE_H) ++ PATCH(nocompress_regex); ++#endif ++ ++ /* skip the first, the global context */ ++ for (i = 1; i < srv->config_context->used; i++) { ++ data_config *dc = (data_config *)srv->config_context->data[i]; ++ s = p->config_storage[i]; ++ ++ /* condition didn't match */ ++ if (!config_check_cond(srv, con, dc)) continue; ++ ++ /* merge config */ ++ for (j = 0; j < dc->value->used; j++) { ++ data_unset *du = dc->value->data[j]; ++ ++ if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.output-buffer-size"))) { ++ PATCH(output_buffer_size); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.mimetypes"))) { ++ PATCH(mimetypes); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.compression-level"))) { ++ PATCH(compression_level); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.mem-level"))) { ++ PATCH(mem_level); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.window-size"))) { ++ PATCH(window_size); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.min-compress-size"))) { ++ PATCH(min_compress_size); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.work-block-size"))) { ++ PATCH(work_block_size); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.enabled"))) { ++ PATCH(enabled); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.debug"))) { ++ PATCH(debug); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.bzip2"))) { ++ PATCH(bzip2); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.sync-flush"))) { ++ PATCH(sync_flush); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.nocompress-url"))) { ++#if defined(HAVE_PCRE_H) ++ PATCH(nocompress_regex); ++#endif ++ } ++ } ++ } ++ ++ return 0; ++} ++#undef PATCH ++ ++PHYSICALPATH_FUNC(mod_deflate_handle_response_start) { ++ plugin_data *p = p_d; ++ handler_ctx *hctx; ++ data_string *ds; ++ int accept_encoding = 0; ++ char *value; ++ int srv_encodings = 0; ++ int matched_encodings = 0; ++ const char *dflt_gzip = "gzip"; ++ const char *dflt_deflate = "deflate"; ++ const char *dflt_bzip2 = "bzip2"; ++ const char *compression_name = NULL; ++ int file_len=0; ++ int rc=-2; ++ int end = 0; ++ size_t m; ++#if defined(HAVE_PCRE_H) ++ int n; ++# define N 10 ++ int ovec[N * 3]; ++#endif ++ ++ /* disable compression for some http status types. */ ++ switch(con->http_status) { ++ case 100: ++ case 101: ++ case 204: ++ case 205: ++ case 304: ++ /* disable compression as we have no response entity */ ++ return HANDLER_GO_ON; ++ default: ++ break; ++ } ++ ++ mod_deflate_patch_connection(srv, con, p); ++ ++ /* is compression allowed */ ++ if(!p->conf.enabled) { ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "s", "compression disabled."); ++ } ++ return HANDLER_GO_ON; ++ } ++ ++#if defined(HAVE_PCRE_H) ++ if(p->conf.nocompress_regex) { /*check no compress regex now */ ++ if ((n = pcre_exec(p->conf.nocompress_regex, NULL, con->uri.path->ptr, con->uri.path->used - 1, 0, 0, ovec, 3 * N)) < 0) { ++ if (n != PCRE_ERROR_NOMATCH) { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "execution error while matching:", n); ++ return HANDLER_ERROR; ++ } ++ } else { ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sb", "no compress for url:", con->uri.path); ++ } ++ return HANDLER_GO_ON; ++ } ++ } ++#endif ++ /* Check if response has a Content-Encoding. */ ++ if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "Content-Encoding"))) { ++ return HANDLER_GO_ON; ++ } ++ ++ /* Check Accept-Encoding for supported encoding. */ ++ if (NULL == (ds = (data_string *)array_get_element(con->request.headers, "Accept-Encoding"))) { ++ return HANDLER_GO_ON; ++ } ++ ++ /* get client side support encodings */ ++ value = ds->value->ptr; ++#ifdef USE_ZLIB ++ if (NULL != strstr(value, "gzip")) accept_encoding |= HTTP_ACCEPT_ENCODING_GZIP; ++ if (NULL != strstr(value, "deflate")) accept_encoding |= HTTP_ACCEPT_ENCODING_DEFLATE; ++#endif ++ /* if (NULL != strstr(value, "compress")) accept_encoding |= HTTP_ACCEPT_ENCODING_COMPRESS; */ ++#ifdef USE_BZ2LIB ++ if(p->conf.bzip2) { ++ if (NULL != strstr(value, "bzip2")) accept_encoding |= HTTP_ACCEPT_ENCODING_BZIP2; ++ } ++#endif ++ if (NULL != strstr(value, "identity")) accept_encoding |= HTTP_ACCEPT_ENCODING_IDENTITY; ++ ++ /* get server side supported ones */ ++#ifdef USE_BZ2LIB ++ if(p->conf.bzip2) { ++ srv_encodings |= HTTP_ACCEPT_ENCODING_BZIP2; ++ } ++#endif ++#ifdef USE_ZLIB ++ srv_encodings |= HTTP_ACCEPT_ENCODING_GZIP; ++ srv_encodings |= HTTP_ACCEPT_ENCODING_DEFLATE; ++#endif ++ ++ /* find matching encodings */ ++ matched_encodings = accept_encoding & srv_encodings; ++ if (!matched_encodings) { ++ return HANDLER_GO_ON; ++ } ++ ++ if (con->request.true_http_10_client) { ++ /*disable gzip/bzip2 when we meet HTTP 1.0 client with Accept-Encoding ++ * maybe old buggy proxy server ++ */ ++ /* most of buggy clients are Yahoo Slurp;) */ ++ if (p->conf.debug) log_error_write(srv, __FILE__, __LINE__, "ss", ++ "Buggy HTTP 1.0 client sending Accept Encoding: gzip, deflate", ++ (char *) inet_ntop_cache_get_ip(srv, &(con->dst_addr))); ++ return HANDLER_GO_ON; ++ } ++ /* check if size of response is below min-compress-size */ ++ if(con->file_finished && con->request.http_method != HTTP_METHOD_HEAD) { ++ file_len = chunkqueue_length(con->write_queue); ++ if(file_len == 0) return HANDLER_GO_ON; ++ } else { ++ file_len = 0; ++ } ++ ++ if(file_len > 0 && p->conf.min_compress_size > 0 && file_len < p->conf.min_compress_size) { ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "Content-Length smaller then min_compress_size: file_len=", file_len); ++ } ++ return HANDLER_GO_ON; ++ } ++ ++ /* Check mimetype in response header "Content-Type" */ ++ if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "Content-Type"))) { ++ int found = 0; ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sb", ++ "Content-Type:", ds->value); ++ } ++ for (m = 0; m < p->conf.mimetypes->used; m++) { ++ data_string *mimetype = (data_string *)p->conf.mimetypes->data[m]; ++ ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sb", ++ "mime-type:", mimetype->value); ++ } ++ if (strncmp(mimetype->value->ptr, ds->value->ptr, mimetype->value->used-1) == 0) { ++/* if (buffer_is_equal(mimetype->value, ds->value)) { */ ++ /* mimetype found */ ++ found = 1; ++ break; ++ } ++ } ++ if(!found && p->conf.mimetypes->used > 0) { ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sb", ++ "No compression for mimetype:", ds->value); ++ } ++ return HANDLER_GO_ON; ++ } ++#if 0 ++ if(strncasecmp(ds->value->ptr, "application/x-javascript", 24) == 0) { ++ /*reset compress type to deflate for javascript ++ * prevent buggy IE6 SP1 doesn't work for js in IFrame ++ */ ++ matched_encodings = HTTP_ACCEPT_ENCODING_DEFLATE; ++ } ++#endif ++ } ++ ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sb", ++ "enable compression for ", con->uri.path); ++ } ++ ++ /* the response might change according to Accept-Encoding */ ++ if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "Vary"))) { ++ /* append Accept-Encoding to Vary header */ ++ if (NULL == strstr(ds->value->ptr, "Accept-Encoding")) { ++ buffer_append_string(ds->value, ",Accept-Encoding"); ++ if (p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sb", ++ "appending ,Accept-Encoding for ", con->uri.path); ++ } ++ } ++ } else { ++ if (p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sb", ++ "add Vary: Accept-Encoding for ", con->uri.path); ++ } ++ response_header_insert(srv, con, CONST_STR_LEN("Vary"), ++ CONST_STR_LEN("Accept-Encoding")); ++ } ++ ++ /* enable compression */ ++ hctx = handler_ctx_init(); ++ hctx->plugin_data = p; ++ ++ /* select best matching encoding */ ++ if (matched_encodings & HTTP_ACCEPT_ENCODING_BZIP2) { ++ hctx->compression_type = HTTP_ACCEPT_ENCODING_BZIP2; ++ compression_name = dflt_bzip2; ++ rc = stream_bzip2_init(srv, con, hctx); ++ } else if (matched_encodings & HTTP_ACCEPT_ENCODING_GZIP) { ++ hctx->compression_type = HTTP_ACCEPT_ENCODING_GZIP; ++ compression_name = dflt_gzip; ++ rc = stream_deflate_init(srv, con, hctx); ++ } else if (matched_encodings & HTTP_ACCEPT_ENCODING_DEFLATE) { ++ hctx->compression_type = HTTP_ACCEPT_ENCODING_DEFLATE; ++ compression_name = dflt_deflate; ++ rc = stream_deflate_init(srv, con, hctx); ++ } ++ if(rc == -1) { ++ log_error_write(srv, __FILE__, __LINE__, "s", ++ "Failed to initialize compression."); ++ } ++ ++ if(rc < 0) { ++ handler_ctx_free(hctx); ++ return HANDLER_GO_ON; ++ } ++ ++ /* setup output buffer. */ ++ if(p->conf.sync_flush || p->conf.output_buffer_size == 0) { ++ buffer_prepare_copy(p->tmp_buf, 32 * 1024); ++ hctx->output = p->tmp_buf; ++ } else { ++ hctx->output = buffer_init(); ++ buffer_prepare_copy(hctx->output, p->conf.output_buffer_size); ++ } ++ con->plugin_ctx[p->id] = hctx; ++ ++ /* set Content-Encoding to show selected compression type. */ ++ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name)); ++ ++ if (con->file_finished) end = 1; ++ ++ con->parsed_response &= ~(HTTP_CONTENT_LENGTH); ++#if 0 ++ /* debug */ ++ if (con->parsed_response & HTTP_TRANSFER_ENCODING_CHUNKED) ++ log_error_write(srv, __FILE__, __LINE__, "s", ++ "deflate: response with chunked encoding"); ++ if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) ++ log_error_write(srv, __FILE__, __LINE__, "s", ++ "deflate: transfer encoding with chunked encoding"); ++#endif ++ ++ if (con->file_finished && (p->conf.work_block_size == 0 || file_len < (p->conf.work_block_size * 1024)) ++ && con->request.http_method != HTTP_METHOD_HEAD) { ++ /* disable chunk transfer */ ++ con->response.transfer_encoding = 0; ++ con->parsed_response &= ~(HTTP_TRANSFER_ENCODING_CHUNKED); ++ if(p->conf.debug) { ++ log_error_write(srv, __FILE__, __LINE__, "sd", ++ "Compress all content and use Content-Length header: uncompress len=", file_len); ++ } ++ return deflate_compress_response(srv, con, hctx, end); ++ } else { ++ if (con->request.http_version == HTTP_VERSION_1_1) { ++ if (p->conf.debug) ++ log_error_write(srv, __FILE__, __LINE__, "sb", ++ "chunk transfer encoding for uri", con->uri.path); ++ /* Make sure to use chunked encoding. */ ++ con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; ++ } else { ++ if (p->conf.debug) ++ log_error_write(srv, __FILE__, __LINE__, "sb", ++ "http 1.0 encoding for uri", con->uri.path); ++ /* We don't have to use chunked encoding because HTTP 1.0 don't support it. */ ++ con->response.transfer_encoding = 0; ++ con->parsed_response &= ~(HTTP_TRANSFER_ENCODING_CHUNKED); ++ } ++ } ++ ++ if (p->conf.debug) ++ log_error_write(srv, __FILE__, __LINE__, "sdsb", "end =", end, "for uri", con->uri.path); ++ ++ deflate_compress_response(srv, con, hctx, end); ++ return HANDLER_GO_ON; ++} ++ ++JOBLIST_FUNC(mod_deflate_handle_response_filter) { ++ plugin_data *p = p_d; ++ handler_ctx *hctx = con->plugin_ctx[p->id]; ++ ++ if(hctx == NULL) return HANDLER_GO_ON; ++ if(!hctx->stream_open) return HANDLER_GO_ON; ++ if(con->request.http_method == HTTP_METHOD_HEAD) return HANDLER_GO_ON; ++ ++ return deflate_compress_response(srv, con, hctx, 0); ++} ++ ++handler_t mod_deflate_cleanup(server *srv, connection *con, void *p_d) { ++ plugin_data *p = p_d; ++ handler_ctx *hctx = con->plugin_ctx[p->id]; ++ ++ if(hctx == NULL) return HANDLER_GO_ON; ++ ++ if(p->conf.debug && hctx->stream_open) { ++ log_error_write(srv, __FILE__, __LINE__, "sbsb", ++ "stream open at cleanup. uri=", con->uri.path_raw, ", query=", con->uri.query); ++ } ++ ++ deflate_compress_cleanup(srv, con, hctx); ++ ++ return HANDLER_GO_ON; ++} ++ ++int mod_deflate_plugin_init(plugin *p) { ++ p->version = LIGHTTPD_VERSION_ID; ++ p->name = buffer_init_string("deflate"); ++ ++ p->init = mod_deflate_init; ++ p->cleanup = mod_deflate_free; ++ p->set_defaults = mod_deflate_setdefaults; ++ p->connection_reset = mod_deflate_cleanup; ++ p->handle_connection_close = mod_deflate_cleanup; ++ p->handle_response_start = mod_deflate_handle_response_start; ++ p->handle_response_filter = mod_deflate_handle_response_filter; ++ ++ p->data = NULL; ++ ++ return 0; ++} +diff -Naur lighttpd-1.4.19/src/plugin.c lighttpd-1.4.19.mod_deflate.jz/src/plugin.c +--- lighttpd-1.4.19/src/plugin.c ++++ lighttpd-1.4.19.mod_deflate.jz/src/plugin.c +@@ -40,6 +40,8 @@ + PLUGIN_FUNC_HANDLE_SIGHUP, + PLUGIN_FUNC_HANDLE_SUBREQUEST, + PLUGIN_FUNC_HANDLE_SUBREQUEST_START, ++ PLUGIN_FUNC_HANDLE_RESPONSE_START, ++ PLUGIN_FUNC_HANDLE_RESPONSE_FILTER, + PLUGIN_FUNC_HANDLE_JOBLIST, + PLUGIN_FUNC_HANDLE_DOCROOT, + PLUGIN_FUNC_HANDLE_PHYSICAL, +@@ -266,6 +268,8 @@ + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close) + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest) + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start) ++PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_START, handle_response_start) ++PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_FILTER, handle_response_filter) + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST, handle_joblist) + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot) + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical) +@@ -395,6 +399,8 @@ + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start); ++ PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_START, handle_response_start); ++ PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_FILTER, handle_response_filter); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST, handle_joblist); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical); +diff -Naur lighttpd-1.4.19/src/plugin.h lighttpd-1.4.19.mod_deflate.jz/src/plugin.h +--- lighttpd-1.4.19/src/plugin.h ++++ lighttpd-1.4.19.mod_deflate.jz/src/plugin.h +@@ -54,6 +54,8 @@ + * has to be found + */ + handler_t (* handle_subrequest) (server *srv, connection *con, void *p_d); /* */ ++ handler_t (* handle_response_start) (server *srv, connection *con, void *p_d); /* before response headers are written */ ++ handler_t (* handle_response_filter) (server *srv, connection *con, void *p_d); /* response content filter */ + handler_t (* connection_reset) (server *srv, connection *con, void *p_d); /* */ + void *data; + +@@ -68,6 +70,8 @@ + handler_t plugins_call_handle_uri_clean(server *srv, connection *con); + handler_t plugins_call_handle_subrequest_start(server *srv, connection *con); + handler_t plugins_call_handle_subrequest(server *srv, connection *con); ++handler_t plugins_call_handle_response_start(server *srv, connection *con); ++handler_t plugins_call_handle_response_filter(server *srv, connection *con); + handler_t plugins_call_handle_request_done(server *srv, connection *con); + handler_t plugins_call_handle_docroot(server *srv, connection *con); + handler_t plugins_call_handle_physical(server *srv, connection *con); +diff -Naur lighttpd-1.4.19/src/request.c lighttpd-1.4.19.mod_deflate.jz/src/request.c +--- lighttpd-1.4.19/src/request.c ++++ lighttpd-1.4.19.mod_deflate.jz/src/request.c +@@ -415,8 +415,10 @@ + + if (major_num == 1 && minor_num == 1) { + con->request.http_version = con->conf.allow_http11 ? HTTP_VERSION_1_1 : HTTP_VERSION_1_0; ++ con->request.true_http_10_client = 0; + } else if (major_num == 1 && minor_num == 0) { + con->request.http_version = HTTP_VERSION_1_0; ++ con->request.true_http_10_client = 1; + } else { + con->http_status = 505; + +diff -Naur lighttpd-1.4.19/src/response.c lighttpd-1.4.19.mod_deflate.jz/src/response.c +--- lighttpd-1.4.19/src/response.c ++++ lighttpd-1.4.19.mod_deflate.jz/src/response.c +@@ -32,7 +32,7 @@ + int have_date = 0; + int have_server = 0; + +- b = chunkqueue_get_prepend_buffer(con->write_queue); ++ b = chunkqueue_get_prepend_buffer(con->output_queue); + + if (con->request.http_version == HTTP_VERSION_1_1) { + BUFFER_COPY_STRING_CONST(b, "HTTP/1.1 "); +@@ -49,6 +49,8 @@ + } + + if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { ++/* if (!(con->parsed_response & HTTP_TRANSFER_ENCODING_CHUNKED) && ++ (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED)) { */ + BUFFER_APPEND_STRING_CONST(b, "\r\nTransfer-Encoding: chunked"); + } + +@@ -65,6 +67,16 @@ + if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Date"))) have_date = 1; + if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Server"))) have_server = 1; + ++ /* remove Transfer-Encoding: chunked header when HTTP 1.0 ++ * or transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED == 0 ++ */ ++ if ( (con->request.http_version == HTTP_VERSION_1_0 || ++ !(con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED)) && ++ 0 == strncasecmp(ds->value->ptr, "chunked", sizeof("chunked")-1) && ++ 0 == strncasecmp(ds->key->ptr, "Transfer-Encoding", sizeof("Transfer-Encoding") - 1)) { ++ continue ; ++ } ++ + BUFFER_APPEND_STRING_CONST(b, "\r\n"); + buffer_append_string_buffer(b, ds->key); + BUFFER_APPEND_STRING_CONST(b, ": "); +@@ -576,7 +588,7 @@ + } + + if (slash) pathinfo = slash; +- } while ((found == 0) && (slash != NULL) && ((size_t)(slash - srv->tmp_buf->ptr) > (con->physical.basedir->used - 2))); ++ } while ((found == 0) && (slash != NULL) && ((size_t)(slash - srv->tmp_buf->ptr) > con->physical.basedir->used - 2)); + + if (found == 0) { + /* no it really doesn't exists */ +diff -Naur lighttpd-1.4.19/src/server.c lighttpd-1.4.19.mod_deflate.jz/src/server.c +--- lighttpd-1.4.19/src/server.c ++++ lighttpd-1.4.19.mod_deflate.jz/src/server.c +@@ -203,6 +203,9 @@ + srv->joblist = calloc(1, sizeof(*srv->joblist)); + assert(srv->joblist); + ++ srv->joblist_prev = calloc(1, sizeof(*srv->joblist)); ++ assert(srv->joblist_prev); ++ + srv->fdwaitqueue = calloc(1, sizeof(*srv->fdwaitqueue)); + assert(srv->fdwaitqueue); + +@@ -294,6 +297,7 @@ + #undef CLEAN + + joblist_free(srv, srv->joblist); ++ joblist_free(srv, srv->joblist_prev); + fdwaitqueue_free(srv, srv->fdwaitqueue); + + if (srv->stat_cache) { +@@ -1137,6 +1141,7 @@ + /* main-loop */ + while (!srv_shutdown) { + int n; ++ int timeout; + size_t ndx; + time_t min_ts; + +@@ -1388,7 +1393,12 @@ + } + } + +- if ((n = fdevent_poll(srv->ev, 1000)) > 0) { ++ if(srv->joblist->used > 0) { ++ timeout = 500; ++ } else { ++ timeout = 1000; ++ } ++ if ((n = fdevent_poll(srv->ev, timeout)) > 0) { + /* n is the number of events */ + int revents; + int fd_ndx; +@@ -1436,10 +1446,16 @@ + strerror(errno)); + } + +- for (ndx = 0; ndx < srv->joblist->used; ndx++) { +- connection *con = srv->joblist->ptr[ndx]; ++ if(srv->joblist->used > 0) { ++ connections *joblist = srv->joblist; ++ /* switch joblist queues. */ ++ srv->joblist = srv->joblist_prev; ++ srv->joblist_prev = joblist; ++ for (ndx = 0; ndx < joblist->used; ndx++) { ++ connection *con = joblist->ptr[ndx]; + handler_t r; + ++ con->in_joblist = 0; + connection_state_machine(srv, con); + + switch(r = plugins_call_handle_joblist(srv, con)) { +@@ -1450,11 +1466,9 @@ + log_error_write(srv, __FILE__, __LINE__, "d", r); + break; + } +- +- con->in_joblist = 0; + } +- +- srv->joblist->used = 0; ++ joblist->used = 0; ++ } + } + + if (srv->srvconf.pid_file->used && diff --git a/lighttpd-mod_dirlisting.conf b/lighttpd-mod_dirlisting.conf new file mode 100644 index 0000000..ba9cc52 --- /dev/null +++ b/lighttpd-mod_dirlisting.conf @@ -0,0 +1,9 @@ +# Virtual directory listings. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModDirlisting + +# NOTE: this module is automatically loaded + +#dir-listing.encoding = "utf-8" +#dir-listing.activate = "enable" +dir-listing.hide-dotfiles = "enable" diff --git a/lighttpd-mod_evasive-status_code.patch b/lighttpd-mod_evasive-status_code.patch new file mode 100644 index 0000000..02cbd9e --- /dev/null +++ b/lighttpd-mod_evasive-status_code.patch @@ -0,0 +1,97 @@ +--- lighttpd-1.4.19/src/mod_evasive.c 2008-09-19 17:50:24.307245276 +0300 ++++ lighttpd-1.4.22/src/mod_evasive.c 2009-05-12 02:21:58.524447939 +0300 +@@ -28,11 +28,15 @@ + typedef struct { + unsigned short max_conns; + unsigned short silent; ++ unsigned short http_status_code; ++ unsigned int retry_after; + } plugin_config; + + typedef struct { + PLUGIN_DATA; + ++ buffer *evasive_rftmp; ++ + plugin_config **config_storage; + + plugin_config conf; +@@ -46,6 +50,10 @@ + + p = calloc(1, sizeof(*p)); + ++ p->evasive_rftmp = buffer_init(); ++ ++ buffer_prepare_copy(p->evasive_rftmp, 255); ++ + return p; + } + +@@ -56,6 +64,8 @@ + + if (!p) return HANDLER_GO_ON; + ++ buffer_free(p->evasive_rftmp); ++ + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { +@@ -85,6 +85,8 @@ + config_values_t cv[] = { + { "evasive.max-conns-per-ip", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "evasive.silent", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ ++ { "evasive.http-status-code", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ ++ { "evasive.retry-after", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + +@@ -94,11 +96,15 @@ + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); +- s->max_conns = 0; +- s->silent = 0; ++ s->max_conns = 0; ++ s->silent = 0; ++ s->http_status_code = 503; ++ s->retry_after = 0; + + cv[0].destination = &(s->max_conns); + cv[1].destination = &(s->silent); ++ cv[2].destination = &(s->http_status_code); ++ cv[3].destination = &(s->retry_after); + + p->config_storage[i] = s; + +@@ -107,6 +123,8 @@ + + PATCH(max_conns); + PATCH(silent); ++ PATCH(http_status_code); ++ PATCH(retry_after); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { +@@ -124,6 +142,10 @@ + PATCH(max_conns); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.silent"))) { + PATCH(silent); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.http-status-code"))) { ++ PATCH(http_status_code); ++ } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.retry-after"))) { ++ PATCH(retry_after); + } + } + } +@@ -192,8 +214,10 @@ + inet_ntop_cache_get_ip(srv, &(con->dst_addr)), + "turned away. Too many connections."); + +- con->http_status = 403; + con->mode = DIRECT; ++ con->http_status = p->conf.http_status_code; ++ buffer_copy_long(p->evasive_rftmp, p->conf.retry_after); ++ response_header_overwrite(srv, con, CONST_STR_LEN("Retry-After"), CONST_BUF_LEN(p->evasive_rftmp)); + return HANDLER_FINISHED; + } + } diff --git a/lighttpd-mod_evasive.conf b/lighttpd-mod_evasive.conf new file mode 100644 index 0000000..8ecb17f --- /dev/null +++ b/lighttpd-mod_evasive.conf @@ -0,0 +1,12 @@ +# Evasive module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModEvasive + +server.modules += ( + "mod_evasive" +) + +## +## Limits number of connections per IP +## +#evasive.max-conns-per-ip = 5 diff --git a/lighttpd-mod_evhost.conf b/lighttpd-mod_evhost.conf new file mode 100644 index 0000000..744068a --- /dev/null +++ b/lighttpd-mod_evhost.conf @@ -0,0 +1,17 @@ +# Enhanced virtual-hosting module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModEVhost + +server.modules += ( + "mod_evhost" +) + +# define a pattern for the host url finding +# %% => % sign +# %0 => domain name + tld +# %1 => tld +# %2 => domain name without tld +# %3 => subdomain 1 name +# %4 => subdomain 2 name +# +#evhost.path-pattern = "/home/storage/dev/www/%3/htdocs/" diff --git a/lighttpd-mod_expire.conf b/lighttpd-mod_expire.conf new file mode 100644 index 0000000..d8f6daa --- /dev/null +++ b/lighttpd-mod_expire.conf @@ -0,0 +1,12 @@ +# Expires module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModExpire + +server.modules += ( + "mod_expire" +) + +#expire.url = ( +# "/buggy/" => "access 2 hours", +# "/asdhas/" => "access plus 1 seconds 2 minutes", +#) diff --git a/lighttpd-mod_extforward.conf b/lighttpd-mod_extforward.conf new file mode 100644 index 0000000..5ad8a90 --- /dev/null +++ b/lighttpd-mod_extforward.conf @@ -0,0 +1,42 @@ +# mod_extforward for lighttpd +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModExtForward + +server.modules += ( + "mod_extforward" +) + +# Trust proxy 10.0.0.232 and 10.0.0.232 +#extforward.forwarder = ( +# "10.0.0.232" => "trust", +# "10.0.0.233" => "trust", +#) + +# Trust all proxies (NOT RECOMMENDED!) +#extforward.forwarder = ( "all" => "trust") + +# Note that "all" has precedence over specific entries, +# so "all except" setups will not work. + +# Note: The effect of this module is variable on $HTTP["remotip"] directives and +# other module's remote ip dependent actions. +# Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP. +# Things done in between these two moments will match on the real client's IP. +# The moment things are done by a module depends on in which hook it does things and within the same hook +# on whether they are before/after us in the module loading order +# (order in the server.modules directive in the config file). +# +# Tested behaviours: +# +# mod_access: Will match on the real client. +# +# mod_accesslog: +# In order to see the "real" ip address in access log , +# you'll have to load mod_extforward after mod_accesslog. +# like this: +# +# server.modules = ( +# ..... +# mod_accesslog, +# mod_extforward +# ) diff --git a/lighttpd-mod_fastcgi.conf b/lighttpd-mod_fastcgi.conf new file mode 100644 index 0000000..fabe29a --- /dev/null +++ b/lighttpd-mod_fastcgi.conf @@ -0,0 +1,17 @@ +# FastCGI programs have the same functionality as CGI programs, +# but are considerably faster through lower interpreter startup +# time and socketed communication +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFastCGI + +server.modules += ( + "mod_fastcgi" +) + +#### fastcgi module +## read fastcgi.txt for more info +#fastcgi.debug = 1 + +#static-file.exclude-extensions = ( +# ".fcgi", +#) diff --git a/lighttpd-mod_flv_streaming.conf b/lighttpd-mod_flv_streaming.conf new file mode 100644 index 0000000..898654c --- /dev/null +++ b/lighttpd-mod_flv_streaming.conf @@ -0,0 +1,7 @@ +# FLV streaming module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFLVStreaming + +server.modules += ( + "mod_flv_streaming" +) diff --git a/lighttpd-mod_h264_streaming.conf b/lighttpd-mod_h264_streaming.conf new file mode 100644 index 0000000..1e137ca --- /dev/null +++ b/lighttpd-mod_h264_streaming.conf @@ -0,0 +1,11 @@ +# h264 streaming module. +# +# Documentation: http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-Lighttpd-Version2 + +server.modules += ( + "mod_h264_streaming" +) + +h264-streaming.extensions = ( + ".mp4", +) diff --git a/lighttpd-mod_h264_streaming.patch b/lighttpd-mod_h264_streaming.patch new file mode 100644 index 0000000..e9430b6 --- /dev/null +++ b/lighttpd-mod_h264_streaming.patch @@ -0,0 +1,3440 @@ +--- lighttpd-1.4.18/src/Makefile.am 2007-09-03 01:23:53.000000000 +0300 ++++ lighttpd-mod_h264_streaming-1.4.18/src/Makefile.am 2007-10-23 23:42:37.736979478 +0300 +@@ -77,6 +77,11 @@ + mod_flv_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined + mod_flv_streaming_la_LIBADD = $(common_libadd) + ++lib_LTLIBRARIES += mod_h264_streaming.la ++mod_h264_streaming_la_SOURCES = mod_h264_streaming.c moov.c ++mod_h264_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined ++mod_h264_streaming_la_LIBADD = $(common_libadd) ++ + lib_LTLIBRARIES += mod_evasive.la + mod_evasive_la_SOURCES = mod_evasive.c + mod_evasive_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined +--- /dev/null 2008-11-04 20:33:38.146691408 +0200 ++++ lighttpd-1.4.18/src/mod_h264_streaming.c 2009-01-26 20:59:51.385271731 +0200 +@@ -0,0 +1,337 @@ ++/******************************************************************************* ++ mod_h264_streaming.c ++ ++ mod_h264_streaming - A lighttpd plugin for pseudo-streaming Quicktime/MPEG4 files. ++ http://h264.code-shop.com ++ ++ Copyright (C) 2007-2009 CodeShop B.V. ++******************************************************************************/ ++ ++#include ++#include ++#include ++#include ++ ++#include "base.h" ++#include "log.h" ++#include "buffer.h" ++#include "response.h" ++#include "http_chunk.h" ++#include "stat_cache.h" ++ ++#include "plugin.h" ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include "moov.h" ++ ++/* plugin config for all request/connections */ ++ ++typedef struct { ++ array *extensions; ++} plugin_config; ++ ++typedef struct { ++ PLUGIN_DATA; ++ ++ buffer *query_str; ++ array *get_params; ++ ++ plugin_config **config_storage; ++ ++ plugin_config conf; ++} plugin_data; ++ ++/* init the plugin data */ ++INIT_FUNC(mod_h264_streaming_init) { ++ plugin_data *p; ++ ++ p = calloc(1, sizeof(*p)); ++ ++ p->query_str = buffer_init(); ++ p->get_params = array_init(); ++ ++ return p; ++} ++ ++/* detroy the plugin data */ ++FREE_FUNC(mod_h264_streaming_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 (!s) continue; ++ ++ array_free(s->extensions); ++ ++ free(s); ++ } ++ free(p->config_storage); ++ } ++ ++ buffer_free(p->query_str); ++ array_free(p->get_params); ++ ++ free(p); ++ ++ return HANDLER_GO_ON; ++} ++ ++/* handle plugin config and check values */ ++ ++SETDEFAULTS_FUNC(mod_h264_streaming_set_defaults) { ++ plugin_data *p = p_d; ++ size_t i = 0; ++ ++ config_values_t cv[] = { ++ { "h264-streaming.extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ ++ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } ++ }; ++ ++ if (!p) return HANDLER_ERROR; ++ ++ p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); ++ ++ for (i = 0; i < srv->config_context->used; i++) { ++ plugin_config *s; ++ ++ s = calloc(1, sizeof(plugin_config)); ++ s->extensions = array_init(); ++ ++ cv[0].destination = s->extensions; ++ ++ p->config_storage[i] = s; ++ ++ if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { ++ return HANDLER_ERROR; ++ } ++ } ++ ++ return HANDLER_GO_ON; ++} ++ ++#define PATCH(x) \ ++ p->conf.x = s->x; ++static int mod_h264_streaming_patch_connection(server *srv, connection *con, plugin_data *p) { ++ size_t i, j; ++ plugin_config *s = p->config_storage[0]; ++ ++ PATCH(extensions); ++ ++ /* skip the first, the global context */ ++ for (i = 1; i < srv->config_context->used; i++) { ++ data_config *dc = (data_config *)srv->config_context->data[i]; ++ s = p->config_storage[i]; ++ ++ /* condition didn't match */ ++ if (!config_check_cond(srv, con, dc)) continue; ++ ++ /* merge config */ ++ for (j = 0; j < dc->value->used; j++) { ++ data_unset *du = dc->value->data[j]; ++ ++ if (buffer_is_equal_string(du->key, CONST_STR_LEN("h264-streaming.extensions"))) { ++ PATCH(extensions); ++ } ++ } ++ } ++ ++ return 0; ++} ++#undef PATCH ++ ++static int split_get_params(array *get_params, buffer *qrystr) { ++ size_t is_key = 1; ++ size_t i; ++ char *key = NULL, *val = NULL; ++ ++ key = qrystr->ptr; ++ ++ /* we need the \0 */ ++ for (i = 0; i < qrystr->used; i++) { ++ switch(qrystr->ptr[i]) { ++ case '=': ++ if (is_key) { ++ val = qrystr->ptr + i + 1; ++ ++ qrystr->ptr[i] = '\0'; ++ ++ is_key = 0; ++ } ++ ++ break; ++ case '&': ++ case '\0': /* fin symbol */ ++ if (!is_key) { ++ data_string *ds; ++ /* we need at least a = since the last & */ ++ ++ /* terminate the value */ ++ qrystr->ptr[i] = '\0'; ++ ++ if (NULL == (ds = (data_string *)array_get_unused_element(get_params, TYPE_STRING))) { ++ ds = data_string_init(); ++ } ++ buffer_copy_string_len(ds->key, key, strlen(key)); ++ buffer_copy_string_len(ds->value, val, strlen(val)); ++ ++ array_insert_unique(get_params, (data_unset *)ds); ++ } ++ ++ key = qrystr->ptr + i + 1; ++ val = NULL; ++ is_key = 1; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++URIHANDLER_FUNC(mod_h264_streaming_path_handler) { ++ plugin_data *p = p_d; ++ int s_len; ++ size_t k; ++ ++ UNUSED(srv); ++ ++ if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; ++ ++ mod_h264_streaming_patch_connection(srv, con, p); ++ ++ s_len = con->physical.path->used - 1; ++ ++ for (k = 0; k < p->conf.extensions->used; k++) { ++ data_string *ds = (data_string *)p->conf.extensions->data[k]; ++ int ct_len = ds->value->used - 1; ++ ++ if (ct_len > s_len) continue; ++ if (ds->value->used == 0) continue; ++ ++ if (0 == strncmp(con->physical.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { ++ data_string *get_param; ++ stat_cache_entry *sce = NULL; ++ double start = 0.0; ++ double end = 0.0; ++ char *err = NULL; ++ int client_is_flash = 0; ++ ++ array_reset(p->get_params); ++ buffer_copy_string_buffer(p->query_str, con->uri.query); ++ split_get_params(p->get_params, p->query_str); ++ ++ /* if there is a start=[0-9]+ in the header use it as start, ++ * otherwise send the full file */ ++ if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "start"))) ++ { ++ /* check if it is a number */ ++ start = strtod(get_param->value->ptr, &err); ++ if (*err != '\0') { ++ return HANDLER_GO_ON; ++ } ++ if (start < 0) return HANDLER_GO_ON; ++ } ++ ++ /* if there is an end=[0-9]+ in the header use it as end */ ++ if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "end"))) ++ { ++ /* check if it is a number */ ++ end = strtod(get_param->value->ptr, &err); ++ if (*err != '\0') { ++ return HANDLER_GO_ON; ++ } ++ if (end < 0 || start >= end) return HANDLER_GO_ON; ++ } ++ ++ if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "client"))) ++ { ++ client_is_flash = starts_with(get_param->value->ptr, "FLASH"); ++ } ++ ++ /* get file info */ ++ if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { ++ return HANDLER_GO_ON; ++ } ++ ++ /* we are safe now, let's build a h264 header */ ++ { ++ { ++ unsigned int filesize = sce->st.st_size; ++ ++ void* mp4_header; ++ uint32_t mp4_header_size; ++ uint64_t mdat_offset; ++ uint64_t mdat_size; ++ ++ int result = mp4_split(con->physical.path->ptr, filesize, start, end, ++ &mp4_header, &mp4_header_size, ++ &mdat_offset, &mdat_size, client_is_flash); ++ ++ if(result) ++ { ++ buffer* b = chunkqueue_get_append_buffer(con->write_queue); ++ buffer_append_memory(b, mp4_header, mp4_header_size); ++ b->used++; /* add virtual \0 */ ++ ++ http_chunk_append_file(srv, con, con->physical.path, ++ mdat_offset, mdat_size); ++ } ++ ++ if(mp4_header) ++ { ++ free(mp4_header); ++ } ++ ++ if(!result) ++ { ++ return HANDLER_GO_ON; ++ } ++ } ++ } ++ ++ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/mp4")); ++ if(!client_is_flash) ++ { ++ response_header_overwrite(srv, con, CONST_STR_LEN("X-Mod-H264-Streaming"), CONST_STR_LEN("version=2.0")); ++ } ++ else ++ { ++ response_header_overwrite(srv, con, CONST_STR_LEN("X-Mod-H264-Streaming"), CONST_STR_LEN("version=2.0,client=flash")); ++ } ++ ++ con->file_finished = 1; ++ ++ return HANDLER_FINISHED; ++ } ++ } ++ ++ /* not found */ ++ return HANDLER_GO_ON; ++} ++ ++/* this function is called at dlopen() time and inits the callbacks */ ++ ++int mod_h264_streaming_plugin_init(plugin *p) { ++ p->version = LIGHTTPD_VERSION_ID; ++ p->name = buffer_init_string("h264_streaming"); ++ ++ p->init = mod_h264_streaming_init; ++ p->handle_physical = mod_h264_streaming_path_handler; ++ p->set_defaults = mod_h264_streaming_set_defaults; ++ p->cleanup = mod_h264_streaming_free; ++ ++ p->data = NULL; ++ ++ return 0; ++} ++ +--- /dev/null 2008-11-04 20:33:38.146691408 +0200 ++++ lighttpd-1.4.18/src/moov.c 2009-01-26 21:00:05.071936866 +0200 +@@ -0,0 +1,3031 @@ ++/******************************************************************************* ++ moov.c (version 2) ++ ++ moov - A library for splitting Quicktime/MPEG4 files. ++ http://h264.code-shop.com ++ ++ Copyright (C) 2007-2009 CodeShop B.V. ++ ++ Licensing ++ The H264 Streaming Module is licened under a Creative Common License. It allows ++ you to use, modify and redistribute the module, but only for *noncommercial* ++ purposes. For corporate use, please apply for a commercial license. ++ ++ Creative Commons License: ++ http://creativecommons.org/licenses/by-nc-sa/3.0/ ++ ++ Commercial License: ++ http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-License-Version2 ++******************************************************************************/ ++ ++#include "moov.h" ++ ++#ifdef _MSVC_VER ++#define _CRTDBG_MAP_ALLOC ++#include ++#include ++#endif ++ ++#ifdef UNUSED ++#elif defined(__GNUC__) ++# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) ++#elif defined(__LCLINT__) ++# define UNUSED(x) /*@unused@*/ x ++#else ++# define UNUSED(x) x ++#endif ++ ++/* ++ The QuickTime File Format PDF from Apple: ++ http://developer.apple.com/techpubs/quicktime/qtdevdocs/PDF/QTFileFormat.pdf ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#ifdef HAVE_STDINT_H ++# include ++#endif ++#ifdef HAVE_INTTYPES_H ++# include ++#endif ++#if defined HAVE_ZLIB_H && defined HAVE_LIBZ ++// Compress the MOOV atom. Turn this off for Flash as it doesn't support it. ++// # define COMPRESS_MOOV_ATOM ++# include ++#endif ++ ++#ifdef WIN32 ++#define ftello _ftelli64 ++#define fseeko _fseeki64 ++#endif ++ ++#define MAX_TRACKS 8 ++ ++#define FOURCC(a, b, c, d) ((uint32_t)(a) << 24) + \ ++ ((uint32_t)(b) << 16) + \ ++ ((uint32_t)(c) << 8) + \ ++ ((uint32_t)(d)) ++ ++/* Returns true when the test string is a prefix of the input */ ++int starts_with(const char* input, const char* test) ++{ ++ while(*input && *test) ++ { ++ if(*input != *test) ++ return 0; ++ ++input; ++ ++test; ++ } ++ ++ return *test == '\0'; ++} ++ ++static unsigned int read_8(unsigned char const* buffer) ++{ ++ return buffer[0]; ++} ++ ++static unsigned char* write_8(unsigned char* buffer, unsigned char v) ++{ ++ buffer[0] = v; ++ ++ return buffer + 1; ++} ++ ++static uint16_t read_16(unsigned char const* buffer) ++{ ++ return (buffer[0] << 8) | ++ (buffer[1] << 0); ++} ++ ++static unsigned char* write_16(unsigned char* buffer, unsigned int v) ++{ ++ buffer[0] = (unsigned char)(v >> 8); ++ buffer[1] = (unsigned char)(v >> 0); ++ ++ return buffer + 2; ++} ++ ++static unsigned int read_24(unsigned char const* buffer) ++{ ++ return (buffer[0] << 16) | ++ (buffer[1] << 8) | ++ (buffer[2] << 0); ++} ++ ++static unsigned char* write_24(unsigned char* buffer, unsigned int v) ++{ ++ buffer[0] = (unsigned char)(v >> 16); ++ buffer[1] = (unsigned char)(v >> 8); ++ buffer[2] = (unsigned char)(v >> 0); ++ ++ return buffer + 3; ++} ++ ++static uint32_t read_32(unsigned char const* buffer) ++{ ++ return (buffer[0] << 24) | ++ (buffer[1] << 16) | ++ (buffer[2] << 8) | ++ (buffer[3] << 0); ++} ++ ++static unsigned char* write_32(unsigned char* buffer, uint32_t v) ++{ ++ buffer[0] = (unsigned char)(v >> 24); ++ buffer[1] = (unsigned char)(v >> 16); ++ buffer[2] = (unsigned char)(v >> 8); ++ buffer[3] = (unsigned char)(v >> 0); ++ ++ return buffer + 4; ++} ++ ++static uint64_t read_64(unsigned char const* buffer) ++{ ++ return ((uint64_t)(read_32(buffer)) << 32) + read_32(buffer + 4); ++} ++ ++static unsigned char* write_64(unsigned char* buffer, uint64_t v) ++{ ++ write_32(buffer + 0, (uint32_t)(v >> 32)); ++ write_32(buffer + 4, (uint32_t)(v >> 0)); ++ ++ return buffer + 8; ++} ++ ++#define ATOM_PREAMBLE_SIZE 8 ++ ++struct atom_t ++{ ++ uint32_t type_; ++ uint32_t short_size_; ++ uint64_t size_; ++ unsigned char* start_; ++ unsigned char* end_; ++}; ++ ++static unsigned char* atom_read_header(unsigned char* buffer, struct atom_t* atom) ++{ ++ atom->start_ = buffer; ++ atom->short_size_ = read_32(buffer); ++ atom->type_ = read_32(buffer + 4); ++ ++ if(atom->short_size_ == 1) ++ atom->size_ = read_64(buffer + 8); ++ else ++ atom->size_ = atom->short_size_; ++ ++ atom->end_ = atom->start_ + atom->size_; ++ ++ return buffer + ATOM_PREAMBLE_SIZE + (atom->short_size_ == 1 ? 8 : 0); ++} ++ ++static void atom_print(struct atom_t const* atom) ++{ ++ printf("Atom(%c%c%c%c,%lld)\n", ++ atom->type_ >> 24, ++ atom->type_ >> 16, ++ atom->type_ >> 8, ++ atom->type_, ++ atom->size_); ++} ++ ++struct unknown_atom_t ++{ ++ void* atom_; ++ struct unknown_atom_t* next_; ++}; ++ ++static struct unknown_atom_t* unknown_atom_init() ++{ ++ struct unknown_atom_t* atom = (struct unknown_atom_t*)malloc(sizeof(struct unknown_atom_t)); ++ atom->atom_ = 0; ++ atom->next_ = 0; ++ ++ return atom; ++} ++ ++static void unknown_atom_exit(struct unknown_atom_t* atom) ++{ ++ while(atom) ++ { ++ struct unknown_atom_t* next = atom->next_; ++ free(atom->atom_); ++ free(atom); ++ atom = next; ++ } ++} ++ ++static struct unknown_atom_t* unknown_atom_add_atom(struct unknown_atom_t* parent, void* atom) ++{ ++ size_t size = read_32(atom); ++ struct unknown_atom_t* unknown = unknown_atom_init(); ++ unknown->atom_ = malloc(size); ++ memcpy(unknown->atom_, atom, size); ++ unknown->next_ = parent; ++ return unknown; ++} ++ ++struct atom_read_list_t ++{ ++ uint32_t type_; ++ void* parent_; ++ int (*destination_)(void* parent, void* child); ++ void* (*reader_)(void* parent, unsigned char* buffer, uint64_t size); ++}; ++ ++static int atom_reader(struct atom_read_list_t* atom_read_list, ++ unsigned int atom_read_list_size, ++ void* parent, ++ unsigned char* buffer, uint64_t size) ++{ ++ struct atom_t leaf_atom; ++ unsigned char* buffer_start = buffer; ++ ++ while(buffer < buffer_start + size) ++ { ++ unsigned int i; ++ buffer = atom_read_header(buffer, &leaf_atom); ++ ++ atom_print(&leaf_atom); ++ ++ for(i = 0; i != atom_read_list_size; ++i) ++ { ++ if(leaf_atom.type_ == atom_read_list[i].type_) ++ { ++ break; ++ } ++ } ++ ++ if(i == atom_read_list_size) ++ { ++ // add to unkown chunks ++ (*(struct unknown_atom_t**)parent) = ++ unknown_atom_add_atom(*(struct unknown_atom_t**)(parent), buffer - ATOM_PREAMBLE_SIZE); ++ } ++ else ++ { ++ void* child = ++ atom_read_list[i].reader_(parent, buffer, ++ leaf_atom.size_ - ATOM_PREAMBLE_SIZE); ++ if(!child) ++ break; ++ if(!atom_read_list[i].destination_(parent, child)) ++ break; ++ } ++ buffer = leaf_atom.end_; ++ } ++ ++ if(buffer < buffer_start + size) ++ { ++ return 0; ++ } ++ ++ return 1; ++} ++ ++struct atom_write_list_t ++{ ++ uint32_t type_; ++ void* parent_; ++ void* source_; ++ unsigned char* (*writer_)(void* parent, void* atom, unsigned char* buffer); ++}; ++ ++static unsigned char* atom_writer_unknown(struct unknown_atom_t* atoms, ++ unsigned char* buffer) ++{ ++ while(atoms) ++ { ++ size_t size = read_32(atoms->atom_); ++ memcpy(buffer, atoms->atom_, size); ++ buffer += size; ++ atoms = atoms->next_; ++ } ++ ++ return buffer; ++} ++ ++static unsigned char* atom_writer(struct unknown_atom_t* unknown_atoms, ++ struct atom_write_list_t* atom_write_list, ++ unsigned int atom_write_list_size, ++ unsigned char* buffer) ++{ ++ unsigned i; ++ const int write_box64 = 0; ++ ++ if(unknown_atoms) ++ { ++ buffer = atom_writer_unknown(unknown_atoms, buffer); ++ } ++ ++ for(i = 0; i != atom_write_list_size; ++i) ++ { ++ if(atom_write_list[i].source_ != 0) ++ { ++ unsigned char* atom_start = buffer; ++ // atom size ++ if(write_box64) ++ { ++ write_32(buffer, 1); // box64 ++ } ++ buffer += 4; ++ ++ // atom type ++ buffer = write_32(buffer, atom_write_list[i].type_); ++ if(write_box64) ++ { ++ buffer += 8; // box64 ++ } ++ ++ // atom payload ++ buffer = atom_write_list[i].writer_(atom_write_list[i].parent_, ++ atom_write_list[i].source_, buffer); ++ ++ if(write_box64) ++ write_64(atom_start + 8, buffer - atom_start); ++ else ++ write_32(atom_start, buffer - atom_start); ++ } ++ } ++ ++ return buffer; ++} ++ ++struct tkhd_t ++{ ++ unsigned int version_; ++ unsigned int flags_; ++ uint64_t creation_time_; ++ uint64_t modification_time_; ++ uint32_t track_id_; ++ uint32_t reserved_; ++ uint64_t duration_; ++ uint32_t reserved2_[2]; ++ uint16_t layer_; ++ uint16_t predefined_; ++ uint16_t volume_; ++ uint16_t reserved3_; ++ uint32_t matrix_[9]; ++ uint32_t width_; ++ uint32_t height_; ++}; ++ ++struct mdhd_t ++{ ++ unsigned int version_; ++ unsigned int flags_; ++ uint64_t creation_time_; ++ uint64_t modification_time_; ++ uint32_t timescale_; ++ uint64_t duration_; ++ unsigned int language_[3]; ++ uint16_t predefined_; ++}; ++ ++struct vmhd_t ++{ ++ unsigned int version_; ++ unsigned int flags_; ++ uint16_t graphics_mode_; ++ uint16_t opcolor_[3]; ++}; ++ ++struct hdlr_t ++{ ++ unsigned int version_; ++ unsigned int flags_; ++ uint32_t predefined_; ++ uint32_t handler_type_; ++ uint32_t reserved1_; ++ uint32_t reserved2_; ++ uint32_t reserved3_; ++ char* name_; ++}; ++ ++struct stbl_t ++{ ++ struct unknown_atom_t* unknown_atoms_; ++//struct stsd_t* stsd_; // sample description ++ struct stts_t* stts_; // decoding time-to-sample ++ struct stss_t* stss_; // sync sample ++ struct stsc_t* stsc_; // sample-to-chunk ++ struct stsz_t* stsz_; // sample size ++ struct stco_t* stco_; // chunk offset ++ struct ctts_t* ctts_; // composition time-to-sample ++ ++ void* stco_inplace_; // newly generated stco (patched inplace) ++}; ++ ++struct stts_table_t ++{ ++ uint32_t sample_count_; ++ uint32_t sample_duration_; ++}; ++ ++struct stts_t ++{ ++ unsigned int version_; ++ unsigned int flags_; ++ uint32_t entries_; ++ struct stts_table_t* table_; ++}; ++ ++struct stss_t ++{ ++ unsigned int version_; ++ unsigned int flags_; ++ uint32_t entries_; ++ uint32_t* sample_numbers_; ++}; ++ ++struct stsc_table_t ++{ ++ uint32_t chunk_; ++ uint32_t samples_; ++ uint32_t id_; ++}; ++ ++struct stsc_t ++{ ++ unsigned int version_; ++ unsigned int flags_; ++ uint32_t entries_; ++ struct stsc_table_t* table_; ++}; ++ ++struct stsz_t ++{ ++ unsigned int version_; ++ unsigned int flags_; ++ uint32_t sample_size_; ++ uint32_t entries_; ++ uint32_t* sample_sizes_; ++}; ++ ++struct stco_t ++{ ++ unsigned int version_; ++ unsigned int flags_; ++ uint32_t entries_; ++ uint64_t* chunk_offsets_; ++}; ++ ++struct ctts_table_t ++{ ++ uint32_t sample_count_; ++ uint32_t sample_offset_; ++}; ++ ++struct ctts_t ++{ ++ unsigned int version_; ++ unsigned int flags_; ++ uint32_t entries_; ++ struct ctts_table_t* table_; ++}; ++ ++struct minf_t ++{ ++ struct unknown_atom_t* unknown_atoms_; ++ struct vmhd_t* vmhd_; ++// struct dinf_t* dinf_; ++ struct stbl_t* stbl_; ++}; ++ ++struct mdia_t ++{ ++ struct unknown_atom_t* unknown_atoms_; ++ struct mdhd_t* mdhd_; ++ struct hdlr_t* hdlr_; ++ struct minf_t* minf_; ++}; ++ ++struct chunks_t ++{ ++ unsigned int sample_; // number of the first sample in the chunk ++ unsigned int size_; // number of samples in the chunk ++ int id_; // for multiple codecs mode - not used ++ uint64_t pos_; // start byte position of chunk ++}; ++ ++struct samples_t ++{ ++ unsigned int pts_; // decoding/presentation time ++ unsigned int size_; // size in bytes ++ uint64_t pos_; // byte offset ++ unsigned int cto_; // composition time offset ++}; ++ ++struct trak_t ++{ ++ struct unknown_atom_t* unknown_atoms_; ++ struct tkhd_t* tkhd_; ++ struct mdia_t* mdia_; ++ ++ /* temporary indices */ ++ unsigned int chunks_size_; ++ struct chunks_t* chunks_; ++ ++ unsigned int samples_size_; ++ struct samples_t* samples_; ++}; ++ ++struct mvhd_t ++{ ++ unsigned int version_; ++ unsigned int flags_; ++ uint64_t creation_time_; ++ uint64_t modification_time_; ++ uint32_t timescale_; ++ uint64_t duration_; ++ uint32_t rate_; ++ uint16_t volume_; ++ uint16_t reserved1_; ++ uint32_t reserved2_[2]; ++ uint32_t matrix_[9]; ++ uint32_t predefined_[6]; ++ uint32_t next_track_id_; ++}; ++ ++struct moov_t ++{ ++ struct unknown_atom_t* unknown_atoms_; ++ struct mvhd_t* mvhd_; ++ unsigned int tracks_; ++ struct trak_t* traks_[MAX_TRACKS]; ++}; ++ ++ ++static struct tkhd_t* tkhd_init() ++{ ++ struct tkhd_t* tkhd = (struct tkhd_t*)malloc(sizeof(struct tkhd_t)); ++ ++ return tkhd; ++} ++ ++static void tkhd_exit(struct tkhd_t* tkhd) ++{ ++ free(tkhd); ++} ++ ++static void* tkhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ unsigned int i; ++ ++ struct tkhd_t* tkhd = tkhd_init(); ++ ++ tkhd->version_ = read_8(buffer + 0); ++ tkhd->flags_ = read_24(buffer + 1); ++ if(tkhd->version_ == 0) ++ { ++ if(size < 92-8) ++ return 0; ++ ++ tkhd->creation_time_ = read_32(buffer + 4); ++ tkhd->modification_time_ = read_32(buffer + 8); ++ tkhd->track_id_ = read_32(buffer + 12); ++ tkhd->reserved_ = read_32(buffer + 16); ++ tkhd->duration_ = read_32(buffer + 20); ++ buffer += 24; ++ } ++ else ++ { ++ if(size < 104-8) ++ return 0; ++ ++ tkhd->creation_time_ = read_64(buffer + 4); ++ tkhd->modification_time_ = read_64(buffer + 12); ++ tkhd->track_id_ = read_32(buffer + 20); ++ tkhd->reserved_ = read_32(buffer + 24); ++ tkhd->duration_ = read_64(buffer + 28); ++ buffer += 36; ++ } ++ ++ tkhd->reserved2_[0] = read_32(buffer + 0); ++ tkhd->reserved2_[1] = read_32(buffer + 4); ++ tkhd->layer_ = read_16(buffer + 8); ++ tkhd->predefined_ = read_16(buffer + 10); ++ tkhd->volume_ = read_16(buffer + 12); ++ tkhd->reserved3_ = read_16(buffer + 14); ++ buffer += 16; ++ ++ for(i = 0; i != 9; ++i) ++ { ++ tkhd->matrix_[i] = read_32(buffer); ++ buffer += 4; ++ } ++ ++ tkhd->width_ = read_32(buffer + 0); ++ tkhd->height_ = read_32(buffer + 4); ++ ++ return tkhd; ++} ++ ++static unsigned char* tkhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct tkhd_t const* tkhd = atom; ++ unsigned int i; ++ ++ buffer = write_8(buffer, tkhd->version_); ++ buffer = write_24(buffer, tkhd->flags_); ++ ++ if(tkhd->version_ == 0) ++ { ++ buffer = write_32(buffer, (uint32_t)tkhd->creation_time_); ++ buffer = write_32(buffer, (uint32_t)tkhd->modification_time_); ++ buffer = write_32(buffer, tkhd->track_id_); ++ buffer = write_32(buffer, tkhd->reserved_); ++ buffer = write_32(buffer, (uint32_t)tkhd->duration_); ++ } ++ else ++ { ++ buffer = write_64(buffer, tkhd->creation_time_); ++ buffer = write_64(buffer, tkhd->modification_time_); ++ buffer = write_32(buffer, tkhd->track_id_); ++ buffer = write_32(buffer, tkhd->reserved_); ++ buffer = write_64(buffer, tkhd->duration_); ++ } ++ ++ buffer = write_32(buffer, tkhd->reserved2_[0]); ++ buffer = write_32(buffer, tkhd->reserved2_[1]); ++ buffer = write_16(buffer, tkhd->layer_); ++ buffer = write_16(buffer, tkhd->predefined_); ++ buffer = write_16(buffer, tkhd->volume_); ++ buffer = write_16(buffer, tkhd->reserved3_); ++ ++ for(i = 0; i != 9; ++i) ++ { ++ buffer = write_32(buffer, tkhd->matrix_[i]); ++ } ++ ++ buffer = write_32(buffer, tkhd->width_); ++ buffer = write_32(buffer, tkhd->height_); ++ ++ return buffer; ++} ++ ++static struct mdhd_t* mdhd_init() ++{ ++ struct mdhd_t* mdhd = (struct mdhd_t*)malloc(sizeof(struct mdhd_t)); ++ ++ return mdhd; ++} ++ ++static void mdhd_exit(struct mdhd_t* mdhd) ++{ ++ free(mdhd); ++} ++ ++static void* mdhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t UNUSED(size)) ++{ ++ uint16_t language; ++ unsigned int i; ++ ++ struct mdhd_t* mdhd = mdhd_init(); ++ mdhd->version_ = read_8(buffer + 0); ++ mdhd->flags_ = read_24(buffer + 1); ++ if(mdhd->version_ == 0) ++ { ++ mdhd->creation_time_ = read_32(buffer + 4); ++ mdhd->modification_time_ = read_32(buffer + 8); ++ mdhd->timescale_ = read_32(buffer + 12); ++ mdhd->duration_ = read_32(buffer + 16); ++ buffer += 20; ++ } ++ else ++ { ++ mdhd->creation_time_ = read_64(buffer + 4); ++ mdhd->modification_time_ = read_64(buffer + 12); ++ mdhd->timescale_ = read_32(buffer + 20); ++ mdhd->duration_ = read_64(buffer + 24); ++ buffer += 32; ++ } ++ ++ language = read_16(buffer + 0); ++ for(i = 0; i != 3; ++i) ++ { ++ mdhd->language_[i] = ((language >> ((2 - i) * 5)) & 0x1f) + 0x60; ++ } ++ ++ mdhd->predefined_ = read_16(buffer + 2); ++ ++ return mdhd; ++} ++ ++static unsigned char* mdhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct mdhd_t const* mdhd = atom; ++ ++ buffer = write_8(buffer, mdhd->version_); ++ buffer = write_24(buffer, mdhd->flags_); ++ ++ if(mdhd->version_ == 0) ++ { ++ buffer = write_32(buffer, (uint32_t)mdhd->creation_time_); ++ buffer = write_32(buffer, (uint32_t)mdhd->modification_time_); ++ buffer = write_32(buffer, mdhd->timescale_); ++ buffer = write_32(buffer, (uint32_t)mdhd->duration_); ++ } ++ else ++ { ++ buffer = write_64(buffer, mdhd->creation_time_); ++ buffer = write_64(buffer, mdhd->modification_time_); ++ buffer = write_32(buffer, mdhd->timescale_); ++ buffer = write_64(buffer, mdhd->duration_); ++ } ++ ++ buffer = write_16(buffer, ++ ((mdhd->language_[0] - 0x60) << 10) + ++ ((mdhd->language_[1] - 0x60) << 5) + ++ ((mdhd->language_[2] - 0x60) << 0)); ++ ++ buffer = write_16(buffer, mdhd->predefined_); ++ ++ return buffer; ++} ++ ++static struct vmhd_t* vmhd_init() ++{ ++ struct vmhd_t* atom = (struct vmhd_t*)malloc(sizeof(struct vmhd_t)); ++ ++ return atom; ++} ++ ++void vmhd_exit(struct vmhd_t* atom) ++{ ++ free(atom); ++} ++ ++static void* vmhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ unsigned int i; ++ ++ struct vmhd_t* atom; ++ ++ if(size < 20-8) ++ return 0; ++ ++ atom = vmhd_init(); ++ atom->version_ = read_8(buffer + 0); ++ atom->flags_ = read_24(buffer + 1); ++ ++ atom->graphics_mode_ = read_16(buffer + 4); ++ buffer += 6; ++ for(i = 0; i != 3; ++i) ++ { ++ atom->opcolor_[i] = read_16(buffer); ++ buffer += 2; ++ } ++ ++ return atom; ++} ++ ++static unsigned char* vmhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct vmhd_t const* vmhd = atom; ++ unsigned int i; ++ ++ buffer = write_8(buffer, vmhd->version_); ++ buffer = write_24(buffer, vmhd->flags_); ++ buffer = write_16(buffer, vmhd->graphics_mode_); ++ for(i = 0; i != 3; ++i) ++ { ++ buffer = write_16(buffer, vmhd->opcolor_[i]); ++ } ++ ++ return buffer; ++} ++ ++static struct hdlr_t* hdlr_init() ++{ ++ struct hdlr_t* atom = (struct hdlr_t*)malloc(sizeof(struct hdlr_t)); ++ atom->name_ = 0; ++ ++ return atom; ++} ++ ++static void hdlr_exit(struct hdlr_t* atom) ++{ ++ if(atom->name_) ++ { ++ free(atom->name_); ++ } ++ free(atom); ++} ++ ++static void* hdlr_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ struct hdlr_t* atom; ++ ++ if(size < 8) ++ return 0; ++ ++ atom = hdlr_init(); ++ atom->version_ = read_8(buffer + 0); ++ atom->flags_ = read_24(buffer + 1); ++ atom->predefined_ = read_32(buffer + 4); ++ atom->handler_type_ = read_32(buffer + 8); ++ atom->reserved1_ = read_32(buffer + 12); ++ atom->reserved2_ = read_32(buffer + 16); ++ atom->reserved3_ = read_32(buffer + 20); ++ buffer += 24; ++ size -= 24; ++ if(size > 0) ++ { ++ size_t length = (size_t)size; ++ atom->name_ = malloc(length + 1); ++ if(atom->predefined_ == FOURCC('m', 'h', 'l', 'r')) ++ { ++ length = read_8(buffer); ++ buffer += 1; ++ if(size < length) ++ length = (size_t)size; ++ } ++ memcpy(atom->name_, buffer, length); ++ atom->name_[length] = '\0'; ++ } ++ ++ return atom; ++} ++ ++static unsigned char* hdlr_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct hdlr_t* hdlr = atom; ++ buffer = write_8(buffer, hdlr->version_); ++ buffer = write_24(buffer, hdlr->flags_); ++ ++ buffer = write_32(buffer, hdlr->predefined_); ++ buffer = write_32(buffer, hdlr->handler_type_); ++ buffer = write_32(buffer, hdlr->reserved1_); ++ buffer = write_32(buffer, hdlr->reserved2_); ++ buffer = write_32(buffer, hdlr->reserved3_); ++ if(hdlr->name_) ++ { ++ char const* p; ++ if(hdlr->predefined_ == FOURCC('m', 'h', 'l', 'r')) ++ { ++ buffer = write_8(buffer, strlen(hdlr->name_)); ++ } ++ ++ for(p = hdlr->name_; *p; ++p) ++ buffer = write_8(buffer, *p); ++ } ++ ++ return buffer; ++} ++ ++static struct stts_t* stts_init() ++{ ++ struct stts_t* atom = (struct stts_t*)malloc(sizeof(struct stts_t)); ++ atom->table_ = 0; ++ ++ return atom; ++} ++ ++void stts_exit(struct stts_t* atom) ++{ ++ if(atom->table_) ++ { ++ free(atom->table_); ++ } ++ free(atom); ++} ++ ++static void* stts_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ unsigned int i; ++ ++ struct stts_t* atom; ++ ++ if(size < 8) ++ return 0; ++ ++ atom = stts_init(); ++ atom->version_ = read_8(buffer + 0); ++ atom->flags_ = read_24(buffer + 1); ++ atom->entries_ = read_32(buffer + 4); ++ ++ if(size < 8 + atom->entries_ * sizeof(struct stts_table_t)) ++ return 0; ++ ++ buffer += 8; ++ ++ atom->table_ = (struct stts_table_t*)(malloc(atom->entries_ * sizeof(struct stts_table_t))); ++ ++ for(i = 0; i != atom->entries_; ++i) ++ { ++ atom->table_[i].sample_count_ = read_32(buffer + 0); ++ atom->table_[i].sample_duration_ = read_32(buffer + 4); ++ buffer += 8; ++ } ++ ++ return atom; ++} ++ ++static unsigned char* stts_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct stts_t* stts = atom; ++ unsigned int i; ++ ++ buffer = write_8(buffer, stts->version_); ++ buffer = write_24(buffer, stts->flags_); ++ buffer = write_32(buffer, stts->entries_); ++ for(i = 0; i != stts->entries_; ++i) ++ { ++ buffer = write_32(buffer, stts->table_[i].sample_count_); ++ buffer = write_32(buffer, stts->table_[i].sample_duration_); ++ } ++ ++ return buffer; ++} ++ ++static unsigned int stts_get_sample(struct stts_t const* stts, uint64_t time) ++{ ++ unsigned int stts_index = 0; ++ unsigned int stts_count; ++ ++ unsigned int ret = 0; ++ uint64_t time_count = 0; ++ ++ for(; stts_index != stts->entries_; ++stts_index) ++ { ++ unsigned int sample_count = stts->table_[stts_index].sample_count_; ++ unsigned int sample_duration = stts->table_[stts_index].sample_duration_; ++ if(time_count + (uint64_t)sample_duration * (uint64_t)sample_count >= time) ++ { ++ stts_count = (unsigned int)((time - time_count) / sample_duration); ++ time_count += (uint64_t)stts_count * (uint64_t)sample_duration; ++ ret += stts_count; ++ break; ++ } ++ else ++ { ++ time_count += (uint64_t)sample_duration * (uint64_t)sample_count; ++ ret += sample_count; ++ } ++ } ++ return ret; ++} ++ ++static uint64_t stts_get_time(struct stts_t const* stts, unsigned int sample) ++{ ++ uint64_t ret = 0; ++ unsigned int stts_index = 0; ++ unsigned int sample_count = 0; ++ ++ for(;;) ++ { ++ unsigned int table_sample_count = stts->table_[stts_index].sample_count_; ++ unsigned int table_sample_duration = stts->table_[stts_index].sample_duration_; ++ if(sample_count + table_sample_count > sample) ++ { ++ unsigned int stts_count = (sample - sample_count); ++ ret += (uint64_t)stts_count * (uint64_t)table_sample_duration; ++ break; ++ } ++ else ++ { ++ sample_count += table_sample_count; ++ ret += (uint64_t)table_sample_count * (uint64_t)table_sample_duration; ++ stts_index++; ++ } ++ } ++ return ret; ++} ++ ++static uint64_t stts_get_duration(struct stts_t const* stts) ++{ ++ uint64_t duration = 0; ++ unsigned int i; ++ for(i = 0; i != stts->entries_; ++i) ++ { ++ unsigned int sample_count = stts->table_[i].sample_count_; ++ unsigned int sample_duration = stts->table_[i].sample_duration_; ++ duration += (uint64_t)sample_duration * (uint64_t)sample_count; ++ } ++ ++ return duration; ++} ++ ++static unsigned int stts_get_samples(struct stts_t const* stts) ++{ ++ unsigned int samples = 0; ++ unsigned int entries = stts->entries_; ++ unsigned int i; ++ for(i = 0; i != entries; ++i) ++ { ++ unsigned int sample_count = stts->table_[i].sample_count_; ++// unsigned int sample_duration = stts->table_[i].sample_duration_; ++ samples += sample_count; ++ } ++ ++ return samples; ++} ++ ++static struct stss_t* stss_init() ++{ ++ struct stss_t* atom = (struct stss_t*)malloc(sizeof(struct stss_t)); ++ atom->sample_numbers_ = 0; ++ ++ return atom; ++} ++ ++void stss_exit(struct stss_t* atom) ++{ ++ if(atom->sample_numbers_) ++ { ++ free(atom->sample_numbers_); ++ } ++ free(atom); ++} ++ ++static void* stss_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ unsigned int i; ++ ++ struct stss_t* atom; ++ ++ if(size < 8) ++ return 0; ++ ++ atom = stss_init(); ++ atom->version_ = read_8(buffer + 0); ++ atom->flags_ = read_24(buffer + 1); ++ atom->entries_ = read_32(buffer + 4); ++ ++ if(size < 8 + atom->entries_ * sizeof(uint32_t)) ++ return 0; ++ ++ buffer += 8; ++ ++ atom->sample_numbers_ = (uint32_t*)malloc(atom->entries_ * sizeof(uint32_t)); ++ ++ for(i = 0; i != atom->entries_; ++i) ++ { ++ atom->sample_numbers_[i] = read_32(buffer); ++ buffer += 4; ++ } ++ ++ return atom; ++} ++ ++static unsigned char* stss_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct stss_t const* stss = atom; ++ unsigned int i; ++ ++ buffer = write_8(buffer, stss->version_); ++ buffer = write_24(buffer, stss->flags_); ++ buffer = write_32(buffer, stss->entries_); ++ for(i = 0; i != stss->entries_; ++i) ++ { ++ buffer = write_32(buffer, stss->sample_numbers_[i]); ++ } ++ ++ return buffer; ++} ++ ++static unsigned int stss_get_nearest_keyframe(struct stss_t const* stss, unsigned int sample) ++{ ++ // scan the sync samples to find the key frame that precedes the sample number ++ unsigned int i; ++ unsigned int table_sample = 0; ++ for(i = 0; i != stss->entries_; ++i) ++ { ++ table_sample = stss->sample_numbers_[i]; ++ if(table_sample >= sample) ++ break; ++ } ++ if(table_sample == sample) ++ return table_sample; ++ else ++ return stss->sample_numbers_[i - 1]; ++} ++ ++static struct stsc_t* stsc_init() ++{ ++ struct stsc_t* atom = (struct stsc_t*)malloc(sizeof(struct stsc_t)); ++ atom->table_ = 0; ++ ++ return atom; ++} ++ ++static void stsc_exit(struct stsc_t* atom) ++{ ++ if(atom->table_) ++ { ++ free(atom->table_); ++ } ++ free(atom); ++} ++ ++static void* stsc_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ unsigned int i; ++ ++ struct stsc_t* atom; ++ ++ if(size < 8) ++ return 0; ++ ++ atom = stsc_init(); ++ atom->version_ = read_8(buffer + 0); ++ atom->flags_ = read_24(buffer + 1); ++ atom->entries_ = read_32(buffer + 4); ++ ++ if(size < 8 + atom->entries_ * sizeof(struct stsc_table_t)) ++ return 0; ++ ++ buffer += 8; ++ ++ // reserve space for one extra entry as when splitting the video we may have to ++ // split the first entry ++ atom->table_ = (struct stsc_table_t*)(malloc((atom->entries_ + 1) * sizeof(struct stsc_table_t))); ++ ++ for(i = 0; i != atom->entries_; ++i) ++ { ++ atom->table_[i].chunk_ = read_32(buffer + 0) - 1; // Note: we use zero based ++ atom->table_[i].samples_ = read_32(buffer + 4); ++ atom->table_[i].id_ = read_32(buffer + 8); ++ buffer += 12; ++ } ++ ++ return atom; ++} ++ ++static unsigned char* stsc_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct stsc_t* stsc = atom; ++ unsigned int i; ++ ++ buffer = write_8(buffer, stsc->version_); ++ buffer = write_24(buffer, stsc->flags_); ++ buffer = write_32(buffer, stsc->entries_); ++ for(i = 0; i != stsc->entries_; ++i) ++ { ++ buffer = write_32(buffer, stsc->table_[i].chunk_ + 1); ++ buffer = write_32(buffer, stsc->table_[i].samples_); ++ buffer = write_32(buffer, stsc->table_[i].id_); ++ } ++ ++ return buffer; ++} ++ ++static struct stsz_t* stsz_init() ++{ ++ struct stsz_t* atom = (struct stsz_t*)malloc(sizeof(struct stsz_t)); ++ atom->sample_sizes_ = 0; ++ ++ return atom; ++} ++ ++static void stsz_exit(struct stsz_t* atom) ++{ ++ if(atom->sample_sizes_) ++ { ++ free(atom->sample_sizes_); ++ } ++ free(atom); ++} ++ ++static void* stsz_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ unsigned int i; ++ ++ struct stsz_t* atom; ++ ++ if(size < 12) ++ { ++ printf("Error: not enough bytes for stsz atom\n"); ++ return 0; ++ } ++ ++ atom = stsz_init(); ++ atom->version_ = read_8(buffer + 0); ++ atom->flags_ = read_24(buffer + 1); ++ atom->sample_size_ = read_32(buffer + 4); ++ atom->entries_ = read_32(buffer + 8); ++ buffer += 12; ++ ++ // fix for clayton.mp4, it mistakenly says there is 1 entry ++ if(atom->sample_size_ && atom->entries_) ++ atom->entries_ = 0; ++ ++ if(size < 12 + atom->entries_ * sizeof(uint32_t)) ++ { ++ printf("Error: stsz.entries don't match with size\n"); ++ stsz_exit(atom); ++ return 0; ++ } ++ ++ if(!atom->sample_size_) ++ { ++ atom->sample_sizes_ = (uint32_t*)malloc(atom->entries_ * sizeof(uint32_t)); ++ for(i = 0; i != atom->entries_; ++i) ++ { ++ atom->sample_sizes_[i] = read_32(buffer); ++ buffer += 4; ++ } ++ } ++ ++ return atom; ++} ++ ++static unsigned char* stsz_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct stsz_t* stsz = atom; ++ unsigned int i; ++ unsigned int entries = stsz->sample_size_ ? 0 : stsz->entries_; ++ ++ buffer = write_8(buffer, stsz->version_); ++ buffer = write_24(buffer, stsz->flags_); ++ buffer = write_32(buffer, stsz->sample_size_); ++ buffer = write_32(buffer, entries); ++ for(i = 0; i != entries; ++i) ++ { ++ buffer = write_32(buffer, stsz->sample_sizes_[i]); ++ } ++ ++ return buffer; ++} ++ ++static struct stco_t* stco_init() ++{ ++ struct stco_t* atom = (struct stco_t*)malloc(sizeof(struct stco_t)); ++ atom->chunk_offsets_ = 0; ++ ++ return atom; ++} ++ ++static void stco_exit(struct stco_t* atom) ++{ ++ if(atom->chunk_offsets_) ++ { ++ free(atom->chunk_offsets_); ++ } ++ free(atom); ++} ++ ++static void* stco_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ unsigned int i; ++ ++ struct stco_t* atom; ++ ++ if(size < 8) ++ return 0; ++ ++ atom = stco_init(); ++ atom->version_ = read_8(buffer + 0); ++ atom->flags_ = read_24(buffer + 1); ++ atom->entries_ = read_32(buffer + 4); ++ buffer += 8; ++ ++ if(size < 8 + atom->entries_ * sizeof(uint32_t)) ++ return 0; ++ ++ atom->chunk_offsets_ = (uint64_t*)malloc(atom->entries_ * sizeof(uint64_t)); ++ for(i = 0; i != atom->entries_; ++i) ++ { ++ atom->chunk_offsets_[i] = read_32(buffer); ++ buffer += 4; ++ } ++ ++ return atom; ++} ++ ++static void* co64_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ unsigned int i; ++ ++ struct stco_t* atom; ++ ++ if(size < 8) ++ return 0; ++ ++ atom = stco_init(); ++ atom->version_ = read_8(buffer + 0); ++ atom->flags_ = read_24(buffer + 1); ++ atom->entries_ = read_32(buffer + 4); ++ buffer += 8; ++ ++ if(size < 8 + atom->entries_ * sizeof(uint64_t)) ++ return 0; ++ ++ atom->chunk_offsets_ = (uint64_t*)malloc(atom->entries_ * sizeof(uint64_t)); ++ for(i = 0; i != atom->entries_; ++i) ++ { ++ atom->chunk_offsets_[i] = read_64(buffer); ++ buffer += 8; ++ } ++ ++ return atom; ++} ++ ++static unsigned char* stco_write(void* parent, void* atom, unsigned char* buffer) ++{ ++ struct stbl_t* stbl = parent; ++ struct stco_t* stco = atom; ++ unsigned int i; ++ ++ stbl->stco_inplace_ = buffer; // newly generated stco (patched inplace) ++ ++ buffer = write_8(buffer, stco->version_); ++ buffer = write_24(buffer, stco->flags_); ++ buffer = write_32(buffer, stco->entries_); ++ for(i = 0; i != stco->entries_; ++i) ++ { ++ buffer = write_32(buffer, (uint32_t)(stco->chunk_offsets_[i])); ++ } ++ ++ return buffer; ++} ++ ++static void stco_shift_offsets(struct stco_t* stco, int offset) ++{ ++ unsigned int i; ++ for(i = 0; i != stco->entries_; ++i) ++ stco->chunk_offsets_[i] += offset; ++} ++ ++static void stco_shift_offsets_inplace(unsigned char* stco, int offset) ++{ ++ unsigned int entries = read_32(stco + 4); ++ unsigned int* table = (unsigned int*)(stco + 8); ++ unsigned int i; ++ for(i = 0; i != entries; ++i) ++ write_32((unsigned char*)&table[i], (read_32((unsigned char*)&table[i]) + offset)); ++} ++ ++static struct ctts_t* ctts_init() ++{ ++ struct ctts_t* atom = (struct ctts_t*)malloc(sizeof(struct ctts_t)); ++ atom->table_ = 0; ++ ++ return atom; ++} ++ ++static void ctts_exit(struct ctts_t* atom) ++{ ++ if(atom->table_) ++ { ++ free(atom->table_); ++ } ++ free(atom); ++} ++ ++static void* ctts_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ unsigned int i; ++ ++ struct ctts_t* atom; ++ ++ if(size < 8) ++ return 0; ++ ++ atom = ctts_init(); ++ atom->version_ = read_8(buffer + 0); ++ atom->flags_ = read_24(buffer + 1); ++ atom->entries_ = read_32(buffer + 4); ++ ++ if(size < 8 + atom->entries_ * sizeof(struct ctts_table_t)) ++ return 0; ++ ++ buffer += 8; ++ ++ atom->table_ = (struct ctts_table_t*)(malloc(atom->entries_ * sizeof(struct ctts_table_t))); ++ ++ for(i = 0; i != atom->entries_; ++i) ++ { ++ atom->table_[i].sample_count_ = read_32(buffer + 0); ++ atom->table_[i].sample_offset_ = read_32(buffer + 4); ++ buffer += 8; ++ } ++ ++ return atom; ++} ++ ++static unsigned char* ctts_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct ctts_t const* ctts = atom; ++ unsigned int i; ++ ++ buffer = write_8(buffer, ctts->version_); ++ buffer = write_24(buffer, ctts->flags_); ++ buffer = write_32(buffer, ctts->entries_); ++ for(i = 0; i != ctts->entries_; ++i) ++ { ++ buffer = write_32(buffer, (uint32_t)(ctts->table_[i].sample_count_)); ++ buffer = write_32(buffer, (uint32_t)(ctts->table_[i].sample_offset_)); ++ } ++ ++ return buffer; ++} ++ ++static unsigned int ctts_get_samples(struct ctts_t const* ctts) ++{ ++ unsigned int samples = 0; ++ unsigned int entries = ctts->entries_; ++ unsigned int i; ++ for(i = 0; i != entries; ++i) ++ { ++ unsigned int sample_count = ctts->table_[i].sample_count_; ++// unsigned int sample_offset = ctts->table_[i].sample_offset_; ++ samples += sample_count; ++ } ++ ++ return samples; ++} ++ ++static struct stbl_t* stbl_init() ++{ ++ struct stbl_t* atom = (struct stbl_t*)malloc(sizeof(struct stbl_t)); ++ atom->unknown_atoms_ = 0; ++ atom->stts_ = 0; ++ atom->stss_ = 0; ++ atom->stsc_ = 0; ++ atom->stsz_ = 0; ++ atom->stco_ = 0; ++ atom->ctts_ = 0; ++ ++ return atom; ++} ++ ++static void stbl_exit(struct stbl_t* atom) ++{ ++ if(atom->unknown_atoms_) ++ { ++ unknown_atom_exit(atom->unknown_atoms_); ++ } ++ if(atom->stts_) ++ { ++ stts_exit(atom->stts_); ++ } ++ if(atom->stss_) ++ { ++ stss_exit(atom->stss_); ++ } ++ if(atom->stsc_) ++ { ++ stsc_exit(atom->stsc_); ++ } ++ if(atom->stsz_) ++ { ++ stsz_exit(atom->stsz_); ++ } ++ if(atom->stco_) ++ { ++ stco_exit(atom->stco_); ++ } ++ if(atom->ctts_) ++ { ++ ctts_exit(atom->ctts_); ++ } ++ ++ free(atom); ++} ++ ++static int stbl_add_stts(void* parent, void* child) ++{ ++ struct stbl_t* stbl = parent; ++ stbl->stts_ = child; ++ ++ return 1; ++} ++ ++static int stbl_add_stss(void* parent, void* child) ++{ ++ struct stbl_t* stbl = parent; ++ stbl->stss_ = child; ++ ++ return 1; ++} ++ ++static int stbl_add_stsc(void* parent, void* child) ++{ ++ struct stbl_t* stbl = parent; ++ stbl->stsc_ = child; ++ ++ return 1; ++} ++ ++static int stbl_add_stsz(void* parent, void* child) ++{ ++ struct stbl_t* stbl = parent; ++ stbl->stsz_ = child; ++ ++ return 1; ++} ++ ++static int stbl_add_stco(void* parent, void* child) ++{ ++ struct stbl_t* stbl = parent; ++ stbl->stco_ = child; ++ ++ return 1; ++} ++ ++static int stbl_add_ctts(void* parent, void* child) ++{ ++ struct stbl_t* stbl = parent; ++ stbl->ctts_ = child; ++ ++ return 1; ++} ++ ++static void* stbl_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ struct stbl_t* atom = stbl_init(); ++ ++ struct atom_read_list_t atom_read_list[] = { ++ { FOURCC('s', 't', 't', 's'), atom, &stbl_add_stts, &stts_read }, ++ { FOURCC('s', 't', 's', 's'), atom, &stbl_add_stss, &stss_read }, ++ { FOURCC('s', 't', 's', 'c'), atom, &stbl_add_stsc, &stsc_read }, ++ { FOURCC('s', 't', 's', 'z'), atom, &stbl_add_stsz, &stsz_read }, ++ { FOURCC('s', 't', 'c', 'o'), atom, &stbl_add_stco, &stco_read }, ++ { FOURCC('c', 'o', '6', '4'), atom, &stbl_add_stco, &co64_read }, ++ { FOURCC('c', 't', 't', 's'), atom, &stbl_add_ctts, &ctts_read }, ++ }; ++ ++ int result = atom_reader(atom_read_list, ++ sizeof(atom_read_list) / sizeof(atom_read_list[0]), ++ atom, ++ buffer, size); ++ ++ // check for mandatory atoms ++ if(!atom->stts_) ++ { ++ printf("stbl: missing stts\n"); ++ result = 0; ++ } ++ ++ if(!atom->stsc_) ++ { ++ printf("stbl: missing stsc\n"); ++ result = 0; ++ } ++ ++ if(!atom->stsz_) ++ { ++ printf("stbl: missing stsz\n"); ++ result = 0; ++ } ++ ++ if(!atom->stco_) ++ { ++ printf("stbl: missing stco\n"); ++ result = 0; ++ } ++ ++ if(!result) ++ { ++ stbl_exit(atom); ++ return 0; ++ } ++ ++ return atom; ++} ++ ++static unsigned char* stbl_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct stbl_t* stbl = atom; ++ struct atom_write_list_t atom_write_list[] = { ++ { FOURCC('s', 't', 't', 's'), stbl, stbl->stts_, &stts_write }, ++ { FOURCC('s', 't', 's', 's'), stbl, stbl->stss_, &stss_write }, ++ { FOURCC('s', 't', 's', 'c'), stbl, stbl->stsc_, &stsc_write }, ++ { FOURCC('s', 't', 's', 'z'), stbl, stbl->stsz_, &stsz_write }, ++ { FOURCC('s', 't', 'c', 'o'), stbl, stbl->stco_, &stco_write }, ++ { FOURCC('c', 't', 't', 's'), stbl, stbl->ctts_, &ctts_write }, ++ }; ++ ++ buffer = atom_writer(stbl->unknown_atoms_, ++ atom_write_list, ++ sizeof(atom_write_list) / sizeof(atom_write_list[0]), ++ buffer); ++ ++ return buffer; ++} ++ ++static unsigned int stbl_get_nearest_keyframe(struct stbl_t const* stbl, unsigned int sample) ++{ ++ // If the sync atom is not present, all samples are implicit sync samples. ++ if(!stbl->stss_) ++ return sample; ++ ++ return stss_get_nearest_keyframe(stbl->stss_, sample); ++} ++ ++static struct minf_t* minf_init() ++{ ++ struct minf_t* atom = (struct minf_t*)malloc(sizeof(struct minf_t)); ++ atom->unknown_atoms_ = 0; ++ atom->vmhd_ = 0; ++ atom->stbl_ = 0; ++ ++ return atom; ++} ++ ++static void minf_exit(struct minf_t* atom) ++{ ++ if(atom->unknown_atoms_) ++ { ++ unknown_atom_exit(atom->unknown_atoms_); ++ } ++ if(atom->vmhd_) ++ { ++ vmhd_exit(atom->vmhd_); ++ } ++ if(atom->stbl_) ++ { ++ stbl_exit(atom->stbl_); ++ } ++ free(atom); ++} ++ ++static int minf_add_vmhd(void* parent, void* child) ++{ ++ struct minf_t* minf = parent; ++ minf->vmhd_ = child; ++ ++ return 1; ++} ++ ++static int minf_add_stbl(void* parent, void* child) ++{ ++ struct minf_t* minf = parent; ++ minf->stbl_ = child; ++ ++ return 1; ++} ++ ++static void* minf_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ struct minf_t* atom = minf_init(); ++ ++ struct atom_read_list_t atom_read_list[] = { ++ { FOURCC('v', 'm', 'h', 'd'), atom, &minf_add_vmhd, &vmhd_read }, ++ { FOURCC('s', 't', 'b', 'l'), atom, &minf_add_stbl, &stbl_read } ++ }; ++ ++ int result = atom_reader(atom_read_list, ++ sizeof(atom_read_list) / sizeof(atom_read_list[0]), ++ atom, ++ buffer, size); ++ ++ // check for mandatory atoms ++ if(!atom->stbl_) ++ { ++ printf("minf: missing stbl\n"); ++ result = 0; ++ } ++ ++ if(!result) ++ { ++ minf_exit(atom); ++ return 0; ++ } ++ ++ return atom; ++} ++ ++static unsigned char* minf_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct minf_t const* minf = atom; ++ struct atom_write_list_t atom_write_list[] = { ++ { FOURCC('v', 'm', 'h', 'd'), atom, minf->vmhd_, &vmhd_write }, ++ { FOURCC('s', 't', 'b', 'l'), atom, minf->stbl_, &stbl_write } ++ }; ++ ++ buffer = atom_writer(minf->unknown_atoms_, ++ atom_write_list, ++ sizeof(atom_write_list) / sizeof(atom_write_list[0]), ++ buffer); ++ ++ return buffer; ++} ++ ++static struct mdia_t* mdia_init() ++{ ++ struct mdia_t* atom = (struct mdia_t*)malloc(sizeof(struct mdia_t)); ++ atom->unknown_atoms_ = 0; ++ atom->mdhd_ = 0; ++ atom->hdlr_ = 0; ++ atom->minf_ = 0; ++ ++ return atom; ++} ++ ++static void mdia_exit(struct mdia_t* atom) ++{ ++ if(atom->unknown_atoms_) ++ { ++ unknown_atom_exit(atom->unknown_atoms_); ++ } ++ if(atom->mdhd_) ++ { ++ mdhd_exit(atom->mdhd_); ++ } ++ if(atom->hdlr_) ++ { ++ hdlr_exit(atom->hdlr_); ++ } ++ if(atom->minf_) ++ { ++ minf_exit(atom->minf_); ++ } ++ free(atom); ++} ++ ++static int mdia_add_mdhd(void* parent, void* child) ++{ ++ struct mdia_t* mdia = parent; ++ mdia->mdhd_ = child; ++ ++ return 1; ++} ++ ++static int mdia_add_hdlr(void* parent, void* child) ++{ ++ struct mdia_t* mdia = parent; ++ mdia->hdlr_ = child; ++ ++ return 1; ++} ++ ++static int mdia_add_minf(void* parent, void* child) ++{ ++ struct mdia_t* mdia = parent; ++ mdia->minf_ = child; ++ ++ return 1; ++} ++ ++static void* mdia_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ struct mdia_t* atom = mdia_init(); ++ ++ struct atom_read_list_t atom_read_list[] = { ++ { FOURCC('m', 'd', 'h', 'd'), atom, &mdia_add_mdhd, &mdhd_read }, ++ { FOURCC('h', 'd', 'l', 'r'), atom, &mdia_add_hdlr, &hdlr_read }, ++ { FOURCC('m', 'i', 'n', 'f'), atom, &mdia_add_minf, &minf_read } ++ }; ++ ++ int result = atom_reader(atom_read_list, ++ sizeof(atom_read_list) / sizeof(atom_read_list[0]), ++ atom, ++ buffer, size); ++ ++ // check for mandatory atoms ++ if(!atom->mdhd_) ++ { ++ printf("mdia: missing mdhd\n"); ++ result = 0; ++ } ++ ++ if(!atom->hdlr_) ++ { ++ printf("mdia: missing hdlr\n"); ++ result = 0; ++ } ++ ++ if(!atom->minf_) ++ { ++ printf("mdia: missing minf\n"); ++ result = 0; ++ } ++ ++ if(!result) ++ { ++ mdia_exit(atom); ++ return 0; ++ } ++ ++ return atom; ++} ++ ++static unsigned char* mdia_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct mdia_t const* mdia = atom; ++ struct atom_write_list_t atom_write_list[] = { ++ { FOURCC('m', 'd', 'h', 'd'), atom, mdia->mdhd_, &mdhd_write }, ++ { FOURCC('h', 'd', 'l', 'r'), atom, mdia->hdlr_, &hdlr_write }, ++ { FOURCC('m', 'i', 'n', 'f'), atom, mdia->minf_, &minf_write } ++ }; ++ ++ buffer = atom_writer(mdia->unknown_atoms_, ++ atom_write_list, ++ sizeof(atom_write_list) / sizeof(atom_write_list[0]), ++ buffer); ++ ++ return buffer; ++} ++ ++void trak_build_index(struct trak_t* trak) ++{ ++ struct stco_t const* stco = trak->mdia_->minf_->stbl_->stco_; ++ ++ trak->chunks_size_ = stco->entries_; ++ trak->chunks_ = malloc(trak->chunks_size_ * sizeof(struct chunks_t)); ++ ++ { ++ unsigned int i; ++ for(i = 0; i != trak->chunks_size_; ++i) ++ { ++ trak->chunks_[i].pos_ = stco->chunk_offsets_[i]; ++ } ++ } ++ ++ // process chunkmap: ++ { ++ struct stsc_t const* stsc = trak->mdia_->minf_->stbl_->stsc_; ++ unsigned int last = trak->chunks_size_; ++ unsigned int i = stsc->entries_; ++ while(i > 0) ++ { ++ unsigned int j; ++ ++ --i; ++ ++ for(j = stsc->table_[i].chunk_; j < last; j++) ++ { ++ trak->chunks_[j].id_ = stsc->table_[i].id_; ++ trak->chunks_[j].size_ = stsc->table_[i].samples_; ++ } ++ last = stsc->table_[i].chunk_; ++ } ++ } ++ ++ // calc pts of chunks: ++ { ++ struct stsz_t const* stsz = trak->mdia_->minf_->stbl_->stsz_; ++ unsigned int sample_size = stsz->sample_size_; ++ unsigned int s = 0; ++ { ++ unsigned int j; ++ for(j = 0; j < trak->chunks_size_; j++) ++ { ++ trak->chunks_[j].sample_ = s; ++ s += trak->chunks_[j].size_; ++ } ++ } ++ ++ if(sample_size == 0) ++ { ++ trak->samples_size_ = stsz->entries_; ++ } ++ else ++ { ++ trak->samples_size_ = s; ++ } ++ ++ trak->samples_ = malloc(trak->samples_size_ * sizeof(struct samples_t)); ++ ++ if(sample_size == 0) ++ { ++ unsigned int i; ++ for(i = 0; i != trak->samples_size_ ; ++i) ++ trak->samples_[i].size_ = stsz->sample_sizes_[i]; ++ } ++ else ++ { ++ unsigned int i; ++ for(i = 0; i != trak->samples_size_ ; ++i) ++ trak->samples_[i].size_ = sample_size; ++ } ++ } ++ ++// i = 0; ++// for (j = 0; j < trak->durmap_size; j++) ++// i += trak->durmap[j].num; ++// if (i != s) { ++// mp_msg(MSGT_DEMUX, MSGL_WARN, ++// "MOV: durmap and chunkmap sample count differ (%i vs %i)\n", i, s); ++// if (i > s) s = i; ++// } ++ ++ // calc pts: ++ { ++ struct stts_t const* stts = trak->mdia_->minf_->stbl_->stts_; ++ unsigned int s = 0; ++ unsigned int pts = 0; ++ unsigned int entries = stts->entries_; ++ unsigned int j; ++ for(j = 0; j < entries; j++) ++ { ++ unsigned int i; ++ unsigned int sample_count = stts->table_[j].sample_count_; ++ unsigned int sample_duration = stts->table_[j].sample_duration_; ++ for(i = 0; i < sample_count; i++) ++ { ++ trak->samples_[s].pts_ = pts; ++ ++s; ++ pts += sample_duration; ++ } ++ } ++ } ++ ++ // calc composition times: ++ { ++ struct ctts_t const* ctts = trak->mdia_->minf_->stbl_->ctts_; ++ if(ctts) ++ { ++ unsigned int s = 0; ++ unsigned int entries = ctts->entries_; ++ unsigned int j; ++ for(j = 0; j != entries; j++) ++ { ++ unsigned int i; ++ unsigned int sample_count = ctts->table_[j].sample_count_; ++ unsigned int sample_offset = ctts->table_[j].sample_offset_; ++ for(i = 0; i < sample_count; i++) ++ { ++ trak->samples_[s].cto_ = sample_offset; ++ ++s; ++ } ++ } ++ } ++ } ++ ++ // calc sample offsets ++ { ++ unsigned int s = 0; ++ unsigned int j; ++ for(j = 0; j != trak->chunks_size_; j++) ++ { ++ uint64_t pos = trak->chunks_[j].pos_; ++ unsigned int i; ++ for(i = 0; i != trak->chunks_[j].size_; i++) ++ { ++ trak->samples_[s].pos_ = pos; ++ pos += trak->samples_[s].size_; ++ ++s; ++ } ++ } ++ } ++} ++ ++void trak_update_index(struct trak_t* trak, unsigned int start, unsigned int end) ++{ ++ // write samples [start,end> ++ ++ // stts = [entries * [sample_count, sample_duration] ++ { ++ struct stts_t* stts = trak->mdia_->minf_->stbl_->stts_; ++ ++ unsigned int entries = 0; ++ unsigned int s; ++ ++ for(s = start; s != end; ++s) ++ { ++ unsigned int sample_count = 1; ++ unsigned int sample_duration = ++ trak->samples_[s + 1].pts_ - trak->samples_[s].pts_; ++ while(s != end - 1) ++ { ++ if((trak->samples_[s + 1].pts_ - trak->samples_[s].pts_) != sample_duration) ++ break; ++ ++sample_count; ++ ++s; ++ } ++ stts->table_[entries].sample_count_ = sample_count; ++ stts->table_[entries].sample_duration_ = sample_duration; ++ ++entries; ++ } ++ stts->entries_ = entries; ++ ++ if(stts_get_samples(stts) != end - start) ++ { ++ printf("ERROR: stts_get_samples=%d, should be %d\n", ++ stts_get_samples(stts), end - start); ++ } ++ } ++ ++ // ctts = [entries * [sample_count, sample_offset] ++ { ++ struct ctts_t* ctts = trak->mdia_->minf_->stbl_->ctts_; ++ if(ctts) ++ { ++ unsigned int entries = 0; ++ unsigned int s; ++ ++ for(s = start; s != end; ++s) ++ { ++ unsigned int sample_count = 1; ++ unsigned int sample_offset = trak->samples_[s].cto_; ++ while(s != end - 1) ++ { ++ if(trak->samples_[s + 1].cto_ != sample_offset) ++ break; ++ ++sample_count; ++ ++s; ++ } ++ // write entry ++ ctts->table_[entries].sample_count_ = sample_count; ++ ctts->table_[entries].sample_offset_ = sample_offset; ++ ++entries; ++ } ++ ctts->entries_ = entries; ++ if(ctts_get_samples(ctts) != end - start) ++ { ++ printf("ERROR: ctts_get_samples=%d, should be %d\n", ++ ctts_get_samples(ctts), end - start); ++ } ++ } ++ } ++ ++ // process chunkmap: ++ { ++ struct stsc_t* stsc = trak->mdia_->minf_->stbl_->stsc_; ++ unsigned int i; ++ ++ for(i = 0; i != trak->chunks_size_; ++i) ++ { ++ if(trak->chunks_[i].sample_ + trak->chunks_[i].size_ > start) ++ break; ++ } ++ ++ { ++ unsigned int stsc_entries = 0; ++ unsigned int chunk_start = i; ++ unsigned int chunk_end; ++ // problem.mp4: reported by Jin-seok Lee. Second track contains no samples ++ if(trak->chunks_size_ != 0) ++ { ++ unsigned int samples = ++ trak->chunks_[i].sample_ + trak->chunks_[i].size_ - start; ++ unsigned int id = trak->chunks_[i].id_; ++ ++ // write entry [chunk,samples,id] ++ stsc->table_[stsc_entries].chunk_ = 0; ++ stsc->table_[stsc_entries].samples_ = samples; ++ stsc->table_[stsc_entries].id_ = id; ++ ++stsc_entries; ++ ++ if(i != trak->chunks_size_) ++ { ++ for(i += 1; i != trak->chunks_size_; ++i) ++ { ++ if(trak->chunks_[i].sample_ >= end) ++ break; ++ ++ if(trak->chunks_[i].size_ != samples) ++ { ++ samples = trak->chunks_[i].size_; ++ id = trak->chunks_[i].id_; ++ ++ stsc->table_[stsc_entries].chunk_ = i - chunk_start; ++ stsc->table_[stsc_entries].samples_ = samples; ++ stsc->table_[stsc_entries].id_ = id; ++ ++stsc_entries; ++ } ++ } ++ } ++ } ++ chunk_end = i; ++ stsc->entries_ = stsc_entries; ++ ++ { ++ struct stco_t* stco = trak->mdia_->minf_->stbl_->stco_; ++ unsigned int entries = 0; ++ for(i = chunk_start; i != chunk_end; ++i) ++ { ++ stco->chunk_offsets_[entries] = stco->chunk_offsets_[i]; ++ ++entries; ++ } ++ stco->entries_ = entries; ++ ++ // patch first chunk with correct sample offset ++ stco->chunk_offsets_[0] = (uint32_t)trak->samples_[start].pos_; ++ } ++ } ++ } ++ ++ // process sync samples: ++ if(trak->mdia_->minf_->stbl_->stss_) ++ { ++ struct stss_t* stss = trak->mdia_->minf_->stbl_->stss_; ++ unsigned int entries = 0; ++ unsigned int stss_start; ++ unsigned int i; ++ ++ for(i = 0; i != stss->entries_; ++i) ++ { ++ if(stss->sample_numbers_[i] >= start + 1) ++ break; ++ } ++ stss_start = i; ++ for(; i != stss->entries_; ++i) ++ { ++ unsigned int sync_sample = stss->sample_numbers_[i]; ++ if(sync_sample >= end + 1) ++ break; ++ stss->sample_numbers_[entries] = sync_sample - start; ++ ++entries; ++ } ++ stss->entries_ = entries; ++ } ++ ++ // process sample sizes ++ { ++ struct stsz_t* stsz = trak->mdia_->minf_->stbl_->stsz_; ++ ++ if(stsz->sample_size_ == 0) ++ { ++ unsigned int entries = 0; ++ unsigned int i; ++ for(i = start; i != end; ++i) ++ { ++ stsz->sample_sizes_[entries] = stsz->sample_sizes_[i]; ++ ++entries; ++ } ++ stsz->entries_ = entries; ++ } ++ } ++} ++ ++static struct trak_t* trak_init() ++{ ++ struct trak_t* trak = (struct trak_t*)malloc(sizeof(struct trak_t)); ++ trak->unknown_atoms_ = 0; ++ trak->tkhd_ = 0; ++ trak->mdia_ = 0; ++ trak->chunks_size_ = 0; ++ trak->chunks_ = 0; ++ trak->samples_size_ = 0; ++ trak->samples_ = 0; ++ ++ return trak; ++} ++ ++static void trak_exit(struct trak_t* trak) ++{ ++ if(trak->unknown_atoms_) ++ { ++ unknown_atom_exit(trak->unknown_atoms_); ++ } ++ if(trak->tkhd_) ++ { ++ tkhd_exit(trak->tkhd_); ++ } ++ if(trak->mdia_) ++ { ++ mdia_exit(trak->mdia_); ++ } ++ if(trak->chunks_) ++ { ++ free(trak->chunks_); ++ } ++ if(trak->samples_) ++ { ++ free(trak->samples_); ++ } ++ free(trak); ++} ++ ++static int trak_add_tkhd(void* parent, void* tkhd) ++{ ++ struct trak_t* trak = parent; ++ trak->tkhd_ = tkhd; ++ ++ return 1; ++} ++ ++static int trak_add_mdia(void* parent, void* mdia) ++{ ++ struct trak_t* trak = parent; ++ trak->mdia_ = mdia; ++ ++ return 1; ++} ++ ++static void* trak_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ struct trak_t* atom = trak_init(); ++ ++ struct atom_read_list_t atom_read_list[] = { ++ { FOURCC('t', 'k', 'h', 'd'), atom, &trak_add_tkhd, &tkhd_read }, ++ { FOURCC('m', 'd', 'i', 'a'), atom, &trak_add_mdia, &mdia_read } ++ }; ++ ++ int result = atom_reader(atom_read_list, ++ sizeof(atom_read_list) / sizeof(atom_read_list[0]), ++ atom, ++ buffer, size); ++ ++ // check for mandatory atoms ++ if(!atom->tkhd_) ++ { ++ printf("trak: missing tkhd\n"); ++ result = 0; ++ } ++ ++ if(!atom->mdia_) ++ { ++ printf("trak: missing mdia\n"); ++ result = 0; ++ } ++ ++ if(!result) ++ { ++ trak_exit(atom); ++ return 0; ++ } ++ ++ trak_build_index(atom); ++ ++ return atom; ++} ++ ++static unsigned char* trak_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct trak_t* trak = atom; ++ struct atom_write_list_t atom_write_list[] = { ++ { FOURCC('t', 'k', 'h', 'd'), atom, trak->tkhd_, &tkhd_write }, ++ { FOURCC('m', 'd', 'i', 'a'), atom, trak->mdia_, &mdia_write } ++ }; ++ ++ buffer = atom_writer(trak->unknown_atoms_, ++ atom_write_list, ++ sizeof(atom_write_list) / sizeof(atom_write_list[0]), ++ buffer); ++ ++ return buffer; ++} ++ ++void trak_shift_offsets(struct trak_t* trak, int64_t offset) ++{ ++ struct stco_t* stco = trak->mdia_->minf_->stbl_->stco_; ++ stco_shift_offsets(stco, (int32_t)offset); ++} ++ ++void trak_shift_offsets_inplace(struct trak_t* trak, int64_t offset) ++{ ++ void* stco = trak->mdia_->minf_->stbl_->stco_inplace_; ++ stco_shift_offsets_inplace(stco, (int32_t)offset); ++} ++ ++static struct mvhd_t* mvhd_init() ++{ ++ struct mvhd_t* atom = (struct mvhd_t*)malloc(sizeof(struct mvhd_t)); ++ ++ return atom; ++} ++ ++void mvhd_exit(struct mvhd_t* atom) ++{ ++ free(atom); ++} ++ ++static void* mvhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ unsigned int i; ++ ++ struct mvhd_t* atom = mvhd_init(); ++ atom->version_ = read_8(buffer + 0); ++ atom->flags_ = read_24(buffer + 1); ++ if(atom->version_ == 0) ++ { ++ if(size < 108-8) ++ return 0; ++ ++ atom->creation_time_ = read_32(buffer + 4); ++ atom->modification_time_ = read_32(buffer + 8); ++ atom->timescale_ = read_32(buffer + 12); ++ atom->duration_ = read_32(buffer + 16); ++ buffer += 20; ++ } ++ else ++ { ++ if(size < 120-8) ++ return 0; ++ ++ atom->creation_time_ = read_64(buffer + 4); ++ atom->modification_time_ = read_64(buffer + 12); ++ atom->timescale_ = read_32(buffer + 20); ++ atom->duration_ = read_64(buffer + 24); ++ buffer += 32; ++ } ++ atom->rate_ = read_32(buffer + 0); ++ atom->volume_ = read_16(buffer + 4); ++ atom->reserved1_ = read_16(buffer + 6); ++ atom->reserved2_[0] = read_32(buffer + 8); ++ atom->reserved2_[1] = read_32(buffer + 12); ++ buffer += 16; ++ ++ for(i = 0; i != 9; ++i) ++ { ++ atom->matrix_[i] = read_32(buffer); ++ buffer += 4; ++ } ++ ++ for(i = 0; i != 6; ++i) ++ { ++ atom->predefined_[i] = read_32(buffer); ++ buffer += 4; ++ } ++ ++ atom->next_track_id_ = read_32(buffer + 0); ++ ++ return atom; ++} ++ ++static unsigned char* mvhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer) ++{ ++ struct mvhd_t const* mvhd = atom; ++ unsigned int i; ++ ++ buffer = write_8(buffer, mvhd->version_); ++ buffer = write_24(buffer, mvhd->flags_); ++ ++ if(mvhd->version_ == 0) ++ { ++ buffer = write_32(buffer, (uint32_t)mvhd->creation_time_); ++ buffer = write_32(buffer, (uint32_t)mvhd->modification_time_); ++ buffer = write_32(buffer, mvhd->timescale_); ++ buffer = write_32(buffer, (uint32_t)mvhd->duration_); ++ } ++ else ++ { ++ buffer = write_64(buffer, mvhd->creation_time_); ++ buffer = write_64(buffer, mvhd->modification_time_); ++ buffer = write_32(buffer, mvhd->timescale_); ++ buffer = write_64(buffer, mvhd->duration_); ++ } ++ ++ buffer = write_32(buffer, mvhd->rate_); ++ buffer = write_16(buffer, mvhd->volume_); ++ buffer = write_16(buffer, mvhd->reserved1_); ++ buffer = write_32(buffer, mvhd->reserved2_[0]); ++ buffer = write_32(buffer, mvhd->reserved2_[1]); ++ ++ for(i = 0; i != 9; ++i) ++ { ++ buffer = write_32(buffer, mvhd->matrix_[i]); ++ } ++ ++ for(i = 0; i != 6; ++i) ++ { ++ buffer = write_32(buffer, mvhd->predefined_[i]); ++ } ++ ++ buffer = write_32(buffer, mvhd->next_track_id_); ++ ++ return buffer; ++} ++ ++static struct moov_t* moov_init() ++{ ++ struct moov_t* moov = malloc(sizeof(struct moov_t)); ++ moov->unknown_atoms_ = 0; ++ moov->mvhd_ = 0; ++ moov->tracks_ = 0; ++ ++ return moov; ++} ++ ++static void moov_exit(struct moov_t* atom) ++{ ++ unsigned int i; ++ if(atom->unknown_atoms_) ++ { ++ unknown_atom_exit(atom->unknown_atoms_); ++ } ++ if(atom->mvhd_) ++ { ++ mvhd_exit(atom->mvhd_); ++ } ++ for(i = 0; i != atom->tracks_; ++i) ++ { ++ trak_exit(atom->traks_[i]); ++ } ++ free(atom); ++} ++ ++static int moov_add_mvhd(void* parent, void* mvhd) ++{ ++ struct moov_t* moov = parent; ++ moov->mvhd_ = mvhd; ++ ++ return 1; ++} ++ ++static int moov_add_trak(void* parent, void* child) ++{ ++ struct moov_t* moov = parent; ++ struct trak_t* trak = child; ++ if(moov->tracks_ == MAX_TRACKS) ++ { ++ trak_exit(trak); ++ return 0; ++ } ++ ++ if(trak->mdia_->hdlr_->handler_type_ != FOURCC('v', 'i', 'd', 'e') && ++ trak->mdia_->hdlr_->handler_type_ != FOURCC('s', 'o', 'u', 'n')) ++ { ++ printf("Trak ignored (handler_type=%c%c%c%c, name=%s)\n", ++ trak->mdia_->hdlr_->handler_type_ >> 24, ++ trak->mdia_->hdlr_->handler_type_ >> 16, ++ trak->mdia_->hdlr_->handler_type_ >> 8, ++ trak->mdia_->hdlr_->handler_type_, ++ trak->mdia_->hdlr_->name_); ++ trak_exit(trak); ++ return 1; // continue ++ } ++ ++ moov->traks_[moov->tracks_] = trak; ++ ++moov->tracks_; ++ ++ return 1; ++} ++ ++static void* moov_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) ++{ ++ struct moov_t* atom = moov_init(); ++ ++ struct atom_read_list_t atom_read_list[] = { ++ { FOURCC('m', 'v', 'h', 'd'), atom, &moov_add_mvhd, &mvhd_read }, ++ { FOURCC('t', 'r', 'a', 'k'), atom, &moov_add_trak, &trak_read } ++ }; ++ ++ int result = atom_reader(atom_read_list, ++ sizeof(atom_read_list) / sizeof(atom_read_list[0]), ++ atom, ++ buffer, size); ++ ++ // check for mandatory atoms ++ if(!atom->mvhd_) ++ { ++ printf("moov: missing mvhd\n"); ++ result = 0; ++ } ++ ++ if(!atom->tracks_) ++ { ++ printf("moov: missing trak\n"); ++ result = 0; ++ } ++ ++ if(!result) ++ { ++ moov_exit(atom); ++ return 0; ++ } ++ ++ return atom; ++} ++ ++static void moov_write(struct moov_t* atom, unsigned char* buffer) ++{ ++ unsigned i; ++ ++ unsigned char* atom_start = buffer; ++ ++ struct atom_write_list_t atom_write_list[] = { ++ { FOURCC('m', 'v', 'h', 'd'), atom, atom->mvhd_, &mvhd_write }, ++ }; ++ ++ // atom size ++ buffer += 4; ++ ++ // atom type ++ buffer = write_32(buffer, FOURCC('m', 'o', 'o', 'v')); ++ ++ buffer = atom_writer(atom->unknown_atoms_, ++ atom_write_list, ++ sizeof(atom_write_list) / sizeof(atom_write_list[0]), ++ buffer); ++ ++ for(i = 0; i != atom->tracks_; ++i) ++ { ++ struct atom_write_list_t trak_atom_write_list[] = { ++ { FOURCC('t', 'r', 'a', 'k'), atom, atom->traks_[i], &trak_write }, ++ }; ++ buffer = atom_writer(0, ++ trak_atom_write_list, ++ sizeof(trak_atom_write_list) / sizeof(trak_atom_write_list[0]), ++ buffer); ++ } ++ write_32(atom_start, buffer - atom_start); ++} ++ ++void moov_shift_offsets(struct moov_t* moov, int64_t offset) ++{ ++ unsigned int i; ++ for(i = 0; i != moov->tracks_; ++i) ++ { ++ trak_shift_offsets(moov->traks_[i], offset); ++ } ++} ++ ++void moov_shift_offsets_inplace(struct moov_t* moov, int64_t offset) ++{ ++ unsigned int i; ++ for(i = 0; i != moov->tracks_; ++i) ++ { ++ trak_shift_offsets_inplace(moov->traks_[i], offset); ++ } ++} ++ ++unsigned int moov_seek(unsigned char* moov_data, ++ uint64_t* moov_size, ++ float start_time, ++ float end_time, ++ uint64_t* mdat_start, ++ uint64_t* mdat_size, ++ uint64_t offset, ++ int client_is_flash) ++{ ++ struct moov_t* moov = moov_read(NULL, moov_data + ATOM_PREAMBLE_SIZE, ++ *moov_size - ATOM_PREAMBLE_SIZE); ++ ++ if(moov == 0 || moov->mvhd_ == 0) ++ { ++ printf("Error parsing moov header\n"); ++ return 0; ++ } ++ ++ { ++ long moov_time_scale = moov->mvhd_->timescale_; ++ unsigned int start = (unsigned int)(start_time * moov_time_scale); ++ unsigned int end = (unsigned int)(end_time * moov_time_scale); ++ uint64_t skip_from_start = UINT64_MAX; ++ uint64_t end_offset = 0; ++ unsigned int i; ++ unsigned int pass; ++ ++ // for every trak, convert seconds to sample (time-to-sample). ++ // adjust sample to keyframe ++ unsigned int trak_sample_start[MAX_TRACKS]; ++ unsigned int trak_sample_end[MAX_TRACKS]; ++ ++ uint64_t moov_duration = 0; ++ ++ // clayton.mp4 has a third track with one sample that lasts the whole clip. ++ // Assuming the first two tracks are the audio and video track, we patch ++ // the remaining tracks to 'free' atoms. ++// if(moov->tracks_ > 2) ++// { ++// for(i = 2; i != moov->tracks_; ++i) ++// { ++// // patch 'trak' to 'free' ++// unsigned char* p = moov->traks_[i].start_ - 4; ++// p[0] = 'f'; ++// p[1] = 'r'; ++// p[2] = 'e'; ++// p[3] = 'e'; ++// } ++// moov->tracks_ = 2; ++// } ++ ++ // reported by everwanna: ++ // av out of sync because: ++ // audio track 0 without stss, seek to the exact time. ++ // video track 1 with stss, seek to the nearest key frame time. ++ // ++ // fixed: ++ // first pass we get the new aligned times for traks with an stss present ++ // second pass is for traks without an stss ++ for(pass = 0; pass != 2; ++pass) ++ { ++ for(i = 0; i != moov->tracks_; ++i) ++ { ++ struct trak_t* trak = moov->traks_[i]; ++ struct stbl_t* stbl = trak->mdia_->minf_->stbl_; ++ long trak_time_scale = trak->mdia_->mdhd_->timescale_; ++ float moov_to_trak_time = (float)trak_time_scale / (float)moov_time_scale; ++ float trak_to_moov_time = (float)moov_time_scale / (float)trak_time_scale; ++ ++ // 1st pass: stss present, 2nd pass: no stss present ++ if(pass == 0 && !stbl->stss_) ++ continue; ++ if(pass == 1 && stbl->stss_) ++ continue; ++ ++ // ignore empty track ++ if(trak->mdia_->mdhd_->duration_ == 0) ++ continue; ++ ++ // get start ++ if(start == 0) ++ { ++ trak_sample_start[i] = start; ++ } ++ else ++ { ++ start = stts_get_sample(stbl->stts_, (uint64_t)(start * moov_to_trak_time)); ++ printf("start=%u (trac time)=%.2f (seconds)", start, ++ stts_get_time(stbl->stts_, start) / (float)trak_time_scale); ++ start = stbl_get_nearest_keyframe(stbl, start + 1) - 1; ++ printf("=%u (zero based keyframe)", start); ++ trak_sample_start[i] = start; ++ start = (unsigned int)(stts_get_time(stbl->stts_, start) * trak_to_moov_time); ++ printf("=%u (moov time)\n", start); ++ } ++ ++ // get end ++ if(end == 0) ++ { ++ trak_sample_end[i] = trak->samples_size_; ++ } ++ else ++ { ++ end = stts_get_sample(stbl->stts_, (uint64_t)(end * moov_to_trak_time)); ++ if(end >= trak->samples_size_) ++ { ++ end = trak->samples_size_; ++ } ++ else ++ { ++ end = stbl_get_nearest_keyframe(stbl, end + 1) - 1; ++ } ++ trak_sample_end[i] = end; ++ printf("endframe=%u, samples_size_=%u\n", end, trak->samples_size_); ++ end = (unsigned int)(stts_get_time(stbl->stts_, end) * trak_to_moov_time); ++ } ++ } ++ } ++ ++ printf("start=%u\n", start); ++ printf("end=%u\n", end); ++ ++ if(end && start >= end) ++ { ++ moov_exit(moov); ++ return 0; ++ } ++ ++ for(i = 0; i != moov->tracks_; ++i) ++ { ++ struct trak_t* trak = moov->traks_[i]; ++ struct stbl_t* stbl = trak->mdia_->minf_->stbl_; ++ ++ unsigned int start_sample = trak_sample_start[i]; ++ unsigned int end_sample = trak_sample_end[i]; ++ ++ // ignore empty track ++ if(trak->mdia_->mdhd_->duration_ == 0) ++ continue; ++ ++ trak_update_index(trak, start_sample, end_sample); ++ ++ { ++ uint64_t skip = ++ trak->samples_[start_sample].pos_ - trak->samples_[0].pos_; ++ if(skip < skip_from_start) ++ skip_from_start = skip; ++ printf("Trak can skip %llu bytes\n", skip); ++ ++ if(end_sample != trak->samples_size_) ++ { ++ uint64_t end_pos = trak->samples_[end_sample].pos_; ++ if(end_pos > end_offset) ++ end_offset = end_pos; ++ printf("New endpos=%llu\n", end_pos); ++ printf("Trak can skip %llu bytes at end\n", ++ *mdat_start + *mdat_size - end_offset); ++ } ++ } ++ ++ { ++ // fixup trak (duration) ++ uint64_t trak_duration = stts_get_duration(stbl->stts_); ++ long trak_time_scale = trak->mdia_->mdhd_->timescale_; ++ float trak_to_moov_time = (float)moov_time_scale / (float)trak_time_scale; ++ { ++ uint64_t duration = (long)((float)trak_duration * trak_to_moov_time); ++ trak->mdia_->mdhd_->duration_= trak_duration; ++ trak->tkhd_->duration_ = duration; ++ printf("trak: new_duration=%lld\n", duration); ++ ++ if(duration > moov_duration) ++ moov_duration = duration; ++ } ++ } ++ ++// printf("stco.size=%d, ", read_int32(stbl->stco_ + 4)); ++// printf("stts.size=%d samples=%d\n", read_int32(stbl->stts_ + 4), stts_get_samples(stbl->stts_)); ++// printf("stsz.size=%d\n", read_int32(stbl->stsz_ + 8)); ++// printf("stsc.samples=%d\n", stsc_get_samples(stbl->stsc_)); ++ } ++ moov->mvhd_->duration_ = moov_duration; ++ ++ // subtract bytes we skip at the front of the mdat atom ++ offset -= skip_from_start; ++ ++ // subtract old moov size ++ offset -= *moov_size; ++ ++ printf("moov: writing header\n"); ++ ++ moov_write(moov, moov_data); ++ *moov_size = read_32(moov_data); ++ ++ // add new moov size ++ offset += *moov_size; ++ ++ printf("shifting offsets by %lld\n", offset); ++ moov_shift_offsets_inplace(moov, offset); ++ ++// moov_write(moov, moov_data); ++ ++#ifdef COMPRESS_MOOV_ATOM ++ if(!client_is_flash) ++ { ++ uLong sourceLen = *moov_size - ATOM_PREAMBLE_SIZE; ++ uLong destLen = compressBound(sourceLen); ++ unsigned char* cmov = malloc(destLen); ++ int zstatus = compress(cmov, &destLen, moov_data, sourceLen); ++ if(zstatus == Z_OK) ++ { ++ printf("cmov size = %lu (%ld%%)\n", destLen, 100 * destLen / sourceLen); ++ } ++ ++ { ++ const int extra_space = 4096; ++ if(destLen + extra_space < sourceLen) ++ { ++ const int bytes_saved = sourceLen - destLen; ++ uLong destLen2; ++ int extra = 0; ++ printf("shifting offsets by %d\n", -bytes_saved); ++ moov_shift_offsets_inplace(moov, -bytes_saved); ++ ++ extra += ATOM_PREAMBLE_SIZE + 4; // dcom ++ extra += ATOM_PREAMBLE_SIZE + 4; // cmvd ++ extra += ATOM_PREAMBLE_SIZE; // cmov ++ extra += ATOM_PREAMBLE_SIZE + extra_space; // free ++ ++ printf("shifting offsets by %d\n", extra); ++ moov_shift_offsets_inplace(moov, extra); ++ ++ // recompress ++ destLen2 = compressBound(sourceLen); ++ zstatus = compress(cmov, &destLen2, moov_data, sourceLen); ++ if(zstatus == Z_OK) ++ { ++ printf("cmov size = %lu (%ld%%)\n", destLen2, 100 * destLen2 / sourceLen); ++ ++ if(destLen2 < destLen + extra_space) ++ { ++ // copy compressed movie atom ++ unsigned char* outbuffer = moov_data; ++ ++ uint32_t dcom_size = ATOM_PREAMBLE_SIZE + 4; ++ uint32_t cmvd_size = ATOM_PREAMBLE_SIZE + 4 + destLen2; ++ uint32_t cmov_size = ATOM_PREAMBLE_SIZE + dcom_size + cmvd_size; ++ uint32_t free_size = ATOM_PREAMBLE_SIZE + extra_space + destLen - destLen2; ++ *moov_size = ATOM_PREAMBLE_SIZE + cmov_size + free_size; ++ ++ outbuffer = write_32(outbuffer, (uint32_t)*moov_size); ++ ++ // skip 'moov' ++ outbuffer += 4; ++ ++ outbuffer = write_32(outbuffer, cmov_size); ++ { ++ outbuffer = write_32(outbuffer, FOURCC('c', 'm', 'o', 'v')); ++ outbuffer = write_32(outbuffer, dcom_size); ++ outbuffer = write_32(outbuffer, FOURCC('d', 'c', 'o', 'm')); ++ outbuffer = write_32(outbuffer, FOURCC('z', 'l', 'i', 'b')); ++ ++ outbuffer = write_32(outbuffer, cmvd_size); ++ { ++ outbuffer = write_32(outbuffer, FOURCC('c', 'm', 'v', 'd')); ++ outbuffer = write_32(outbuffer, sourceLen); ++ memcpy(outbuffer, cmov, destLen2); ++ outbuffer += destLen2; ++ } ++ } ++ ++ // add final padding ++ outbuffer = write_32(outbuffer, free_size); ++ outbuffer = write_32(outbuffer, FOURCC('f', 'r', 'e', 'e')); ++ { ++ const char free_bytes[8] = ++ { ++ 'C', 'o', 'd', 'e','S','h', 'o', 'p' ++ }; ++ uint32_t padding_index; ++ for(padding_index = ATOM_PREAMBLE_SIZE; padding_index != free_size; ++padding_index) ++ { ++ outbuffer[padding_index] = free_bytes[padding_index % 8]; ++ } ++ } ++ } ++ else ++ { ++ printf("2nd pass compress overflow\n"); ++ } ++ } ++ } ++ } ++ free(cmov); ++ } ++#endif ++ ++ *mdat_start += skip_from_start; ++ if(end_offset != 0) ++ { ++ *mdat_size = end_offset; ++ } ++ *mdat_size -= skip_from_start; ++ } ++ ++ moov_exit(moov); ++ ++ return 1; ++} ++ ++//////////////////////////////////////////////////////////////////////////////// ++ ++struct mp4_atom_t ++{ ++ uint32_t type_; ++ uint32_t short_size_; ++ uint64_t size_; ++ uint64_t start_; ++ uint64_t end_; ++}; ++ ++static int mp4_atom_read_header(FILE* infile, struct mp4_atom_t* atom) ++{ ++ unsigned char atom_header[8]; ++ ++ atom->start_ = ftell(infile); ++ fread(atom_header, 8, 1, infile); ++ atom->short_size_ = read_32(&atom_header[0]); ++ atom->type_ = read_32(&atom_header[4]); ++ ++ if(atom->short_size_ == 1) ++ { ++ fread(atom_header, 8, 1, infile); ++ atom->size_ = read_64(&atom_header[0]); ++ } ++ else ++ { ++ atom->size_ = atom->short_size_; ++ } ++ ++ atom->end_ = atom->start_ + atom->size_; ++ ++ return 1; ++} ++ ++static int mp4_atom_write_header(unsigned char* outbuffer, struct mp4_atom_t* atom) ++{ ++ int write_box64 = atom->short_size_ == 1 ? 1 : 0; ++ ++ if(write_box64) ++ write_32(outbuffer, 1); ++ else ++ write_32(outbuffer, (uint32_t)atom->size_); ++ ++ write_32(outbuffer + 4, atom->type_); ++ ++ if(write_box64) ++ { ++ write_64(outbuffer + 8, atom->size_); ++ return 16; ++ } ++ else ++ { ++ return 8; ++ } ++} ++ ++int mp4_split(const char* filename, int64_t filesize, ++ float start_time, float end_time, ++ void** mp4_header, uint32_t* mp4_header_size, ++ uint64_t* mdat_offset, uint64_t* mdat_size, ++ int client_is_flash) ++{ ++ FILE* infile; ++ struct mp4_atom_t ftyp_atom; ++ struct mp4_atom_t moov_atom; ++ struct mp4_atom_t mdat_atom; ++ unsigned char* moov_data = 0; ++ unsigned char* buffer; ++ uint64_t new_mdat_start; ++ ++ *mp4_header = 0; ++ memset(&ftyp_atom, 0, sizeof(ftyp_atom)); ++ memset(&moov_atom, 0, sizeof(moov_atom)); ++ memset(&mdat_atom, 0, sizeof(mdat_atom)); ++ ++ infile = fopen(filename, "rb"); ++ if(infile == NULL) ++ { ++ return 0; ++ } ++ ++ while(ftello(infile) < filesize) ++ { ++ struct mp4_atom_t leaf_atom; ++ ++ if(!mp4_atom_read_header(infile, &leaf_atom)) ++ break; ++ ++ printf("Atom(%c%c%c%c,%lld)\n", ++ leaf_atom.type_ >> 24, leaf_atom.type_ >> 16, ++ leaf_atom.type_ >> 8, leaf_atom.type_, ++ leaf_atom.size_); ++ ++ switch(leaf_atom.type_) ++ { ++ case FOURCC('f', 't', 'y', 'p'): ++ ftyp_atom = leaf_atom; ++ break; ++ case FOURCC('m', 'o', 'o', 'v'): ++ moov_atom = leaf_atom; ++ moov_data = (unsigned char*)malloc((size_t)moov_atom.size_); ++ fseeko(infile, moov_atom.start_, SEEK_SET); ++ fread(moov_data, (off_t)moov_atom.size_, 1, infile); ++ break; ++ case FOURCC('m', 'd', 'a', 't'): ++ mdat_atom = leaf_atom; ++ break; ++ } ++ fseeko(infile, leaf_atom.end_, SEEK_SET); ++ } ++ ++ if(moov_atom.size_ == 0) ++ { ++ printf("Error: moov atom not found\n"); ++ fclose(infile); ++ return 0; ++ } ++ ++ if(mdat_atom.size_ == 0) ++ { ++ printf("Error: mdat atom not found\n"); ++ fclose(infile); ++ return 0; ++ } ++ ++ buffer = (unsigned char*)malloc((uint32_t)moov_atom.size_ + 4 * 1024); ++ *mp4_header = buffer; ++ ++ if(ftyp_atom.size_) ++ { ++ fseeko(infile, ftyp_atom.start_, SEEK_SET); ++ fread(buffer, (off_t)ftyp_atom.size_, 1, infile); ++ buffer += ftyp_atom.size_; ++ } ++ ++ { ++ static char const free_data[] = { ++ 0x0, 0x0, 0x0, 42, 'f', 'r', 'e', 'e', ++ 'v', 'i', 'd', 'e', 'o', ' ', 's', 'e', ++ 'r', 'v', 'e', 'd', ' ', 'b', 'y', ' ', ++ 'm', 'o', 'd', '_', 'h', '2', '6', '4', ++ '_', 's', 't', 'r', 'e', 'a', 'm', 'i', ++ 'n', 'g' ++ }; ++ memcpy(buffer, free_data, sizeof(free_data)); ++ buffer += sizeof(free_data); ++ } ++ ++ new_mdat_start = buffer - (unsigned char*)(*mp4_header) + moov_atom.size_; ++ if(!moov_seek(moov_data, ++ &moov_atom.size_, ++ start_time, ++ end_time, ++ &mdat_atom.start_, ++ &mdat_atom.size_, ++ new_mdat_start - mdat_atom.start_, ++ client_is_flash)) ++ { ++ free(moov_data); ++ fclose(infile); ++ return 0; ++ } ++ ++ memcpy(buffer, moov_data, (uint32_t)moov_atom.size_); ++ buffer += moov_atom.size_; ++ free(moov_data); ++ ++ { ++ int mdat_header_size = mp4_atom_write_header(buffer, &mdat_atom); ++ buffer += mdat_header_size; ++ *mdat_offset = mdat_atom.start_ + mdat_header_size; ++ *mdat_size = mdat_atom.size_ - mdat_header_size; ++ } ++ ++ *mp4_header_size = (uint32_t)(buffer - (unsigned char*)(*mp4_header)); ++ ++ fclose(infile); ++ ++ return 1; ++} ++ ++// End Of File ++ +--- /dev/null 2008-11-04 20:33:38.146691408 +0200 ++++ lighttpd-1.4.18/src/moov.h 2009-01-26 21:00:05.071936866 +0200 +@@ -0,0 +1,49 @@ ++/******************************************************************************* ++ moov.h (version 2) ++ ++ moov - A library for splitting Quicktime/MPEG4 files. ++ http://h264.code-shop.com ++ ++ Copyright (C) 2007-2009 CodeShop B.V. ++ ++ Licensing ++ The H264 Streaming Module is licened under a Creative Common License. It allows ++ you to use, modify and redistribute the module, but only for *noncommercial* ++ purposes. For corporate use, please apply for a commercial license. ++ ++ Creative Commons License: ++ http://creativecommons.org/licenses/by-nc-sa/3.0/ ++ ++ Commercial License: ++ http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-License-Version2 ++******************************************************************************/ ++ ++// NOTE: don't include stdio.h (for FILE) or sys/types.h (for off_t). ++// nginx redefines _FILE_OFFSET_BITS and off_t will have different sizes ++// depending on include order ++ ++#ifndef _MSC_VER ++#include ++#else ++#include "inttypes.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++extern int mp4_split(const char* infile, int64_t filesize, ++ float start_time, float end_time, ++ void** mp4_header, uint32_t* mp4_header_size, ++ uint64_t* mdat_offset, uint64_t* mdat_size, ++ int client_is_flash); ++ ++/* Returns true when the test string is a prefix of the input */ ++extern int starts_with(const char* input, const char* test); ++ ++#ifdef __cplusplus ++} /* extern C definitions */ ++#endif ++ ++// End Of File ++ diff --git a/lighttpd-mod_indexfile.conf b/lighttpd-mod_indexfile.conf new file mode 100644 index 0000000..61d7477 --- /dev/null +++ b/lighttpd-mod_indexfile.conf @@ -0,0 +1,13 @@ +# indexfile module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Index-file-names_Details + +# NOTE: this module is automatically loaded + +# files to check for if .../ is requested +index-file.names = ( + "index.html", + "index.htm", + "index.php", + "default.htm", +) diff --git a/lighttpd-mod_magnet.conf b/lighttpd-mod_magnet.conf new file mode 100644 index 0000000..3df693f --- /dev/null +++ b/lighttpd-mod_magnet.conf @@ -0,0 +1,10 @@ +# a power-magnet module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModMagnet + +server.modules += ( + "mod_magnet" +) + +#magnet.attract-raw-url-to = ... +#magnet.attract-physical-path-to = ... diff --git a/lighttpd-mod_mysql_vhost.conf b/lighttpd-mod_mysql_vhost.conf new file mode 100644 index 0000000..b54b090 --- /dev/null +++ b/lighttpd-mod_mysql_vhost.conf @@ -0,0 +1,7 @@ +# mysql_vhost module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModMySQLVhost + +server.modules += ( + "mod_mysql_vhost" +) diff --git a/lighttpd-mod_proxy.conf b/lighttpd-mod_proxy.conf new file mode 100644 index 0000000..db61021 --- /dev/null +++ b/lighttpd-mod_proxy.conf @@ -0,0 +1,31 @@ +# Let lighttpd act as a proxy server for special file types, hosts etc +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModProxy + +server.modules += ( + "mod_proxy" +) + +## Balance algorithm, possible values are: "hash", "round-robin" or "fair" (default) +# proxy.balance = "hash" + +## Redirect all queries to files ending with ".php" to 192.168.0.101:80 +#proxy.server = ( +# ".php" => (( +# "host" => "192.168.0.101", +# "port" => 80 +# )) +#) + +## Redirect all connections on www.example.com to 10.0.0.1{0,1,2,3} +#$HTTP["host"] == "www.example.com" { +# proxy.balance = "hash" +# proxy.server = ( +# "" => ( +# ( "host" => "10.0.0.10" ), +# ( "host" => "10.0.0.11" ), +# ( "host" => "10.0.0.12" ), +# ( "host" => "10.0.0.13" ), +# ) +# ) +#} diff --git a/lighttpd-mod_redirect.conf b/lighttpd-mod_redirect.conf new file mode 100644 index 0000000..d511b63 --- /dev/null +++ b/lighttpd-mod_redirect.conf @@ -0,0 +1,15 @@ +# Redirection module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModRedirect + +server.modules += ( + "mod_redirect" +) + +#url.redirect = ( +# "^/wishlist/(.+)" => "http://www.123.org/$1", +#) +#### both rewrite/redirect support back reference to regex conditional using %n +#$HTTP["host"] =~ "^www\.(.*)" { +# url.redirect = ( "^/(.*)" => "http://%1/$1" ) +#} diff --git a/lighttpd-mod_rewrite.conf b/lighttpd-mod_rewrite.conf new file mode 100644 index 0000000..0c9fe5a --- /dev/null +++ b/lighttpd-mod_rewrite.conf @@ -0,0 +1,12 @@ +# Rewrite module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModRewrite + +server.modules += ( + "mod_rewrite" +) + +#### url handling modules (rewrite, redirect, access) +#url.rewrite-once = ( +# "^/$" => "/server-status", +#) diff --git a/lighttpd-mod_rrdtool.conf b/lighttpd-mod_rrdtool.conf new file mode 100644 index 0000000..0db0f77 --- /dev/null +++ b/lighttpd-mod_rrdtool.conf @@ -0,0 +1,11 @@ +# rrdtool module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModRRDTool + +server.modules += ( + "mod_rrdtool" +) + +#### rrdtool +rrdtool.binary = "/usr/bin/rrdtool" +rrdtool.db-name = "/var/lib/lighttpd/lighttpd.rrd" diff --git a/lighttpd-mod_scgi.conf b/lighttpd-mod_scgi.conf new file mode 100644 index 0000000..1558dab --- /dev/null +++ b/lighttpd-mod_scgi.conf @@ -0,0 +1,7 @@ +# SCGI module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModSCGI + +server.modules += ( + "mod_scgi" +) diff --git a/lighttpd-mod_secdownload.conf b/lighttpd-mod_secdownload.conf new file mode 100644 index 0000000..c916f1d --- /dev/null +++ b/lighttpd-mod_secdownload.conf @@ -0,0 +1,7 @@ +# secure and fast downloading module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModSecDownload + +server.modules += ( + "mod_secdownload" +) diff --git a/lighttpd-mod_setenv.conf b/lighttpd-mod_setenv.conf new file mode 100644 index 0000000..68763d9 --- /dev/null +++ b/lighttpd-mod_setenv.conf @@ -0,0 +1,19 @@ +# setenv module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModSetEnv + +server.modules += ( + "mod_setenv" +) + +#### setenv +#setenv.add-request-header = ( "TRAV_ENV" => "mysql://user@host/db" ) +#setenv.add-response-header = ( "X-Secret-Message" => "42" ) + +# Add CORS header for font requests +# http://davidwalsh.name/cdn-fonts +#$HTTP["url"] =~ "\.(?:eot|ttf|otf|woff)$" { +# setenv.add-response-header += ( +# "Access-Control-Allow-Origin" => "*", +# ) +#} diff --git a/lighttpd-mod_simple_vhost.conf b/lighttpd-mod_simple_vhost.conf new file mode 100644 index 0000000..5ffb73e --- /dev/null +++ b/lighttpd-mod_simple_vhost.conf @@ -0,0 +1,15 @@ +# Simple name-based virtual hosting +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModSimpleVhost + +server.modules += ( + "mod_simple_vhost" +) + +## The document root of a virtual host isdocument-root = +## simple-vhost.server-root + $HTTP["host"] + simple-vhost.document-root +simple-vhost.server-root = "/home/services/lighttpd/" +simple-vhost.document-root = "/html/" + +## the default host if no host is sent +simple-vhost.default-host = "www.example.com" diff --git a/lighttpd-mod_ssi.conf b/lighttpd-mod_ssi.conf new file mode 100644 index 0000000..21a40d7 --- /dev/null +++ b/lighttpd-mod_ssi.conf @@ -0,0 +1,11 @@ +# Server-Side Include implements simple preprocessing of +# HTML files compatible to Apache SSI. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModSSI + +server.modules += ( + "mod_ssi" +) + +## The extension of the files which should be preprocessed (mostly .shtml) +ssi.extension = ( ".shtml" ) diff --git a/lighttpd-mod_staticfile.conf b/lighttpd-mod_staticfile.conf new file mode 100644 index 0000000..c20d661 --- /dev/null +++ b/lighttpd-mod_staticfile.conf @@ -0,0 +1,11 @@ +# staticfile module. + +# NOTE: this module is automatically loaded + +# see http://redmine.lighttpd.net/issues/1209 +#etag.use-inode = "enable" +#etag.use-mtime = "enable" +#etag.use-size = "enable" + +# ETag header generation (default: enabled) +#static-file.etags = "enable" diff --git a/lighttpd-mod_status.conf b/lighttpd-mod_status.conf new file mode 100644 index 0000000..e388e9d --- /dev/null +++ b/lighttpd-mod_status.conf @@ -0,0 +1,13 @@ +# Status module +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModStatus + +server.modules += ( + "mod_status" +) + +$HTTP["remoteip"] == "127.0.0.1" { + status.status-url = "/server-status" + status.config-url = "/server-config" + status.statistics-url = "/server-stats" +} diff --git a/lighttpd-mod_trigger_b4_dl.conf b/lighttpd-mod_trigger_b4_dl.conf new file mode 100644 index 0000000..1b63371 --- /dev/null +++ b/lighttpd-mod_trigger_b4_dl.conf @@ -0,0 +1,24 @@ +# A module to prevent deep-linking from other sites. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModTriggerBeforeDownload + +server.modules += ( + "mod_trigger_b4_dl" +) + +## guarded download URL, direct access is denied +#trigger-before-download.download-url = "^/download/" + +## trigger URL to allow downloads from +#trigger-before-download.trigger-url = "^/trigger/" + +## if access to a file is denied, the user is redirected to this URL +#trigger-before-download.deny-url = "/home/services/lighttpd/html/deny.html" + +## access to granted for seconds after the trigger +#trigger-before-download.trigger-timeout = 10 + +## storage of trigger information. If both destinations are provided, +## the GDBM file takes precedence. +#trigger-before-download.gdbm-filename = "/home/services/lighttpd/data/trigger.db" +#trigger-before-download.memcache-hosts = ( "127.0.0.1:11211" ) diff --git a/lighttpd-mod_userdir.conf b/lighttpd-mod_userdir.conf new file mode 100644 index 0000000..bc0e418 --- /dev/null +++ b/lighttpd-mod_userdir.conf @@ -0,0 +1,18 @@ +# The userdir module provides a simple way to link user-based directories into +# the global namespace of the webserver. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModUserDir + +server.modules += ( + "mod_userdir" +) + +## the subdirectory of a user's home dir which should be accessible +## under http://$host/~$user +userdir.path = "public_html" + +## The users whose home directories should not be accessible +userdir.exclude-user = ( "root", "postmaster" ) + +## enable userdir module +userdir.active = "enable" diff --git a/lighttpd-mod_usertrack.conf b/lighttpd-mod_usertrack.conf new file mode 100644 index 0000000..eb1aba4 --- /dev/null +++ b/lighttpd-mod_usertrack.conf @@ -0,0 +1,7 @@ +# usertrack module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModUserTrack + +server.modules += ( + "mod_usertrack" +) diff --git a/lighttpd-mod_webdav.conf b/lighttpd-mod_webdav.conf new file mode 100644 index 0000000..86bc602 --- /dev/null +++ b/lighttpd-mod_webdav.conf @@ -0,0 +1,7 @@ +# webdav module. +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModWebDAV + +server.modules += ( + "mod_webdav" +) diff --git a/lighttpd-modinit-before-fork.patch b/lighttpd-modinit-before-fork.patch new file mode 100644 index 0000000..2f2c5e5 --- /dev/null +++ b/lighttpd-modinit-before-fork.patch @@ -0,0 +1,187 @@ +--- ./src/server.c (working copy) ++++ lighttpd-1.4.19/src/server.c 2008-09-19 17:43:33.695522751 +0300 +@@ -865,34 +865,9 @@ + return -1; + } + +-#ifdef HAVE_FORK +- /* network is up, let's deamonize ourself */ +- if (srv->srvconf.dont_daemonize == 0) daemonize(); +-#endif +- + srv->gid = getgid(); + srv->uid = getuid(); + +- /* write pid file */ +- if (pid_fd != -1) { +- buffer_copy_long(srv->tmp_buf, getpid()); +- buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\n")); +- write(pid_fd, srv->tmp_buf->ptr, srv->tmp_buf->used - 1); +- close(pid_fd); +- pid_fd = -1; +- } +- +- /* Close stderr ASAP in the child process to make sure that nothing +- * is being written to that fd which may not be valid anymore. */ +- if (-1 == log_error_open(srv)) { +- log_error_write(srv, __FILE__, __LINE__, "s", "Opening errorlog failed. Going down."); +- +- plugins_free(srv); +- network_close(srv); +- server_free(srv); +- return -1; +- } +- + if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { + log_error_write(srv, __FILE__, __LINE__, "s", "Configuration of plugins failed. Going down."); + +@@ -918,7 +893,86 @@ + return -1; + } + ++ if (NULL == (srv->ev = fdevent_init(srv->max_fds + 1, srv->event_handler))) { ++ log_error_write(srv, __FILE__, __LINE__, ++ "s", "fdevent_init failed"); ++ return -1; ++ } ++ /* ++ * kqueue() is called here, select resets its internals, ++ * all server sockets get their handlers ++ * ++ * */ ++ if (0 != network_register_fdevents(srv)) { ++ plugins_free(srv); ++ network_close(srv); ++ server_free(srv); ++ ++ return -1; ++ } ++ ++ /* might fail if user is using fam (not gamin) and famd isn't running */ ++ if (NULL == (srv->stat_cache = stat_cache_init())) { ++ log_error_write(srv, __FILE__, __LINE__, "s", ++ "stat-cache could not be setup, dieing."); ++ return -1; ++ } ++ ++#ifdef HAVE_FAM_H ++ /* setup FAM */ ++ if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { ++ if (0 != FAMOpen2(srv->stat_cache->fam, "lighttpd")) { ++ log_error_write(srv, __FILE__, __LINE__, "s", ++ "could not open a fam connection, dieing."); ++ return -1; ++ } ++#ifdef HAVE_FAMNOEXISTS ++ FAMNoExists(srv->stat_cache->fam); ++#endif ++ ++ srv->stat_cache->fam_fcce_ndx = -1; ++ fdevent_register(srv->ev, FAMCONNECTION_GETFD(srv->stat_cache->fam), stat_cache_handle_fdevent, NULL); ++ fdevent_event_add(srv->ev, &(srv->stat_cache->fam_fcce_ndx), FAMCONNECTION_GETFD(srv->stat_cache->fam), FDEVENT_IN); ++ } ++#endif ++ ++ ++ /* get the current number of FDs */ ++ srv->cur_fds = open("/dev/null", O_RDONLY); ++ close(srv->cur_fds); ++ ++ for (i = 0; i < srv->srv_sockets.used; i++) { ++ server_socket *srv_socket = srv->srv_sockets.ptr[i]; ++ if (-1 == fdevent_fcntl_set(srv->ev, srv_socket->fd)) { ++ log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed:", strerror(errno)); ++ return -1; ++ } ++ } ++ ++ /* Close stderr ASAP to make sure that nothing is being written to ++ * that fd which may not be valid anymore after forking. */ ++ if (-1 == log_error_open(srv)) { ++ log_error_write(srv, __FILE__, __LINE__, "s", "Opening errorlog failed. Going down."); ++ ++ plugins_free(srv); ++ network_close(srv); ++ server_free(srv); ++ return -1; ++ } ++ ++#ifdef HAVE_FORK ++ /* network is up, let's deamonize ourself */ ++ if (srv->srvconf.dont_daemonize == 0) daemonize(); ++#endif + ++ /* write pid file */ ++ if (pid_fd != -1) { ++ buffer_copy_long(srv->tmp_buf, getpid()); ++ buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\n")); ++ write(pid_fd, srv->tmp_buf->ptr, srv->tmp_buf->used - 1); ++ close(pid_fd); ++ pid_fd = -1; ++ } + + + #ifdef HAVE_SIGACTION +@@ -1042,62 +1096,6 @@ + } + #endif + +- if (NULL == (srv->ev = fdevent_init(srv->max_fds + 1, srv->event_handler))) { +- log_error_write(srv, __FILE__, __LINE__, +- "s", "fdevent_init failed"); +- return -1; +- } +- /* +- * kqueue() is called here, select resets its internals, +- * all server sockets get their handlers +- * +- * */ +- if (0 != network_register_fdevents(srv)) { +- plugins_free(srv); +- network_close(srv); +- server_free(srv); +- +- return -1; +- } +- +- /* might fail if user is using fam (not gamin) and famd isn't running */ +- if (NULL == (srv->stat_cache = stat_cache_init())) { +- log_error_write(srv, __FILE__, __LINE__, "s", +- "stat-cache could not be setup, dieing."); +- return -1; +- } +- +-#ifdef HAVE_FAM_H +- /* setup FAM */ +- if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { +- if (0 != FAMOpen2(srv->stat_cache->fam, "lighttpd")) { +- log_error_write(srv, __FILE__, __LINE__, "s", +- "could not open a fam connection, dieing."); +- return -1; +- } +-#ifdef HAVE_FAMNOEXISTS +- FAMNoExists(srv->stat_cache->fam); +-#endif +- +- srv->stat_cache->fam_fcce_ndx = -1; +- fdevent_register(srv->ev, FAMCONNECTION_GETFD(srv->stat_cache->fam), stat_cache_handle_fdevent, NULL); +- fdevent_event_add(srv->ev, &(srv->stat_cache->fam_fcce_ndx), FAMCONNECTION_GETFD(srv->stat_cache->fam), FDEVENT_IN); +- } +-#endif +- +- +- /* get the current number of FDs */ +- srv->cur_fds = open("/dev/null", O_RDONLY); +- close(srv->cur_fds); +- +- for (i = 0; i < srv->srv_sockets.used; i++) { +- server_socket *srv_socket = srv->srv_sockets.ptr[i]; +- if (-1 == fdevent_fcntl_set(srv->ev, srv_socket->fd)) { +- log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed:", strerror(errno)); +- return -1; +- } +- } +- + /* main-loop */ + while (!srv_shutdown) { + int n; diff --git a/lighttpd-php-external.conf b/lighttpd-php-external.conf new file mode 100644 index 0000000..73741aa --- /dev/null +++ b/lighttpd-php-external.conf @@ -0,0 +1,48 @@ +# FastCGI server for PHP + +# Sometimes you need to run old html file(s) as php pages, especially if you +# are migrating from Apache web server. To treat html pages as php you need to +# map multiple extensions to the same fastcgi server using fastcgi.map-extensions +# directive. +#fastcgi.map-extensions = ( +# ".html" => ".php", +# ".php3" => ".php", +#) + +fastcgi.server += ( + ".php" => (( + "socket" => "/var/run/php/php-fpm.sock", # spawned by php-fpm +# "socket" => "/var/run/php/fcgi.sock", # spawned by php-fcgi-init + # we enable check-local so you can use server.error-handler-404 for php files too + "check-local" => "enable", + "disable-time" => 1, +# "allow-x-send-file" => "enable", + )), + +# for this to work set in /etc/php/php-fpm.conf: +# pm.status_path = /fpm-status +# "/fpm-status" => (( +# "socket" => "/var/run/php/fpm.sock", +# "check-local" => "disable", +# "disable-time" => 1, +# )), +) + +#fastcgi.server += ( +# ".php" => ( +# "local" => ( +# "host" => "127.0.0.1", +# "port" => 1026, +# # we enable check-local so you can use server.error-handler-404 for php files too +# "check-local" => "enable", +# ), +# "slave" => ( +# "host" => "192.168.5.93", +# "port" => 1026, +# # we enable check-local so you can use server.error-handler-404 for php files too +# "check-local" => "enable", +# ), +# ), +#) + +static-file.exclude-extensions += (".php") diff --git a/lighttpd-php-spawned.conf b/lighttpd-php-spawned.conf new file mode 100644 index 0000000..b9869ae --- /dev/null +++ b/lighttpd-php-spawned.conf @@ -0,0 +1,34 @@ +# FastCGI server for PHP + +# Sometimes you need to run old html file(s) as php pages, especially if you +# are migrating from Apache web server. To treat html pages as php you need to +# map multiple extensions to the same fastcgi server using fastcgi.map-extensions +# directive. +#fastcgi.map-extensions = ( +# ".html" => ".php", +# ".php3" => ".php", +#) + +fastcgi.server += ( + ".php" => (( + "bin-path" => "/usr/bin/php.fcgi", + "socket" => "/var/run/php/php-fcgi-" + var.pid + ".sock", + "max-procs" => 2, + "idle-timeout" => 20, + # SIGTERM is graceful shutdown in PLD Linux, use `kill -l' to figure out other signal numbers. +# "kill-signal" => 15, + "bin-environment" => ( + "PHP_FCGI_CHILDREN" => "4", + "PHP_FCGI_MAX_REQUESTS" => "10000" + ), + "bin-copy-environment" => ( + "PATH", "SHELL" + ), + "broken-scriptfilename" => "enable", + "check-local" => "enable", + "disable-time" => 1, +# "allow-x-send-file" => "enable", + )) +) + +static-file.exclude-extensions += (".php") diff --git a/lighttpd-pld.html b/lighttpd-pld.html new file mode 100644 index 0000000..1ff430b --- /dev/null +++ b/lighttpd-pld.html @@ -0,0 +1,29 @@ + + + + + + + Powered by lighttpd + + + + + + +

+LIGHTTPD - fly light. +

+ +

+Powered by Lighttpd
+Powered by PLD Linux +

+ + + diff --git a/lighttpd-ssl.conf b/lighttpd-ssl.conf new file mode 100644 index 0000000..7e89b24 --- /dev/null +++ b/lighttpd-ssl.conf @@ -0,0 +1,52 @@ +# lighttpd support for SSLv2 and SSLv3 +# +# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_SSL + +# (Following SSL/TLS Deployment Best Practices 1.3 / 17 September 2013 from: +# https://www.ssllabs.com/projects/best-practices/index.html) +# - BEAST is considered mitigaed on client side now, and new weaknesses have been found in RC4, +# so it is strongly advised to disable RC4 ciphers (HIGH doesn't include RC4) +# - It is recommended to disable 3DES too (although disabling RC4 and 3DES breaks IE6+8 on Windows XP, +# so you might want to support 3DES for now - just remove the '!3DES' parts below). +# - The examples below prefer ciphersuites with "Forward Secrecy" (and ECDHE over DHE (alias EDH)), remove '+kEDH +kRSA' +# if you don't want that. +# - SRP and PSK are not supported anyway, excluding those ('!kSRP !kPSK') just keeps the list smaller (easier to review) +# Check your cipher list with: openssl ciphers -v '...' (use single quotes as your shell won't like ! in double quotes) +# +# If you know you have RSA keys (standard), you can use: +#ssl.cipher-list = "aRSA+HIGH !3DES +kEDH +kRSA !kSRP !kPSK" +# The more generic version (without the restriction to RSA keys) is +#ssl.cipher-list = "HIGH !aNULL !3DES +kEDH +kRSA !kSRP !kPSK" + +# Make the server prefer the order of the server side cipher suite instead of the client suite. +# This option is enabled by default, but only used if ssl.cipher-list is set. +# +#ssl.honor-cipher-order = "enable" +# + +$SERVER["socket"] == ":443" { + ssl.engine = "enable" + + # unsafe protocols + ssl.use-sslv2 = "disable" + ssl.use-sslv3 = "disable" + + # https://wiki.mozilla.org/Security/Server_Side_TLS + # forward secrecy + ssl.honor-cipher-order = "enable" + ssl.cipher-list = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" + + # ssl.pemfile: path to the PEM file for SSL support (Should contain both + # the private key and the certificate) + ## If you have a .crt and a .key file, cat them together into a + ## single PEM file: + ## $ cat lighttpd.key lighttpd.crt > lighttpd.pem + ssl.pemfile = "/etc/lighttpd/server.pem" + + # ssl.ca-file: path to the CA file for support of chained certificates +# ssl.ca-file = "/etc/certs/ca-certificates.crt" + + $HTTP["useragent"] =~ "MSIE" { + server.max-keep-alive-requests = 0 + } +} diff --git a/lighttpd-use_bin_sh.patch b/lighttpd-use_bin_sh.patch new file mode 100644 index 0000000..de08dd3 --- /dev/null +++ b/lighttpd-use_bin_sh.patch @@ -0,0 +1,20 @@ +## Use /bin/sh to execute external programs instead of getting +## the shell from $SHELL (See ticket #388) +# +# http://trac.lighttpd.net/trac/ticket/388 + +--- lighttpd-1.4.12/src/proc_open.c~ 2006-09-19 15:49:41.846531074 +0300 ++++ lighttpd-1.4.12/src/proc_open.c 2006-09-19 15:51:13.658593480 +0300 +@@ -219,11 +219,7 @@ + /* {{{ proc_open */ + int proc_open(proc_handler_t *proc, const char *command) { + pid_t child; +- const char *shell; +- +- if (NULL == (shell = getenv(SHELLENV))) { +- shell = "/bin/sh"; +- } ++ const char *shell = "/bin/sh"; + + if (proc_open_pipes(proc) != 0) { + return -1; diff --git a/lighttpd.conf b/lighttpd.conf new file mode 100644 index 0000000..75079f7 --- /dev/null +++ b/lighttpd.conf @@ -0,0 +1,187 @@ +# lighttpd configuration file. + +include_shell "for f in conf.d/*.conf ; do [ -f \"$f\" ] && echo \"include \\"$f\\"\" ; done" + +## a static document-root, for virtual-hosting take look at the +## server.virtual-* options +server.document-root = "/home/services/lighttpd/html/" + +## where to send error-messages to +server.errorlog = "/var/log/lighttpd/error.log" +#server.errorlog-use-syslog = "enable" + +## http://redmine.lighttpd.net/projects/lighttpd/repository/revisions/2550 +## NOTE: It is not reopened in log_error_cycle, so don't setup logrotate to this. +server.breakagelog = "/var/log/lighttpd/breakage.log" + +server.pid-file = "/var/run/lighttpd.pid" + +# mimetype mapping +include "mime.types.conf" +# default mime type +mimetype.assign += ( + "" => "application/octet-stream", +) + +## set the event-handler (read the performance section in the manual) +# Linux 2.4+ rt-signals linux-rtsig +# Linux 2.6+ epoll linux-sysepoll +#server.event-handler = "linux-sysepoll" + +## The basic network interface for all platforms at the syscalls read() +## and write(). Every modern OS provides its own syscall to help network +## servers transfer files as fast as possible +## +## linux-sendfile - is recommended for small files. +## writev - is recommended for sending many large files +## +#server.network-backend = "linux-sendfile" + +# default cames from ulimit -n +#server.max-fds = 8192 + +## +## Stat() call caching. +## +## lighttpd can utilize FAM/Gamin to cache stat call. +## +## possible values are: +## disable, simple or fam. +## +#server.stat-cache-engine = "simple" + +## +## Fine tuning for the request handling +## +## max-connections == max-fds/2 (maybe /3) +## means the other file handles are used for fastcgi/files +## +#server.max-connections = 1024 + +# maximum number of request within a keep-alive session before the server terminates the connection +# default 16 +#server.max-keep-alive-requests = 16 + +# Maximum number of seconds until an idling keep-alive connection is dropped. +# default 30 +#server.max-keep-alive-idle = 30 + +# maximum number of seconds until a waiting, non keep-alive read times out and closes the connection +# default 60 +#server.max-read-idle = 60 + +# maximum number of seconds until a waiting write call times out and closes the connection +# default 360 +#server.max-write-idle = 360 + +## +## Maximum size of a request in kilobytes. +## By default it is unlimited (0). +## +## Uploads to your server can't be larger than this value. +## +#server.max-request-size = 0 + +# Use the "Content-Type" extended attribute to obtain mime type if possible +#mimetype.use-xattr = "enable" + +## +## Should lighttpd follow symlinks? +## +#server.follow-symlink = "enable" + +## +## force all filenames to be lowercase? +## +#server.force-lowercase-filenames = "disable" + +## +## defaults to /var/tmp as we assume it is a local harddisk +## +server.upload-dirs = ( "/var/tmp" ) + +## +## The value for the "Server:" response field. +## +## It would be nice to keep it at "lighttpd". +## +#server.tag = "lighttpd" + +$HTTP["url"] =~ "\.pdf$" { + server.range-requests = "disable" +} + +## Skip responding with 417 for unhandled Expect header for some broken applications +#server.reject-expect-100-with-417 = "disable" + +######### Options that are good to be but not neccesary to be changed ####### + +## bind to port (default: 80) +server.port = 80 + +## Use IPv6? +#server.use-ipv6 = "enable" + +## bind to specific host (default: all interfaces) +## the name is resolved to ip at startup only. +#server.bind = "www.example.org" + +## additionally just bind to localhost:80 +#$SERVER["socket"] == "127.0.0.1:80" {} + +## error-handler for status 404 +#server.error-handler-404 = "/error-handler.html" +#server.error-handler-404 = "/error-handler.php" + +## +## Format: .html +## -> ..../status-404.html for 'File not found' +#server.errorfile-prefix = "/home/weigon/projects/lighttpd/doc/status-" +# +## enable debugging +# http://redmine.lighttpd.net/projects/lighttpd/wiki/DebugVariables +#debug.log-request-header = "enable" +#debug.log-request-header-on-error = "enable" +#debug.log-response-header = "enable" +#debug.log-request-handling = "enable" +#debug.log-file-not-found = "enable" +#debug.log-condition-handling = "enable" +#debug.log-condition-cache-handling = "enable" +#debug.log-timeouts = "enable" +#debug.log-ssl-noise = "enable" + +### only root can use these options +# +# chroot() to directory (default: no chroot() ) +#server.chroot = "/" + +## change uid to (default: don't care) +server.username = "lighttpd" + +## change uid to (default: don't care) +server.groupname = "lighttpd" + +## +## Traffic Shaping +## ----------------- +## +## Values are in kilobyte per second. +## +## Keep in mind that a limit below 32kB/s might actually limit the +## traffic to 32kB/s. This is caused by the size of the TCP send +## buffer. +## +## per server: +## +#server.kbytes-per-second = 128 + +## +## per connection: +## +#connection.kbytes-per-second = 32 + +# webapps configs +include_shell "for f in webapps.d/*.conf ; do [ -f \"$f\" ] && echo \"include \\"$f\\"\" ; done" + +# vhosts config +include_shell "for f in vhosts.d/*.conf ; do [ -f \"$f\" ] && echo \"include \\"$f\\"\" ; done" diff --git a/lighttpd.init b/lighttpd.init new file mode 100755 index 0000000..aaf4d5f --- /dev/null +++ b/lighttpd.init @@ -0,0 +1,226 @@ +#!/bin/sh +# +# lighttpd lighttpd Web Server +# +# chkconfig: 345 85 15 +# description: lighttpd is a World Wide Web server. It is used to serve \ +# HTML files and CGI. +# + +# Source function library +. /etc/rc.d/init.d/functions + +upstart_controlled + +# Get network config +. /etc/sysconfig/network + +# Get service config +[ -f /etc/sysconfig/lighttpd ] && . /etc/sysconfig/lighttpd + +DAEMON=/usr/sbin/lighttpd +ANGEL_DAEMON=/usr/sbin/lighttpd-angel +CONFIGFILE=/etc/lighttpd/lighttpd.conf + +# Check that networking is up. +if is_yes "${NETWORKING}"; then + if [ ! -f /var/lock/subsys/network -a "$1" != stop -a "$1" != status ]; then + msg_network_down "Lighttpd Web Server" + exit 1 + fi +else + exit 0 +fi + +configtest() { + # check for deprecated config options that cause bad side effects + # TODO: should remove the deprecated options in pld after some time being warned out + local out + out=$(env SHELL=/bin/sh $DAEMON -f $CONFIGFILE $HTTPD_OPTS -p 2>&1 | grep -oE '(url.rewrite|url.rewrite-final)[[:space:]]') + if [ -n "$out" ]; then + # make it unique, format nicely + out=$(echo "$out" | sort -u | xargs | sed -e 's/ /, /g') + echo >&2 "WARNING: found deprecated '$out', convert to 'url.rewrite-final' recommented, See http://redmine.lighttpd.net/issues/2379" + fi + + env SHELL=/bin/sh $DAEMON -t -f $CONFIGFILE $HTTPD_OPTS +} + +# wrapper for configtest +checkconfig() { + local details=${1:-0} + + if [ $details = 1 ]; then + # run config test and display report (status action) + show "Checking %s configuration" "Lighttpd Web Server"; busy + local out + out=$(configtest 2>&1) + RETVAL=$? + if [ $RETVAL = 0 ]; then + ok + else + fail + fi + [ "$out" ] && echo >&2 "$out" + else + # run config test and abort with nice message if failed + # (for actions checking status before action). + configtest >/dev/null 2>&1 + RETVAL=$? + if [ $RETVAL != 0 ]; then + show "Checking %s configuration" "Lighttpd Web Server"; fail + nls 'Configuration test failed. See details with %s "checkconfig"' $0 + exit $RETVAL + fi + fi +} + +start() { + # Check if the service is already running? + if [ -f /var/lock/subsys/lighttpd ]; then + msg_already_running "Lighttpd Web Server" + return + fi + + checkconfig + msg_starting "Lighttpd Web Server"; busy + if is_yes "${LIGHT_ANGEL}"; then + daemon --fork --pidfile /var/run/lighttpd-angel.pid --makepid \ + env SHELL=/bin/sh $ANGEL_DAEMON -D -f $CONFIGFILE $HTTPD_OPTS + + else + env SHELL=/bin/sh $DAEMON -f $CONFIGFILE $HTTPD_OPTS + fi + RETVAL=$? + if [ $RETVAL -eq 0 ]; then + ok + touch /var/lock/subsys/lighttpd + else + fail + fi +} + +stop() { + # Stop daemons. + if [ ! -f /var/lock/subsys/lighttpd ]; then + msg_not_running "Lighttpd Web Server" + return + fi + + msg_stopping "Lighttpd Web Server" + killproc --pidfile /var/run/lighttpd.pid lighttpd + rm -f /var/lock/subsys/lighttpd >/dev/null 2>&1 + rm -f /var/run/lighttpd*.pid >/dev/null 2>&1 +} + +restart() { + local pid + + # short circuit to safe reload if pid exists and is alive + if is_yes "${LIGHT_ANGEL}"; then + if [ -f /var/lock/subsys/lighttpd ] && pid=$(pidofproc lighttpd-angel lighttpd-angel.pid) && checkpid $pid; then + reload + return + fi + else + if [ -f /var/lock/subsys/lighttpd ] && pid=$(pidofproc lighttpd lighttpd.pid) && checkpid $pid; then + reload + return + fi + fi + + checkconfig + stop + start +} + +reload() { + # TODO: check if process is running. Start it in this case. + if [ ! -f /var/lock/subsys/lighttpd ]; then + msg_not_running "Lighttpd Web Server" + RETVAL=7 + return + fi + + checkconfig 1 + msg_reloading "Lighttpd Web Server" + + if is_yes "${LIGHT_ANGEL}"; then + # sending HUP signal to angel will make lighttpd close all listening + # sockets and wait for client connections to terminate. After that new + # child will be started + killproc lighttpd-angel -HUP + else + # sending INT signal will make lighttpd close all listening sockets and + # wait for client connections to terminate. + killproc --pidfile /var/run/lighttpd.pid lighttpd -INT + env SHELL=/bin/sh lighttpd -f $CONFIGFILE $HTTPD_OPTS + fi + RETVAL=$? +} + +condrestart() { + if [ ! -f /var/lock/subsys/lighttpd ]; then + msg_not_running "Lighttpd Web Server" + RETVAL=$1 + return + fi + + checkconfig + stop + start +} + +flush-logs() { + if [ ! -f /var/lock/subsys/lighttpd ]; then + msg_not_running "Lighttpd Web Server" + RETVAL=7 + return + fi + + show "Rotating %s logs" lighttpd + # send HUP to main lighttpd (not angel) process to rotate logs: + killproc --pidfile /var/run/lighttpd.pid lighttpd -HUP + RETVAL=$? +} + +RETVAL=0 +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + restart + ;; + try-restart) + condrestart 0 + ;; + reload|force-reload|graceful) + reload + ;; + flush-logs) + flush-logs + ;; + checkconfig|configtest) + checkconfig 1 + ;; + show-config) + env SHELL=/bin/sh $DAEMON -p -f $CONFIGFILE $HTTPD_OPTS + ;; + status) + if is_yes "${LIGHT_ANGEL}"; then + status lighttpd-angel || RETVAL=$? + fi + status lighttpd || RETVAL=$? + ;; + *) + msg_usage "$0 {start|stop|restart|reload|force-reload|graceful|configtest|flush-logs|show-config|status}" + exit 3 + ;; +esac + +exit $RETVAL diff --git a/lighttpd.logrotate b/lighttpd.logrotate new file mode 100644 index 0000000..3d5dc28 --- /dev/null +++ b/lighttpd.logrotate @@ -0,0 +1,12 @@ +/var/log/lighttpd/access.log +/var/log/lighttpd/error.log +{ + olddir /var/log/archive/lighttpd + + create 640 lighttpd logs + + sharedscripts + postrotate + /sbin/service lighttpd flush-logs > /dev/null + endscript +} diff --git a/lighttpd.monitrc b/lighttpd.monitrc new file mode 100644 index 0000000..516dcb9 --- /dev/null +++ b/lighttpd.monitrc @@ -0,0 +1,31 @@ +check process lighttpd with pidfile /var/run/lighttpd.pid + group www + start program = "/etc/rc.d/init.d/lighttpd start" + stop program = "/etc/rc.d/init.d/lighttpd stop" + if failed url http://localhost/ timeout 10 seconds then restart +# if failed host 192.168.1.1 port 443 type TCPSSL +# certmd5 12-34-56-78-90-AB-CD-EF-12-34-56-78-90-AB-CD-EF +# protocol HTTP request /monit/token then restart + if 5 restarts within 5 cycles then timeout +# alert root@localhost +# alert 123456@sms on { timeout } + depends on lighttpd_bin + depends on lighttpd_rc + + check file lighttpd_bin with path /usr/sbin/lighttpd + group www + if failed checksum then unmonitor + if failed permission 755 then unmonitor + if failed uid root then unmonitor + if failed gid root then unmonitor +# alert root@localhost + + check file lighttpd_rc with path /etc/rc.d/init.d/lighttpd + group www + if failed checksum then unmonitor + if failed permission 754 then unmonitor + if failed uid root then unmonitor + if failed gid root then unmonitor +# alert root@localhost + + diff --git a/lighttpd.service b/lighttpd.service new file mode 100644 index 0000000..6f4dce9 --- /dev/null +++ b/lighttpd.service @@ -0,0 +1,12 @@ +[Unit] +Description=Lighttpd Daemon +After=network.target + +[Service] +EnvironmentFile=-/etc/sysconfig/lighttpd +ExecStartPre=/usr/sbin/lighttpd-angel -t -f /etc/lighttpd/lighttpd.conf +ExecStart=/usr/sbin/lighttpd-angel -D -f /etc/lighttpd/lighttpd.conf $HTTPD_OPTS +ExecReload=/bin/kill -HUP $MAINPID + +[Install] +WantedBy=multi-user.target diff --git a/lighttpd.spec b/lighttpd.spec new file mode 100644 index 0000000..c2c4894 --- /dev/null +++ b/lighttpd.spec @@ -0,0 +1,1346 @@ + +# TODO: +# - provide or autogenerate self signed cert in post, so after installing +# lighttpd-ssl server will still work +# - patch with mod_websocket: https://github.com/Juniper/lighttpd-for-juise +# +# Conditional build: +%bcond_with tests # build with tests +%bcond_without xattr # support of extended attributes +%bcond_without ipv6 # IPv4-only version (doesn't require IPv6 in kernel) +%bcond_without largefile # largefile support (see notes above) +%bcond_without ssl # ssl support +%bcond_without mysql # mysql support in mod_mysql_vhost +%bcond_without ldap # ldap support in mod_auth +%bcond_without lua # LUA support in mod_cml (needs LUA >= 5.1) +%bcond_with memcache # memcached support in mod_cml / mod_trigger_b4_dl +%bcond_with gamin # gamin for reducing number of stat() calls. + # NOTE: must be enabled in config: server.stat-cache-engine = "fam" +%bcond_with gdbm # gdbm in mod_trigger_b4_dl +%bcond_with webdav_props # properties in mod_webdav (includes extra sqlite3/libxml deps) +%bcond_with webdav_locks # webdav locks with extra efsprogs deps +%bcond_with valgrind # compile code with valgrind support. +%bcond_with deflate # build deflate module (needs patch update with current svn) + +%if %{with webdav_locks} +%define webdav_progs 1 +%endif + +Summary: Fast and light HTTP server +Summary(pl.UTF-8): Szybki i lekki serwer HTTP +Name: lighttpd +Version: 1.4.35 +Release: 6 +License: BSD +Group: Networking/Daemons/HTTP +Source0: http://download.lighttpd.net/lighttpd/releases-1.4.x/%{name}-%{version}.tar.bz2 +# Source0-md5: f7a88130ee9984b421ad8aa80629750a +Source1: %{name}.init +Source2: %{name}.conf +Source3: %{name}.user +Source4: %{name}.logrotate +Source5: %{name}.sysconfig +Source6: %{name}-mime.types.sh +Source7: http://glen.alkohol.ee/pld/lighty/favicon.ico +# Source7-md5: 00fcac5b861a54f5eb147a589504d480 +Source8: light_button.png +# Source8-md5: 3e1008ee1d3d6d390cf81fe3072b4f50 +Source9: light_logo.png +# Source9-md5: cbb7f0676e51ee2e26cf004df293fc62 +Source10: pld_button.png +# Source10-md5: 185afa921e81bd726b9f0f9f0909dc6e +Source11: %{name}-pld.html +Source12: %{name}.monitrc +Source13: branch.sh +Source14: TODO +Source15: %{name}.upstart +Source16: %{name}.tmpfiles +Source17: %{name}.service +Source100: %{name}-mod_access.conf +Source101: %{name}-mod_accesslog.conf +Source102: %{name}-mod_alias.conf +Source103: %{name}-mod_auth.conf +Source104: %{name}-mod_cgi.conf +Source105: %{name}-mod_cml.conf +Source106: %{name}-mod_compress.conf +Source107: %{name}-mod_deflate.conf +Source108: %{name}-mod_dirlisting.conf +Source109: %{name}-mod_evasive.conf +Source110: %{name}-mod_evhost.conf +Source111: %{name}-mod_expire.conf +Source112: %{name}-mod_fastcgi.conf +Source113: %{name}-mod_flv_streaming.conf +Source114: %{name}-mod_indexfile.conf +Source115: %{name}-mod_proxy.conf +Source116: %{name}-mod_redirect.conf +Source117: %{name}-mod_rewrite.conf +Source118: %{name}-mod_rrdtool.conf +Source119: %{name}-mod_scgi.conf +Source120: %{name}-mod_secdownload.conf +Source121: %{name}-mod_setenv.conf +Source122: %{name}-mod_simple_vhost.conf +Source123: %{name}-mod_ssi.conf +Source124: %{name}-mod_staticfile.conf +Source125: %{name}-mod_status.conf +Source126: %{name}-mod_trigger_b4_dl.conf +Source127: %{name}-mod_userdir.conf +Source128: %{name}-mod_usertrack.conf +Source129: %{name}-mod_webdav.conf +Source130: %{name}-php-spawned.conf +Source131: %{name}-php-external.conf +Source132: %{name}-ssl.conf +Source133: %{name}-mod_mysql_vhost.conf +Source134: %{name}-mod_magnet.conf +Source135: %{name}-mod_extforward.conf +Source136: %{name}-mod_h264_streaming.conf +Source137: %{name}-mod_cgi_php.conf +Source138: %{name}-mod_compress.tmpwatch +#Patch100: %{name}-branch.diff +Patch0: %{name}-use_bin_sh.patch +Patch1: %{name}-mod_evasive-status_code.patch +Patch2: %{name}-mod_h264_streaming.patch +Patch3: %{name}-branding.patch +Patch5: %{name}-mod_deflate.patch +Patch6: test-port-setup.patch +Patch7: env-documentroot.patch +#Patch: %{name}-modinit-before-fork.patch +#Patch: %{name}-errorlog-before-fork.patch +URL: http://www.lighttpd.net/ +%{?with_xattr:BuildRequires: attr-devel} +BuildRequires: autoconf >= 2.57 +%if "%{pld_release}" != "ac" +BuildRequires: automake >= 1:1.11.2 +%else +BuildRequires: automake +%endif +BuildRequires: bzip2-devel +BuildRequires: fcgi-devel +%{?with_gamin:BuildRequires: gamin-devel} +%{?with_gdbm:BuildRequires: gdbm-devel} +%{?with_memcache:BuildRequires: libmemcache-devel} +BuildRequires: libtool +BuildRequires: libuuid-devel +%{?with_webdav_props:BuildRequires: libxml2-devel} +%{?with_lua:BuildRequires: lua51-devel} +BuildRequires: mailcap >= 2.1.14-4.4 +%{?with_mysql:BuildRequires: mysql-devel} +%{?with_ldap:BuildRequires: openldap-devel} +%{?with_ssl:BuildRequires: openssl-devel} +BuildRequires: pcre-devel +BuildRequires: pkgconfig +BuildRequires: rpm >= 4.4.9-56 +BuildRequires: rpmbuild(macros) >= 1.647 +%{?with_webdav_props:BuildRequires: sqlite3-devel} +%{?with_valgrind:BuildRequires: valgrind} +BuildRequires: which +BuildRequires: zlib-devel +Requires(post,preun): /sbin/chkconfig +Requires(postun): /usr/sbin/groupdel +Requires(postun): /usr/sbin/userdel +Requires(pre): /bin/id +Requires(pre): /usr/bin/getgid +Requires(pre): /usr/lib/rpm/user_group.sh +Requires(pre): /usr/sbin/groupadd +Requires(pre): /usr/sbin/useradd +Requires(pre): /usr/sbin/usermod +Requires: %{name}-mod_dirlisting = %{version}-%{release} +Requires: %{name}-mod_indexfile = %{version}-%{release} +Requires: %{name}-mod_staticfile = %{version}-%{release} +Requires: rc-scripts >= 0.4.3.0 +Requires: rpm-whiteout >= 1.5 +Requires: systemd-units >= 38 +Suggests: %{name}-mod_accesslog +Provides: group(http) +Provides: group(lighttpd) +Provides: user(lighttpd) +Provides: webserver +Provides: webserver(headers) +Provides: webserver(mime) +Conflicts: logrotate < 3.7-4 +# for the posttrans scriptlet, conflicts because in vserver environment rpm package is not installed. +Conflicts: rpm < 4.4.2-0.2 +BuildRoot: %{tmpdir}/%{name}-%{version}-root-%(id -u -n) + +%define _libdir %{_prefix}/%{_lib}/%{name} +%define _lighttpddir /home/services/%{name} +%define _sysconfdir /etc/%{name} + +%description +lighttpd is a secure, fast, compliant and very flexible web-server +which has been optimized for high-performance environments. It has a +very low memory footprint compared to other webservers and takes care +of cpu-load. Its advanced feature-set (FastCGI, CGI, Auth, +Output-Compression, URL-Rewriting and many more) make lighttpd the +perfect webserver-software for every server that is suffering load +problems. + +%description -l pl.UTF-8 +lighttpd jest bezpiecznym, szybkim, przyjaznym i bardzo elastycznym +serwerem WWW, który został zoptymalizowany pod kątem +wysokowydajnościowych środowisk. Zajmuje bardzo małą ilość pamięci w +porównaniu do innych serwerów WWW oraz dba o zajętość procesora. +Szeroki zestaw opcji (FastCGI, CGI, uwierzytelnianie, kompresja +wyjścia, przepisywanie URL-i i wiele innych) czynią z lighttpd +doskonałe oprogramowanie web-serwerowe na każdy serwer cierpiący z +powodu problemów z obciążeniem. + +%package mod_access +Summary: lighttpd module for making access restrictions +Summary(pl.UTF-8): Moduł lighttpd ograniczający dostęp +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModAccess +Requires: %{name} = %{version}-%{release} +Provides: webserver(access) + +%description mod_access +The access module is used to deny access to files with given trailing +path names. + +%description mod_access -l pl.UTF-8 +Moduł access służy do ograniczania dostępu do plików o podanych +ścieżkach. + +%package mod_accesslog +Summary: lighttpd module to record access logs +Summary(pl.UTF-8): Moduł lighttpd do zapisu logów dostępu +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModAccessLog +Requires: %{name} = %{version}-%{release} + +%description mod_accesslog +CLF like by default, flexible like Apache. + +%description mod_accesslog -l pl.UTF-8 +Domyślnie podobny do CLF, elastyczny jak Apache. + +%package mod_alias +Summary: lighttpd module for making URL aliasing +Summary(pl.UTF-8): Moduł lighttpd odpowiadający za aliasy URL-i +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModAlias +Requires: %{name} = %{version}-%{release} +Provides: webserver(alias) + +%description mod_alias +The alias module is used to specify a special document-root for a +given URL-subset. + +%description mod_alias -l pl.UTF-8 +Modul alias służy do określania specjalnego drzewa (document-roota) +dla podanego podzbioru URL-i. + +%package mod_auth +Summary: lighttpd module for authentication support +Summary(pl.UTF-8): Moduł lighttpd do obsługi uwierzytelniania +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModAuth +Requires: %{name} = %{version}-%{release} +Provides: webserver(auth) + +%description mod_auth +lighttpd supportes both authentication method described by RFC 2617: +basic and digest. + +%description mod_auth -l pl.UTF-8 +lighttpd obsługuje obie metody uwierzytelniania opisane w RFC 2617: +basic i digest. + +%package mod_cgi +Summary: lighttpd module for CGI handling +Summary(pl.UTF-8): Moduł lighttpd do obsługi CGI +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModCGI +Requires: %{name} = %{version}-%{release} +Requires: %{name}-mod_alias = %{version}-%{release} +Provides: webserver(cgi) + +%description mod_cgi +The cgi module provides a CGI-conforming interface. + +CGI programs allow you to enhance the functionality of the server in a +very straight and simple way. + +%description mod_cgi -l pl.UTF-8 +Moduł cgi udostępnia interfejs zgodny z CGI. + +Programy CGI pozwalają rozszerzać funkcjonalność serwera w bardzo +prosty i naturalny sposób. + +%package mod_cgi_php +Summary: lighttpd module for CGI handling PHP scripts +Summary(pl.UTF-8): Moduł lighttpd do obsługi skryptów PHP przez CGI +Group: Networking/Daemons/HTTP +Requires: %{name}-mod_cgi = %{version}-%{release} +Requires: php(cgi) +Provides: webserver(php) + +%description mod_cgi_php +The cgi module provides a CGI-conforming interface for PHP scripts. + +CGI programs allow you to enhance the functionality of the server in a +very straight and simple way. + +%description mod_cgi_php -l pl.UTF-8 +Moduł cgi udostępnia interfejs zgodny z CGI do wywoływania skryptów +PHP. + +Programy CGI pozwalają rozszerzać funkcjonalność serwera w bardzo +prosty i naturalny sposób. + +%package mod_cml +Summary: lighttpd module for Cache Meta Language +Summary(pl.UTF-8): Moduł Cache Meta Language +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModCML +Requires: %{name} = %{version}-%{release} + +%description mod_cml +CML is a Meta language to describe the dependencies of a page at one +side and building a page from its fragments on the other side using +LUA. + +%description mod_cml -l pl.UTF-8 +CML to metajęzyk służący z jednej strony do opisu zależności strony i +z drugiej strony do budowania strony z fragmentów przy użyciu LUA. + +%package mod_compress +Summary: lighttpd module for output compression +Summary(pl.UTF-8): Moduł lighttpd do kompresji wyjścia +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModCompress +Requires: %{name} = %{version}-%{release} + +%description mod_compress +Output compression reduces the network load and can improve the +overall throughput of the webserver. + +Only static content is supported up to now. + +The server negotiates automatically which compression method is used. +Supported are gzip, deflate, bzip. + +%description mod_compress -l pl.UTF-8 +Kompresja wyjścia zmniejsza obciążenie sieci i może poprawić całkowitą +przepustowość serwera WWW. + +Jak na razie obsługiwana jest tylko statyczna treść. + +Serwer automatycznie negocjuje, która metoda kompresji jest używana. +Obsługiwane są gzip, deflate i bzip. + +%package mod_deflate +Summary: lighttpd module for output compression using deflate method +Summary(pl.UTF-8): Moduł lighttpd do kompresji wyjścia metodą deflate +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Mod_Deflate +Requires: %{name} = %{version}-%{release} + +%description mod_deflate +mod_deflate can compress any output from lighttpd static or dynamic. +It doesn't support caching compressed output like mod_compress. + +%description mod_deflate -l pl.UTF-8 +mod_deflate potrafi kompresować statyczne i dynamiczne wyjście z +lighttpd. Nie obsługuje cache'owania wyniku kompresji, jak robi to +mod_compress. + +%package mod_dirlisting +Summary: lighttpd module for directory listings +Summary(pl.UTF-8): Moduł lighttpd do tworzenia listingów katalogów +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModDirlisting +Requires: %{name} = %{version}-%{release} + +%description mod_dirlisting +mod_dirlisting generates HTML based directory listings with full CSS +control. + +%description mod_dirlisting -l pl.UTF-8 +mod_dirlisting tworzy listingi katalogów w formacie HTML z pełną +kontrolą CSS. + +%package mod_evasive +Summary: lighttpd evasive module +Summary(pl.UTF-8): Moduł evasive dla lighttpd +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModEvasive +Requires: %{name} = %{version}-%{release} + +%description mod_evasive +lighttpd evasive module. + +%description mod_evasive -l pl.UTF-8 +Moduł evasive dla lighttpd. + +%package mod_evhost +Summary: lighttpd module for enhanced virtual-hosting +Summary(pl.UTF-8): Moduł lighttpd rozszerzający obsługę hostów wirtualnych +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModEVhost +Requires: %{name} = %{version}-%{release} + +%description mod_evhost +mod_evhost builds the document-root based on a pattern which contains +wildcards. Those wildcards can represent parts of the submitted +hostname. + +%description mod_evhost -l pl.UTF-8 +mod_evhost tworzy document-root w oparciu o wzorzec zawierający znaki +wieloznaczne (wildcards). Znaki te reprezentują części przekazanej +nazwy hosta. + +%package mod_expire +Summary: lighttpd module for controlling the expiration of content in caches +Summary(pl.UTF-8): Moduł lighttpd sterujący wygasaniem treści w cache'ach +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModExpire +Requires: %{name} = %{version}-%{release} +Provides: webserver(expires) + +%description mod_expire +mod_expire controls the setting of the the Expire response header. + +%description mod_expire -l pl.UTF-8 +mod_expire steruje ustawianiem nagłówka odpowiedzi Expire. + +%package mod_extforward +Summary: lighttpd module to extract the client's "real" IP from X-Forwarded-For header +Summary(pl.UTF-8): Moduł lighttpd wyciągający "prawdziwy" IP klienta z nagłówka X-Forwarded-For +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/DocsModExtForward +Requires: %{name} = %{version}-%{release} + +%description mod_extforward +This module will extract the client's "real" IP from X-Forwarded-For +header which is added by Squid or other proxies. It might be useful +for servers behind reverse proxy servers. + +%description mod_extforward -l pl.UTF-8 +Ten moduł wyciąga "prawdziwy" IP klienta z nagłówka X-Forwarded-For +dodawanego przez Squida czy inne proxy. Może być przydatny dla +serwerów stojących za odwrotnymi serwerami proxy. + +%package mod_fastcgi +Summary: lighttpd module for FastCGI interface +Summary(pl.UTF-8): Moduł lighttpd do interfejsu FastCGI +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModFastCGI +Requires: %{name} = %{version}-%{release} + +%description mod_fastcgi +The FastCGI interface is the fastest and most secure way to interface +external process-handlers like Perl, PHP and your self-written +applications. + +%description mod_fastcgi -l pl.UTF-8 +Interfejs FastCGI to najszybszy i najbezpieczniejszy sposób +komunikacji z zewnętrznymi programami obsługującymi procesy, takimi +jak Perl, PHP czy własne aplikacje. + +%package mod_flv_streaming +Summary: lighttpd module for flv streaming +Summary(pl.UTF-8): Moduł lighttpd do streamingu flv +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModFLVStreaming +Requires: %{name} = %{version}-%{release} + +%description mod_flv_streaming +lighttpd module for flv streaming. + +%description mod_flv_streaming -l pl.UTF-8 +Moduł lighttpd do streamingu flv. + +%package mod_h264_streaming +Summary: lighttpd module for h264 streaming +Summary(pl.UTF-8): Moduł lighttpd do emisji strumieni h264 +License: CC 3.0 BY-NC-SA +Group: Networking/Daemons/HTTP +URL: http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-Lighttpd-Version2 +Requires: %{name} = %{version}-%{release} + +%description mod_h264_streaming +A lighttpd plugin for pseudo-streaming QuickTime/MPEG-4 files. + +%description mod_h264_streaming -l pl.UTF-8 +Moduł lighttpd do pseudostreamingu plików QuickTime/MPEG-4. + +%package mod_indexfile +Summary: lighttpd indexfile module +Summary(pl.UTF-8): Moduł indexfile dla lighttpd +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Index-file-names.Details +Requires: %{name} = %{version}-%{release} +Provides: webserver(indexfile) + +%description mod_indexfile +indexfile module. + +%description mod_indexfile -l pl.UTF-8 +Moduł indexfile. + +%package mod_magnet +Summary: lighttpd powermagnet module +Summary(pl.UTF-8): Moduł powermagnet dla lighttpd +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModMagnet +Requires: %{name} = %{version}-%{release} + +%description mod_magnet +mod_magnet is a module to control the request handling in lighty. + +%description mod_magnet -l pl.UTF-8 +mod_magnet to moduł sterujący obsługą żądań w lighty. + +%package mod_mysql_vhost +Summary: lighttpd module for MySQL based vhosting +Summary(pl.UTF-8): Moduł lighttpd obsługujący vhosty oparte na MySQL-u +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModMySQLVhost +Requires: %{name} = %{version}-%{release} +Conflicts: %{name}-mod_simple_vhost + +%description mod_mysql_vhost +This module provides virtual hosts (vhosts) based on a MySQL table. + +%description mod_mysql_vhost -l pl.UTF-8 +Ten moduł udostępnia wirtualne hosty (vhosty) oparte na tabeli MySQL. + +%package mod_proxy +Summary: lighttpd module for proxying requests +Summary(pl.UTF-8): Moduł lighttpd do przekazywania żądań +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModProxy +Requires: %{name} = %{version}-%{release} + +%description mod_proxy +The proxy module a simplest way to connect lighttpd to Java servers +which have a HTTP-interface. + +%description mod_proxy -l pl.UTF-8 +Moduł proxy to najprostszy sposób łączenia lighttpd z serwerami Javy +mającymi interfejs HTTP. + +%package mod_proxy_core +Summary: lighttpd module for proxying requests +Summary(pl.UTF-8): Moduł lighttpd do przekazywania żądań +Group: Networking/Daemons/HTTP +URL: http://blog.lighttpd.net/articles/2006/07/18/mod_proxy_core-commited-to-svn +Requires: %{name} = %{version}-%{release} + +%description mod_proxy_core +The proxy module a simplest way to connect lighttpd to java servers +which have a HTTP-interface. + +This is the new proxy code. + +%description mod_proxy_core -l pl.UTF-8 +Moduł proxy to najprostszy sposób łączenia lighttpd z serwerami Javy +mającymi interfejs HTTP. + +Ten pakiet zawiera nowy moduł proxy. + +%package mod_redirect +Summary: lighttpd module for URL redirects +Summary(pl.UTF-8): Moduł lighttpd do przekierowań URL-i +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModRedirect +Requires: %{name} = %{version}-%{release} + +%description mod_redirect +With mod_redirect module you can redirect a set of URLs externally. + +%description mod_redirect -l pl.UTF-8 +Przy użyciu modułu mod_redirect można przekierować zbiór URL-i na +zewnątrz. + +%package mod_rewrite +Summary: lighttpd module for internal redirects, URL rewrite +Summary(pl.UTF-8): Moduł lighttpd do wewnętrznych przekierowań i przepisywania URL-i +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModRewrite +Requires: %{name} = %{version}-%{release} +Provides: webserver(rewrite) + +%description mod_rewrite +This module allows you rewrite a set of URLs interally in the +webserver BEFORE they are handled. + +%description mod_rewrite -l pl.UTF-8 +Ten moduł pozwala na przepisywanie zbioru URL-i wewnętrznie w serwerze +WWW _przed_ ich obsługą. + +%package mod_rrdtool +Summary: lighttpd module for monitoring traffic and server load +Summary(pl.UTF-8): Moduł lighttpd do monitorowania ruchu i obciążenia serwera +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModRRDTool +Requires: %{name} = %{version}-%{release} +Requires: rrdtool + +%description mod_rrdtool +RRD is a system to store and display time-series data (i.e. network +bandwidth, machine-room temperature, server load average). + +With this module you can monitor the traffic and load on the +webserver. + +%description mod_rrdtool -l pl.UTF-8 +RRD to system przechowywania i wyświetlania danych zależnych od czasu +(np. obciążenia sieci, temperatury w serwerowni, średniego obciążenia +serwera). + +Przy użyciu tego modułu można monitorować ruch i obciążenie serwera +WWW. + +%package mod_scgi +Summary: lighttpd module for SCGI interface +Summary(pl.UTF-8): Moduł lighttpd do interfejsu SCGI +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModSCGI +Requires: %{name} = %{version}-%{release} + +%description mod_scgi +SCGI is a fast and simplified CGI interface. It is mostly used by +Python + WSGI. + +%description mod_scgi -l pl.UTF-8 +SCGI to szybki i uproszczony interfejs CGI. Jest używany głównie przez +Pythona z WSGI. + +%package mod_secdownload +Summary: lighttpd module for secure and fast downloading +Summary(pl.UTF-8): Moduł lighttpd do bezpiecznego i szybkiego ściągania danych +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModSecDownload +Requires: %{name} = %{version}-%{release} + +%description mod_secdownload +With this module you can easily achieve authenticated file requests +and a countermeasure against deep-linking. + +%description mod_secdownload -l pl.UTF-8 +Przy użyciu tego modułu można łatwo umożliwić ściąganie plików z +uwierzytelnieniem i zapobiec używaniu bezpośrednich odnośników. + +%package mod_setenv +Summary: lighttpd module for setting conditional request headers +Summary(pl.UTF-8): Moduł lighttpd do ustawiania warunkowych nagłówków żądań +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModSetEnv +Requires: %{name} = %{version}-%{release} +Provides: webserver(setenv) + +%description mod_setenv +mod_setenv is used to add request headers. + +%description mod_setenv -l pl.UTF-8 +mod_setenv służy do dodawania nagłówków żądań. + +%package mod_simple_vhost +Summary: lighttpd module for simple virtual-hosting +Summary(pl.UTF-8): Moduł lighttpd do prostych hostów wirtualnych +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModSimpleVhost +Requires: %{name} = %{version}-%{release} +Conflicts: %{name}-mod_mysql_vhost + +%description mod_simple_vhost +lighttpd module for simple virtual-hosting. + +%description mod_simple_vhost -l pl.UTF-8 +Moduł lighttpd do prostych hostów wirtualnych. + +%package mod_ssi +Summary: lighttpd module for server-side includes +Summary(pl.UTF-8): Moduł lighttpd do SSI (server-side includes) +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModSSI +Requires: %{name} = %{version}-%{release} + +%description mod_ssi +The module for server-side includes provides a compatability layer for +NSCA/Apache SSI. + +%description mod_ssi -l pl.UTF-8 +Moduł server-side includes udostępnia warstwę kompatybilności z SSI +znanym z NSCA/Apache'a. + +%package mod_staticfile +Summary: lighttpd module for static file serving +Summary(pl.UTF-8): Moduł lighttpd do serwowania statycznych plików +Group: Networking/Daemons/HTTP +Requires: %{name} = %{version}-%{release} + +%description mod_staticfile +lighttpd module for static file serving. + +%description mod_staticfile -l pl.UTF-8 +Moduł lighttpd do serwowania statycznych plików. + +%package mod_status +Summary: lighttpd module for displaying server status +Summary(pl.UTF-8): Moduł lighttpd do wyświetlania stanu serwera +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModStatus +Requires: %{name} = %{version}-%{release} + +%description mod_status +mod_status displays the server's status and configuration. + +%description mod_status -l pl.UTF-8 +mod_status wyświetla stan i konfigurację serwera. + +%package mod_trigger_b4_dl +Summary: Trigger before Download +Summary(pl.UTF-8): Wyzwalacz przed ściąganiem +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModTriggerBeforeDownload +Requires: %{name} = %{version}-%{release} + +%description mod_trigger_b4_dl +Another anti hot-linking module. + +%description mod_trigger_b4_dl -l pl.UTF-8 +Jeszcze jeden moduł blokujący bezpośrednie linkowanie. + +%package mod_userdir +Summary: lighttpd module for user homedirs +Summary(pl.UTF-8): Moduł lighttpd obsługujący katalogi domowe użytkowników +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModUserDir +Requires: %{name} = %{version}-%{release} + +%description mod_userdir +The userdir module provides a simple way to link user-based +directories into the global namespace of the webserver. + +%description mod_userdir -l pl.UTF-8 +Moduł userdir udostępnia prosty sposób włączenia katalogów +użytkowników do globalnej przestrzeni nazw serwera WWW. + +%package mod_usertrack +Summary: lighttpd usertrack module +Summary(pl.UTF-8): Moduł usertrack dla lighttpd +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModUserTrack +Requires: %{name} = %{version}-%{release} + +%description mod_usertrack +lighttpd usertrack module. + +%description mod_usertrack -l pl.UTF-8 +Moduł usertrack dla lighttpd. + +%package mod_webdav +Summary: WebDAV module for lighttpd +Summary(pl.UTF-8): Moduł WebDAV dla libghttpd +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModWebDAV +Requires: %{name} = %{version}-%{release} + +%description mod_webdav +The WebDAV module is a very minimalistic implementation of RFC 2518. +Minimalistic means that not all operations are implementated yet. + +So far we have +- PROPFIND +- OPTIONS +- MKCOL +- DELETE +- PUT +- LOCK (experimental) + +and the usual GET, POST, HEAD from HTTP/1.1. + +So far mounting a webdav resource into Windows XP works and the basic +litmus tests are passed. + +%description mod_webdav -l pl.UTF-8 +Moduł WebDAV to bardzo minimalistyczna implementacja RFC 2518. +Minimalistyczna oznacza, że jeszcze nie wszystkie operacje są +zaimplementowane. Jak na razie są: +- PROPFIND +- OPTIONS +- MKCOL +- DELETE +- PUT +- LOCK (experimental) + +oraz zwykłe GET, POST, HEAD z HTTP/1.1. + +Jak na razie montowanie zasobu webdav pod Windows XP działa i +podstawowe testy lakmusowe przechodzą. + +%package php-spawned +Summary: PHP support via FastCGI, spawned by lighttpd +Summary(pl.UTF-8): Obsługa PHP przez FastCGI, uruchamiane przez lighttpd +Group: Networking/Daemons/HTTP +Requires: %{name} = %{version}-%{release} +Requires: %{name}-mod_fastcgi = %{version}-%{release} +Requires: php(fcgi) +Provides: webserver(php) +Obsoletes: lighttpd-php-external + +%description php-spawned +PHP support via FastCGI, spawned by lighttpd. + +%description php-spawned -l pl.UTF-8 +Obsługa PHP przez FastCGI, uruchamiane przez lighttpd. + +%package php-external +Summary: PHP support via FastCGI, spawning controlled externally +Summary(pl.UTF-8): Obsługa PHP przez FastCGI, uruchamianie sterowane zewnętrznie +Group: Networking/Daemons/HTTP +Requires: %{name} = %{version}-%{release} +Requires: %{name}-mod_fastcgi = %{version}-%{release} +Suggests: php(fpm) +Suggests: php-fcgi-init +Obsoletes: lighttpd-php-spawned + +%description php-external +PHP support via FastCGI, spawning controlled externally. + +%description php-external -l pl.UTF-8 +Obsługa PHP przez FastCGI, uruchamianie sterowane zewnętrznie. + +%package ssl +Summary: lighttpd support for SSLv2 and SSLv3 +Summary(pl.UTF-8): Obsługa SSLv2 i SSLv3 dla lighttpd +Group: Networking/Daemons/HTTP +URL: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:SSL +Requires: %{name} = %{version}-%{release} +Suggests: ca-certificates + +%description ssl +lighttpd support for SSLv2 and SSLv3. + +%description ssl -l pl.UTF-8 +Obsługa SSLv2 i SSLv3 dla lighttpd. + +%package -n monit-rc-lighttpd +Summary: lighttpd support for monit +Summary(pl.UTF-8): Wsparcie lighttpd dla monit +Group: Applications/System +Requires: %{name} = %{version}-%{release} +Requires: monit + +%description -n monit-rc-lighttpd +monitrc file for monitoring lighttpd web server. + +%description -n monit-rc-lighttpd -l pl.UTF-8 +Plik monitrc do monitorowania serwera www lighttpd. + +%prep +%setup -q +#%patch100 -p0 +#%patch4 -p0 +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%{?with_deflate:%patch5 -p1} +%patch6 -p1 +%patch7 -p0 + +rm -f src/mod_ssi_exprparser.h # bad patching: should be removed by is emptied instead + +# build mime.types.conf +sh %{SOURCE6} /etc/mime.types +cp -p %{SOURCE14} PLD-TODO + +%if "%{pld_release}" == "ac" +%{__sed} -i -e 's/ serial_tests//' configure.ac +%endif + +%build +ver=$(awk '/AC_INIT/{a=$2;gsub(/[\[\],]/, "", a); print a}' configure.ac) +if [ "$ver" != "%{version}" ]; then + : configure.ac specifies wrong version + exit 1 +fi + +%{__libtoolize} +%{__aclocal} +%{__autoconf} +%{__automake} + +%configure \ + %{!?with_ipv6:--disable-ipv6} \ + %{!?with_largefile:--disable-lfs} \ + %{?with_valgrind:--with-valgrind} \ + %{?with_xattr:--with-attr} \ + %{?with_mysql:--with-mysql} \ + %{?with_ldap:--with-ldap} \ + %{?with_ssl:--with-openssl} \ + %{?with_lua:--with-lua=lua51} \ + %{?with_memcache:--with-memcache} \ + %{?with_webdav_props:--with-webdav-props} \ + %{?with_webdav_locks:--with-webdav-locks} \ + %{?with_gamin:--with-gamin} \ + %{?with_gdbm:--with-gdbm} + +# -j1 as src/mod_ssi_exprparser.h regeneration deps are broken +%{__make} -j1 + +%if %{with tests} +export LIGHTTPD_TEST_PORT=$((2048 + RANDOM % 10)) +%{__make} check +%endif + +%install +rm -rf $RPM_BUILD_ROOT +install -d $RPM_BUILD_ROOT{%{_lighttpddir}/{cgi-bin,html},/etc/{logrotate.d,rc.d/init.d,sysconfig,init,monit}} \ + $RPM_BUILD_ROOT%{_sysconfdir}/{conf,vhosts,webapps}.d \ + $RPM_BUILD_ROOT{/var/log/{%{name},archive/%{name}},/var/run/%{name}} \ + $RPM_BUILD_ROOT%{_datadir}/lighttpd/errordocs \ + $RPM_BUILD_ROOT/var/lib/lighttpd \ + $RPM_BUILD_ROOT/var/cache/lighttpd/mod_compress \ + $RPM_BUILD_ROOT{%{systemdtmpfilesdir},%{systemdunitdir}} + +%{__make} install \ + DESTDIR=$RPM_BUILD_ROOT + +install -p %{SOURCE1} $RPM_BUILD_ROOT/etc/rc.d/init.d/%{name} +cp -p %{SOURCE2} %{SOURCE3} mime.types.conf $RPM_BUILD_ROOT%{_sysconfdir} +cp -p %{SOURCE4} $RPM_BUILD_ROOT/etc/logrotate.d/%{name} +cp -p %{SOURCE5} $RPM_BUILD_ROOT/etc/sysconfig/%{name} +cp -p %{SOURCE12} $RPM_BUILD_ROOT/etc/monit/%{name}.monitrc +cp -p %{SOURCE15} $RPM_BUILD_ROOT/etc/init/%{name}.conf +cp -p %{SOURCE16} $RPM_BUILD_ROOT%{systemdtmpfilesdir}/%{name}.conf +cp -p %{SOURCE17} $RPM_BUILD_ROOT%{systemdunitdir}/%{name}.service + +rm -f $RPM_BUILD_ROOT%{_libdir}/*.la + +# Install lighttpd images +cp -p %{SOURCE7} %{SOURCE8} %{SOURCE9} $RPM_BUILD_ROOT%{_lighttpddir}/html +cp -p %{SOURCE10} $RPM_BUILD_ROOT%{_lighttpddir}/html/pld_button.png +cp -p %{SOURCE11} $RPM_BUILD_ROOT%{_lighttpddir}/html/index.html + +# NOTE: the order of the modules is somewhat important as the modules are +# handled in the way they are specified. mod_rewrite should always be the first +# module, mod_accesslog always the last. + +conf_available=$RPM_BUILD_ROOT%{_sysconfdir}/conf.d +conf_enabled=../ + +cp -p %{SOURCE117} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/10_mod_rewrite.conf +cp -p %{SOURCE116} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/11_mod_redirect.conf + +cp -p %{SOURCE100} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_access.conf +cp -p %{SOURCE102} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_alias.conf +cp -p %{SOURCE103} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_auth.conf +cp -p %{SOURCE104} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_cgi.conf +cp -p %{SOURCE137} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_cgi_php.conf +cp -p %{SOURCE105} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_cml.conf +cp -p %{SOURCE107} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_deflate.conf +cp -p %{SOURCE108} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_dirlisting.conf +cp -p %{SOURCE109} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_evasive.conf +cp -p %{SOURCE110} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_evhost.conf +cp -p %{SOURCE112} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_fastcgi.conf +cp -p %{SOURCE113} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_flv_streaming.conf +cp -p %{SOURCE136} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_h264_streaming.conf +cp -p %{SOURCE114} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_indexfile.conf +cp -p %{SOURCE115} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_proxy.conf +cp -p %{SOURCE118} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_rrdtool.conf +cp -p %{SOURCE119} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_scgi.conf +cp -p %{SOURCE120} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_secdownload.conf +cp -p %{SOURCE121} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_setenv.conf +cp -p %{SOURCE122} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_simple_vhost.conf +cp -p %{SOURCE123} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_ssi.conf +cp -p %{SOURCE124} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_staticfile.conf +cp -p %{SOURCE125} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_status.conf +cp -p %{SOURCE126} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_trigger_b4_dl.conf +cp -p %{SOURCE127} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_userdir.conf +cp -p %{SOURCE128} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_usertrack.conf +cp -p %{SOURCE129} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_webdav.conf +cp -p %{SOURCE133} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/50_mod_mysql_vhost.conf + +cp -p %{SOURCE134} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/55_mod_magnet.conf +cp -p %{SOURCE111} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/55_mod_expire.conf + +cp -p %{SOURCE106} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/60_mod_compress.conf + +cp -p %{SOURCE101} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/90_mod_accesslog.conf +cp -p %{SOURCE135} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/91_mod_extforward.conf + +cp -p %{SOURCE130} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/php-spawned.conf +cp -p %{SOURCE131} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/php-external.conf +cp -p %{SOURCE132} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/ssl.conf + +touch $RPM_BUILD_ROOT/var/lib/lighttpd/lighttpd.rrd + +install -d $RPM_BUILD_ROOT/etc/tmpwatch +cp -p %{SOURCE138} $RPM_BUILD_ROOT/etc/tmpwatch/lighttpd-mod_compress.conf + +%if %{without mysql} +# avoid packaging dummy module +%{__rm} $RPM_BUILD_ROOT%{_libdir}/mod_mysql_vhost.so +%{__rm} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/*_mod_mysql_vhost.conf +%endif +%if %{without deflate} +%{__rm} $RPM_BUILD_ROOT%{_sysconfdir}/conf.d/*_mod_deflate.conf +%endif + +touch $RPM_BUILD_ROOT/var/log/%{name}/{access,error,breakage}.log + +%clean +rm -rf $RPM_BUILD_ROOT + +%pre +%groupadd -g 109 lighttpd +%groupadd -g 51 http +%useradd -u 116 -d %{_lighttpddir} -c "Lighttpd User" -g lighttpd lighttpd +%addusertogroup lighttpd http + +%post +for a in access.log error.log breakage.log; do + if [ ! -f /var/log/%{name}/$a ]; then + touch /var/log/%{name}/$a + chown lighttpd:lighttpd /var/log/%{name}/$a + chmod 644 /var/log/%{name}/$a + fi +done +/sbin/chkconfig --add %{name} +%systemd_post %{name}.service + +%preun +if [ "$1" = "0" ]; then + %service %{name} stop + /sbin/chkconfig --del %{name} +fi +%systemd_preun %{name}.service + +%postun +if [ "$1" = "0" ]; then + %userremove lighttpd + %groupremove lighttpd + %groupremove http +fi +%systemd_reload + +%posttrans +# minimizing lighttpd restarts logics. we restart webserver: +# +# 1. at the end of transaction. (posttrans, feature from rpm 4.4.2) +# 2. first install of module (post: $1 = 1) +# 2. uninstall of module (postun: $1 = 0) +# +# the strict internal deps between lighttpd modules and +# main package are very important for all this to work. +%service %{name} restart "Lighttpd webserver" +exit 0 + +# macro called at module post scriptlet +%define module_post \ +if [ "$1" = "1" ]; then \ + %service -q lighttpd restart \ +fi + +# macro called at module postun scriptlet +%define module_postun \ +if [ "$1" = "0" ]; then \ + %service -q lighttpd restart \ +fi + +# it's sooo annoying to write them +%define module_scripts() \ +%post %1 \ +%module_post \ +\ +%postun %1 \ +%module_postun + +%module_scripts mod_access +%module_scripts mod_accesslog +%module_scripts mod_alias +%module_scripts mod_auth +%module_scripts mod_cgi +%module_scripts mod_cml +%module_scripts mod_compress +%module_scripts mod_deflate +%module_scripts mod_dirlisting +%module_scripts mod_evasive +%module_scripts mod_evhost +%module_scripts mod_expire +%module_scripts mod_extforward +%module_scripts mod_fastcgi +%module_scripts mod_flv_streaming +%module_scripts mod_h264_streaming +%module_scripts mod_indexfile +%module_scripts mod_magnet +%module_scripts mod_mysql_vhost +%module_scripts mod_proxy +%module_scripts mod_redirect +%module_scripts mod_rewrite + +%post mod_rrdtool +if [ ! -f /var/lib/lighttpd/lighttpd.rrd ]; then + touch /var/lib/lighttpd/lighttpd.rrd + chown lighttpd:stats /var/lib/lighttpd/lighttpd.rrd + chmod 640 /var/lib/lighttpd/lighttpd.rrd +fi +%module_post + +%postun mod_rrdtool +%module_postun + +%module_scripts mod_scgi +%module_scripts mod_secdownload +%module_scripts mod_setenv +%module_scripts mod_simple_vhost +%module_scripts mod_ssi +%module_scripts mod_staticfile +%module_scripts mod_status +%module_scripts mod_trigger_b4_dl +%module_scripts mod_userdir +%module_scripts mod_usertrack +%module_scripts mod_webdav + +%module_scripts php-spawned +%module_scripts php-external + +%triggerpostun -- %{name} < 1.4.18-10.1 +if [ -f /etc/lighttpd/conf.d/50_mod_extforward.conf.rpmsave ]; then + cp -f /etc/lighttpd/conf.d/91_mod_extforward.conf{,.rpmnew} + mv -f /etc/lighttpd/conf.d/{50_mod_extforward.conf.rpmsave,91_mod_extforward.conf} +fi + +%files +%defattr(644,root,root,755) +%doc NEWS README PLD-TODO +%dir %attr(751,root,lighttpd) %{_sysconfdir} +%dir %attr(750,root,root) %{_sysconfdir}/conf.d +%dir %attr(750,root,root) %{_sysconfdir}/vhosts.d +%dir %attr(750,root,root) %{_sysconfdir}/webapps.d +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/%{name}.conf +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/mime.types.conf +%attr(640,root,lighttpd) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/*.user +%config(noreplace) %verify(not md5 mtime size) /etc/init/%{name}.conf + +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) /etc/logrotate.d/%{name} +%attr(750,root,root) %dir /var/log/archive/%{name} +%dir %attr(751,root,root) /var/log/%{name} +%attr(644,lighttpd,lighttpd) %ghost /var/log/%{name}/access.log +%attr(644,lighttpd,lighttpd) %ghost /var/log/%{name}/error.log +%attr(644,lighttpd,lighttpd) %ghost /var/log/%{name}/breakage.log +%dir %attr(770,root,lighttpd) /var/run/%{name} +%{systemdtmpfilesdir}/%{name}.conf +%{systemdunitdir}/%{name}.service +%attr(754,root,root) /etc/rc.d/init.d/%{name} +%config(noreplace) %verify(not md5 mtime size) /etc/sysconfig/* +%attr(755,root,root) %{_sbindir}/lighttpd +%attr(755,root,root) %{_sbindir}/lighttpd-angel +%dir %{_libdir} +%{_mandir}/man8/lighttpd.8* +%dir %{_lighttpddir} +%dir %{_lighttpddir}/cgi-bin +%dir %{_lighttpddir}/html +%config(noreplace,missingok) %verify(not md5 mtime size) %{_lighttpddir}/html/index.html +%config(missingok) %verify(not md5 mtime size) %{_lighttpddir}/html/*.png +%config(missingok) %verify(not md5 mtime size) %{_lighttpddir}/html/*.ico + +%dir %{_datadir}/lighttpd +%dir %{_datadir}/lighttpd/errordocs + +# rrdtool database is stored there +%dir %attr(771,root,lighttpd) /var/lib/lighttpd + +# mod_compress can put cached files there +%dir /var/cache/lighttpd + +%files mod_access +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_access.conf +%attr(755,root,root) %{_libdir}/mod_access.so + +%files mod_accesslog +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_accesslog.conf +%attr(755,root,root) %{_libdir}/mod_accesslog.so + +%files mod_alias +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_alias.conf +%attr(755,root,root) %{_libdir}/mod_alias.so + +%files mod_auth +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_auth.conf +%attr(755,root,root) %{_libdir}/mod_auth.so + +%files mod_cgi +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_cgi.conf +%attr(755,root,root) %{_libdir}/mod_cgi.so + +%files mod_cgi_php +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_cgi_php.conf + +%files mod_cml +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_cml.conf +%attr(755,root,root) %{_libdir}/mod_cml.so + +%files mod_compress +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) /etc/tmpwatch/lighttpd-mod_compress.conf +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_compress.conf +%attr(755,root,root) %{_libdir}/mod_compress.so +%dir %attr(775,root,lighttpd) /var/cache/lighttpd/mod_compress + +%if %{with deflate} +%files mod_deflate +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_deflate.conf +%attr(755,root,root) %{_libdir}/mod_deflate.so +%endif + +%files mod_dirlisting +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_dirlisting.conf +%attr(755,root,root) %{_libdir}/mod_dirlisting.so + +%files mod_evasive +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_evasive.conf +%attr(755,root,root) %{_libdir}/mod_evasive.so + +%files mod_evhost +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_evhost.conf +%attr(755,root,root) %{_libdir}/mod_evhost.so + +%files mod_expire +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_expire.conf +%attr(755,root,root) %{_libdir}/mod_expire.so + +%files mod_extforward +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_extforward.conf +%attr(755,root,root) %{_libdir}/mod_extforward.so + +%files mod_fastcgi +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_fastcgi.conf +%attr(755,root,root) %{_libdir}/mod_fastcgi.so + +%files mod_flv_streaming +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_flv_streaming.conf +%attr(755,root,root) %{_libdir}/mod_flv_streaming.so + +%files mod_h264_streaming +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_h264_streaming.conf +%attr(755,root,root) %{_libdir}/mod_h264_streaming.so + +%files mod_indexfile +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_indexfile.conf +%attr(755,root,root) %{_libdir}/mod_indexfile.so + +%files mod_magnet +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_magnet.conf +%attr(755,root,root) %{_libdir}/mod_magnet.so + +%if %{with mysql} +%files mod_mysql_vhost +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_mysql_vhost.conf +%attr(755,root,root) %{_libdir}/mod_mysql_vhost.so +%endif + +%files mod_proxy +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_proxy.conf +%attr(755,root,root) %{_libdir}/mod_proxy.so + +%files mod_redirect +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_redirect.conf +%attr(755,root,root) %{_libdir}/mod_redirect.so + +%files mod_rewrite +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_rewrite.conf +%attr(755,root,root) %{_libdir}/mod_rewrite.so + +%files mod_rrdtool +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_rrdtool.conf +%attr(755,root,root) %{_libdir}/mod_rrdtool.so +%attr(640,lighttpd,stats) %ghost /var/lib/lighttpd/lighttpd.rrd + +%files mod_scgi +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_scgi.conf +%attr(755,root,root) %{_libdir}/mod_scgi.so + +%files mod_secdownload +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_secdownload.conf +%attr(755,root,root) %{_libdir}/mod_secdownload.so + +%files mod_setenv +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_setenv.conf +%attr(755,root,root) %{_libdir}/mod_setenv.so + +%files mod_simple_vhost +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_simple_vhost.conf +%attr(755,root,root) %{_libdir}/mod_simple_vhost.so + +%files mod_ssi +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_ssi.conf +%attr(755,root,root) %{_libdir}/mod_ssi.so + +%files mod_staticfile +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_staticfile.conf +%attr(755,root,root) %{_libdir}/mod_staticfile.so + +%files mod_status +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_status.conf +%attr(755,root,root) %{_libdir}/mod_status.so + +%files mod_trigger_b4_dl +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_trigger_b4_dl.conf +%attr(755,root,root) %{_libdir}/mod_trigger_b4_dl.so + +%files mod_userdir +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_userdir.conf +%attr(755,root,root) %{_libdir}/mod_userdir.so + +%files mod_usertrack +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_usertrack.conf +%attr(755,root,root) %{_libdir}/mod_usertrack.so + +%files mod_webdav +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/*mod_webdav.conf +%attr(755,root,root) %{_libdir}/mod_webdav.so + +%files php-spawned +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/php-spawned.conf + +%files php-external +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/php-external.conf + +%files ssl +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/conf.d/ssl.conf + +%files -n monit-rc-lighttpd +%defattr(644,root,root,755) +%attr(640,root,root) %config(noreplace) %verify(not md5 mtime size) /etc/monit/%{name}.monitrc diff --git a/lighttpd.sysconfig b/lighttpd.sysconfig new file mode 100644 index 0000000..27e071f --- /dev/null +++ b/lighttpd.sysconfig @@ -0,0 +1,15 @@ +# Customized settings for lighttpd + +# Define nice level for lighttpd +#SERVICE_RUN_NICE_LEVEL="+0" + +# Use lighttpd-angel +# Allows graceful restart and crash recovery. +# Off by default but consider enabling +#LIGHT_ANGEL="yes" + +# Give it 4096 fd's. +#SERVICE_LIMITS="$DEFAULT_SERVICE_LIMITS -n 4096" + +# Daemon options, for IPV6 set server.use-ipv6 config variable! +#HTTPD_OPTS="" diff --git a/lighttpd.tmpfiles b/lighttpd.tmpfiles new file mode 100644 index 0000000..3918a40 --- /dev/null +++ b/lighttpd.tmpfiles @@ -0,0 +1 @@ +d /var/run/lighttpd 0770 root lighttpd - diff --git a/lighttpd.upstart b/lighttpd.upstart new file mode 100644 index 0000000..8bb9b70 --- /dev/null +++ b/lighttpd.upstart @@ -0,0 +1,16 @@ +description "Lighttpd Web Server" + +start on pld.network-started +stop on pld.shutdown-started + +respawn + +console output + +env SHELL=/bin/sh + +script + # Source old-style service configuration + [ -f /etc/sysconfig/lighttpd ] && . /etc/sysconfig/lighttpd + exec /usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf $HTTPD_OPTS +end script diff --git a/lighttpd.user b/lighttpd.user new file mode 100644 index 0000000..57a29fc --- /dev/null +++ b/lighttpd.user @@ -0,0 +1 @@ +#dummy:test123 diff --git a/test-port-setup.patch b/test-port-setup.patch new file mode 100644 index 0000000..16c3b5d --- /dev/null +++ b/test-port-setup.patch @@ -0,0 +1,11 @@ +--- lighttpd-1.4.30/tests/LightyTest.pm~ 2010-02-02 01:28:20.000000000 +0200 ++++ lighttpd-1.4.30/tests/LightyTest.pm 2011-12-19 14:37:44.255526606 +0200 +@@ -43,7 +43,7 @@ + $self->{MODULES_PATH} = $self->{BASEDIR}.'/build'; + } + $self->{LIGHTTPD_PATH} = $self->{BINDIR}.'/lighttpd'; +- $self->{PORT} = 2048; ++ $self->{PORT} = $ENV{LIGHTTPD_TEST_PORT} ? int($ENV{LIGHTTPD_TEST_PORT}) : 2048; + + my ($name, $aliases, $addrtype, $net) = gethostbyaddr(inet_aton("127.0.0.1"), AF_INET); + -- 2.44.0