1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "apr_strings.h" 18#include "ap_config.h" 19#include "httpd.h" 20#include "http_config.h" 21#include "http_protocol.h" 22#include "http_log.h" 23#include "util_script.h" 24#include "http_main.h" 25#include "http_request.h" 26 27#include "mod_core.h" 28 29#define ASIS_MAGIC_TYPE "httpd/send-as-is" 30 31static int asis_handler(request_rec *r) 32{ 33 conn_rec *c = r->connection; 34 apr_file_t *f = NULL; 35 apr_status_t rv; 36 const char *location; 37 38 if(strcmp(r->handler,ASIS_MAGIC_TYPE) && strcmp(r->handler,"send-as-is")) 39 return DECLINED; 40 41 r->allowed |= (AP_METHOD_BIT << M_GET); 42 if (r->method_number != M_GET) 43 return DECLINED; 44 if (r->finfo.filetype == 0) { 45 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 46 "File does not exist: %s", r->filename); 47 return HTTP_NOT_FOUND; 48 } 49 50 if ((rv = apr_file_open(&f, r->filename, APR_READ, 51 APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) { 52 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, 53 "file permissions deny server access: %s", r->filename); 54 return HTTP_FORBIDDEN; 55 } 56 57 ap_scan_script_header_err(r, f, NULL); 58 location = apr_table_get(r->headers_out, "Location"); 59 60 if (location && location[0] == '/' && 61 ((r->status == HTTP_OK) || ap_is_HTTP_REDIRECT(r->status))) { 62 63 apr_file_close(f); 64 65 /* Internal redirect -- fake-up a pseudo-request */ 66 r->status = HTTP_OK; 67 68 /* This redirect needs to be a GET no matter what the original 69 * method was. 70 */ 71 r->method = apr_pstrdup(r->pool, "GET"); 72 r->method_number = M_GET; 73 74 ap_internal_redirect_handler(location, r); 75 return OK; 76 } 77 78 if (!r->header_only) { 79 apr_bucket_brigade *bb; 80 apr_bucket *b; 81 apr_off_t pos = 0; 82 83 rv = apr_file_seek(f, APR_CUR, &pos); 84 if (rv != APR_SUCCESS) { 85 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, 86 "mod_asis: failed to find end-of-headers position " 87 "for %s", r->filename); 88 apr_file_close(f); 89 return HTTP_INTERNAL_SERVER_ERROR; 90 } 91 92 bb = apr_brigade_create(r->pool, c->bucket_alloc); 93#if APR_HAS_LARGE_FILES 94 if (r->finfo.size - pos > AP_MAX_SENDFILE) { 95 /* APR_HAS_LARGE_FILES issue; must split into mutiple buckets, 96 * no greater than MAX(apr_size_t), and more granular than that 97 * in case the brigade code/filters attempt to read it directly. 98 */ 99 apr_off_t fsize = r->finfo.size - pos; 100 b = apr_bucket_file_create(f, pos, AP_MAX_SENDFILE, 101 r->pool, c->bucket_alloc); 102 while (fsize > AP_MAX_SENDFILE) { 103 APR_BRIGADE_INSERT_TAIL(bb, b); 104 apr_bucket_copy(b, &b); 105 b->start += AP_MAX_SENDFILE; 106 fsize -= AP_MAX_SENDFILE; 107 } 108 b->length = (apr_size_t)fsize; /* Resize just the last bucket */ 109 } 110 else 111#endif 112 b = apr_bucket_file_create(f, pos, (apr_size_t) (r->finfo.size - pos), 113 r->pool, c->bucket_alloc); 114 APR_BRIGADE_INSERT_TAIL(bb, b); 115 b = apr_bucket_eos_create(c->bucket_alloc); 116 APR_BRIGADE_INSERT_TAIL(bb, b); 117 rv = ap_pass_brigade(r->output_filters, bb); 118 if (rv != APR_SUCCESS) { 119 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, 120 "mod_asis: ap_pass_brigade failed for file %s", r->filename); 121 return HTTP_INTERNAL_SERVER_ERROR; 122 } 123 } 124 else { 125 apr_file_close(f); 126 } 127 128 return OK; 129} 130 131static void register_hooks(apr_pool_t *p) 132{ 133 ap_hook_handler(asis_handler,NULL,NULL,APR_HOOK_MIDDLE); 134} 135 136module AP_MODULE_DECLARE_DATA asis_module = 137{ 138 STANDARD20_MODULE_STUFF, 139 NULL, /* create per-directory config structure */ 140 NULL, /* merge per-directory config structures */ 141 NULL, /* create per-server config structure */ 142 NULL, /* merge per-server config structures */ 143 NULL, /* command apr_table_t */ 144 register_hooks /* register hooks */ 145}; 146