/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * mod_bunzip2.c: pipe documents through a "send-bunzip2" handler. * * Written and copyrighted by Helge Oldach * * 04 February 2001 initial release (naive adaption of mod_gunzip) * 29 July 2002 Remove ourselves as the default handler. * */ #include "httpd.h" #include "http_config.h" #include "http_request.h" #include "http_protocol.h" #include "http_log.h" #include "http_main.h" #include "util_script.h" #include "bzlib.h" #ifndef BUFSIZ /* should come from stdio.h */ #define BUFSIZ 4096 #endif static int bunzip2_handler(request_rec *r) { FILE *f, *fnew; int errstatus; request_rec *parent; BZFILE *zf; unsigned char peek[2]; const char *accept; r->allowed |= (1 << M_GET); if (r->method_number != M_GET) return DECLINED; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r, r->content_encoding ? "---%s (%s)---" : "---%s---", r->filename, r->content_encoding); if (r->finfo.st_mode == 0) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "File does not exist: %s", (r->path_info ? ap_pstrcat(r->pool, r->filename, r->path_info, NULL) : r->filename)); return HTTP_NOT_FOUND; } if (!(f = ap_pfopen(r->pool, r->filename, "r"))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "file permissions deny server access: %s", r->filename); return HTTP_FORBIDDEN; } /* cannot clone the FILE* but need to open separately for peek-ahead otherwise BZ2_bzdopen gets confused */ if (!(fnew = ap_pfopen(r->pool, r->filename, "r")) || fread(peek, sizeof peek[0], sizeof peek, fnew) != sizeof peek || ap_pfclose(r->pool, fnew)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "Cannot peek at %d bytes: %s", sizeof peek[0] * sizeof peek, r->filename); return DECLINED; } if (peek[0] != 'B' || peek[1] != 'Z') { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r, "Not bzip2 format (0x%02.2x 0x%02.2x): %s", peek[0], peek[1], r->filename); return DECLINED; } if ((accept = ap_table_get(r->headers_in, "Accept-Encoding")) != NULL && strstr(accept, "bzip2") != NULL) { r->content_encoding = ap_pstrdup(r->pool, "bzip2"); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r, "Client accepts bzip2, no bunzip2 needed: %s", r->filename); return DECLINED; } if (r->content_encoding != NULL && strstr(r->content_encoding, "bzip2") != NULL) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r, "Content-Encoding \"%s\" unset", r->content_encoding); r->content_encoding = NULL; } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r, "Client doesn't accept bzip2: %s", r->filename); #ifdef PLAIN #else if (!(zf = BZ2_bzdopen(fileno(f), "r"))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "file permissions deny server access: %s", r->filename); return HTTP_FORBIDDEN; } #endif ap_update_mtime(r, r->finfo.st_mtime); ap_set_last_modified(r); ap_set_etag(r); /* not supported: ap_table_setn(r->headers_out, "Accept-Ranges", "bytes"); */ if (((errstatus = ap_meets_conditions(r)) != OK) /* not supported: || (errstatus = ap_set_content_length(r, r->finfo.st_size)) */) { #ifdef PLAIN #else BZ2_bzclose(zf); #endif return errstatus; } ap_send_http_header(r); if (!r->header_only) { static char buf[BUFSIZ]; int nread, nwritten; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r, "Bunzip2 file: \"%s\"", r->filename); while ((nread = #ifdef PLAIN fread(buf, 1, BUFSIZ, f) #else BZ2_bzread(zf, buf, BUFSIZ) #endif ) > 0) { ap_soft_timeout("send bunzip2", r); while (!r->connection->aborted && nread > 0) { nwritten = ap_rwrite(buf, nread, r); if (nwritten > 0) { ap_reset_timeout(r); nread -= nwritten; if (nread > 0) /* we're lazy... */ memmove(buf, &buf[nwritten], nread); } else if (nwritten < 0) { if (r->connection->aborted) break; else if (errno == EAGAIN) continue; else { ap_log_rerror(APLOG_MARK, APLOG_INFO, r, "client stopped connection before send completed"); ap_bsetflag(r->connection->client, B_EOUT, 1); r->connection->aborted = 1; break; } } } ap_kill_timeout(r); if (r->sent_bodyct) ap_bgetopt(r->connection->client, BO_BYTECT, &r->bytes_sent); } } #ifdef PLAIN #else BZ2_bzclose(zf); #endif ap_pfclose(r->pool, f); return OK; } static const handler_rec bunzip2_handlers[] = { {"send-bunzip2", bunzip2_handler}, {"*/*", bunzip2_handler}, {NULL} }; module MODULE_VAR_EXPORT bunzip2_module = { STANDARD_MODULE_STUFF, NULL, /* initializer */ NULL, /* dir config creater */ NULL, /* dir merger --- default is to override */ NULL, /* server config */ NULL, /* merge server config */ NULL, /* command table */ bunzip2_handlers, /* handlers */ NULL, /* filename translation */ NULL, /* check_user_id */ NULL, /* check auth */ NULL, /* check access */ NULL, /* type_checker */ NULL, /* fixups */ NULL, /* logger */ NULL, /* header parser */ NULL, /* child_init */ NULL, /* child_exit */ NULL /* post read-request */ };