--- /dev/null
+# 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
+
+
--- /dev/null
+#!/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 &
--- /dev/null
+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"))) {
--- /dev/null
+# 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 <string.h>
+-
+-#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 <sys/resource.h>
+ #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 <stdlib.h>
+ #include <string.h>
+
+-#ifdef USE_OPENSSL
+-# include <openssl/md5.h>
+-#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 <openssl/rand.h>
+ #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 <dirent.h>
+ #include <stdio.h>
+
+-#ifdef USE_OPENSSL
+-# include <openssl/md5.h>
+-#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 <openssl/ssl.h>
+ # include <openssl/err.h>
+
+-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 <unistd.h>
+ #include <ctype.h>
+
+-#ifdef USE_OPENSSL
+-# include <openssl/md5.h>
+-#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 <stdlib.h>
+ #include <string.h>
+
+-#ifdef USE_OPENSSL
+-# include <openssl/md5.h>
+-#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("</td><td class=\"int\">"));
+
+- 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 <time.h>
+ #include <string.h>
+
+-#ifdef USE_OPENSSL
+-# include <openssl/md5.h>
+-#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} = ( <<EOF
+ GET /server-status HTTP/1.0
++Authorization: Basic \x80mFuOmphb
++EOF
++ );
++$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ];
++ok($tf->handle_http($t) == 0, 'Basic-Auth: Invalid base64 Auth-token');
++
++$t->{REQUEST} = ( <<EOF
++GET /server-status HTTP/1.0
+ Authorization: Basic amFuOmphb
+ EOF
+ );
+Index: tests/request.t
+===================================================================
+--- tests/request.t (.../tags/lighttpd-1.4.29)
++++ tests/request.t (.../branches/lighttpd-1.4.x)
+@@ -8,7 +8,7 @@
+
+ use strict;
+ use IO::Socket;
+-use Test::More tests => 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} = ( <<EOF
++GET /image.jpg/index.php HTTP/1.0
++EOF
++ );
++$t->{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} = ( <<EOF
++GET /image.jpg/index.php HTTP/1.0
++Host: zzz.example.org
++EOF
++ );
++$t->{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
+===================================================================
--- /dev/null
+--- 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);
--- /dev/null
+#!/bin/sh
+# Parse /etc/mime.types into lighttpd config format.
+# Copyright (c) 2005 Elan Ruusamäe <glen@pld-linux.org>
+
+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 <<EOF
+# charset used for "text/*" mimetypes
+# Apache's AddCharset equivalent. Leave empty to add no charset.
+# AddCharset "utf-8" would be "; charset=\"utf-8\""
+# See this post about what it affects:
+# http://lists.pld-linux.org/mailman/pipermail/pld-devel-en/2012-February/022499.html
+mimetype_textcharsetheader = ""
+#mimetype_textcharsetheader = "; charset=\"utf-8\""
+
+# mimetype mapping
+mimetype.assign = (
+EOF
+
+# save our hard work
+cat mime.types.conf.tmp >> mime.types.conf
+
+# footer
+cat >> mime.types.conf <<EOF
+)
+EOF
+
+rm -f mime.types.conf.tmp
--- /dev/null
+# Access module.
+#
+# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModAccess
+
+server.modules += (
+ "mod_access"
+)
+
+## deny access the file-extensions
+#
+# ~ is for backupfiles from vi, emacs, joe, ...
+# .inc is often used for code includes which should in general not be part
+# of the document-root
+# *,v and *,t - CVS files
+url.access-deny = ( "~", ".inc", ",v", ",t" )
+
+# forbid access to files inside CVS or RCS dirs
+$HTTP["url"] =~ "/(?:CVS|RCS)/" {
+ url.access-deny = ("")
+}
--- /dev/null
+# accesslog module.
+#
+# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModAccessLog
+
+server.modules += (
+ "mod_accesslog"
+)
+
+accesslog.filename = "/var/log/lighttpd/access.log"
+
+# Use syslog
+# accesslog.use-syslog = "enable"
+
+# Lighttpd default
+#accesslog.format = "%h %V %u %t \"%r\" %>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"
--- /dev/null
+# 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",
+#)
--- /dev/null
+# 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"
+# )
+#)
--- /dev/null
+# 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",
+#)
--- /dev/null
+# Map PHP via CGI module
+
+cgi.assign += (
+ ".php" => "/usr/bin/php.cgi",
+ ".php4" => "/usr/bin/php4.cgi",
+)
+
+static-file.exclude-extensions += (".php")
--- /dev/null
+# 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"
--- /dev/null
+# 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",
+#)
--- /dev/null
+# Cleanup lighttpd-mod_compress cache files:
+/var/cache/lighttpd/mod_compress -u 10d
--- /dev/null
+# Deflate module.
+#
+# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModDeflate
+
+server.modules += (
+ "mod_deflate"
+)
--- /dev/null
+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 <errno.h>
+ #include <string.h>
+
++#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 <sys/types.h>
++#include <sys/stat.h>
++
++#include <fcntl.h>
++#include <unistd.h>
++#include <ctype.h>
++#include <stdlib.h>
++#include <string.h>
++#include <errno.h>
++#include <time.h>
++#include <assert.h>
++
++#if defined(HAVE_PCRE_H)
++#include <pcre.h>
++#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 <zlib.h>
++#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 <bzlib.h>
++#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 &&
--- /dev/null
+# 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"
--- /dev/null
+--- 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;
+ }
+ }
--- /dev/null
+# 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
--- /dev/null
+# 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/"
--- /dev/null
+# 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",
+#)
--- /dev/null
+# 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
+# )
--- /dev/null
+# 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",
+#)
--- /dev/null
+# FLV streaming module.
+#
+# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFLVStreaming
+
+server.modules += (
+ "mod_flv_streaming"
+)
--- /dev/null
+# 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",
+)
--- /dev/null
+--- 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 <ctype.h>
++#include <stdlib.h>
++#include <string.h>
++#include <stdio.h>
++
++#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 <stdlib.h>
++#include <crtdbg.h>
++#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 <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <limits.h>
++#include <stdint.h>
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#ifdef HAVE_STDINT_H
++# include <stdint.h>
++#endif
++#ifdef HAVE_INTTYPES_H
++# include <inttypes.h>
++#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 <zlib.h>
++#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 <inttypes.h>
++#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
++
--- /dev/null
+# 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",
+)
--- /dev/null
+# 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 = ...
--- /dev/null
+# mysql_vhost module.
+#
+# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModMySQLVhost
+
+server.modules += (
+ "mod_mysql_vhost"
+)
--- /dev/null
+# 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" ),
+# )
+# )
+#}
--- /dev/null
+# 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" )
+#}
--- /dev/null
+# 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",
+#)
--- /dev/null
+# 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"
--- /dev/null
+# SCGI module.
+#
+# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModSCGI
+
+server.modules += (
+ "mod_scgi"
+)
--- /dev/null
+# secure and fast downloading module.
+#
+# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModSecDownload
+
+server.modules += (
+ "mod_secdownload"
+)
--- /dev/null
+# 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" => "*",
+# )
+#}
--- /dev/null
+# 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"
--- /dev/null
+# 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" )
--- /dev/null
+# 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"
--- /dev/null
+# 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"
+}
--- /dev/null
+# 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 <download-url>
+#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 <timeout> 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" )
--- /dev/null
+# 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"
--- /dev/null
+# usertrack module.
+#
+# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModUserTrack
+
+server.modules += (
+ "mod_usertrack"
+)
--- /dev/null
+# webdav module.
+#
+# Documentation: http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModWebDAV
+
+server.modules += (
+ "mod_webdav"
+)
--- /dev/null
+--- ./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;
--- /dev/null
+# 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")
--- /dev/null
+# 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")
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<!-- $Id$ -->
+<head>
+ <title>Powered by lighttpd</title>
+ <link rel="shortcut icon" href="favicon.ico" />
+ <link rel="icon" href="favicon.ico" />
+ <style type="text/css">
+ <!--
+ body {background: white; color: #666f85; text-align: center}
+ img {border: none}
+ -->
+ </style>
+</head>
+<body>
+
+<p>
+<img src="light_logo.png" width="249" height="239" alt="LIGHTTPD - fly light." />
+</p>
+
+<p>
+<a href="http://www.lighttpd.net/"><img src="light_button.png" width="80" height="27" alt="Powered by Lighttpd" /></a><br/>
+<a href="http://www.pld-linux.org/"><img src="pld_button.png" width="80" height="15" alt="Powered by PLD Linux" /></a>
+</p>
+
+</body>
+</html>
--- /dev/null
+# 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
+ }
+}
--- /dev/null
+## 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;
--- /dev/null
+# 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: <errorfile-prefix><status-code>.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 <uid> (default: don't care)
+server.username = "lighttpd"
+
+## change uid to <uid> (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"
--- /dev/null
+#!/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
--- /dev/null
+/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
+}
--- /dev/null
+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
+
+
--- /dev/null
+[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
--- /dev/null
+
+# 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
--- /dev/null
+# 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=""
--- /dev/null
+d /var/run/lighttpd 0770 root lighttpd -
--- /dev/null
+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
--- /dev/null
+#dummy:test123
--- /dev/null
+--- 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);
+