~xibo-maintainers/xibo/tempel

« back to all changes in this revision

Viewing changes to docker/mod_xsendfile.c

  • Committer: Dan Garner
  • Date: 2016-02-12 10:41:25 UTC
  • mto: (454.4.130)
  • mto: This revision was merged to the branch mainline in revision 484.
  • Revision ID: git-v1:ca673f8ea522eac5f311ed779b0fbfeb35a0e4dd
Handle duration changes in XLF generation. Fixed region option factory.
xibosignage/xibo#721

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright 2006-2010 by Nils Maier
2
 
 *
3
 
 * Licensed under the Apache License, Version 2.0 (the "License");
4
 
 * you may not use this file except in compliance with the License.
5
 
 * You may obtain a copy of the License at
6
 
 *
7
 
 *     http://www.apache.org/licenses/LICENSE-2.0
8
 
 *
9
 
 * Unless required by applicable law or agreed to in writing, software
10
 
 * distributed under the License is distributed on an "AS IS" BASIS,
11
 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
 
 * See the License for the specific language governing permissions and
13
 
 * limitations under the License.
14
 
 */
15
 
 
16
 
/*
17
 
 * mod_xsendfile.c: Process X-SENDFILE header cgi/scripts may set
18
 
 *     Written by Nils Maier, testnutzer123 at google mail, March 2006
19
 
 *
20
 
 * Whenever an X-SENDFILE header occures in the response headers drop
21
 
 * the body and send the replacement file idenfitied by this header instead.
22
 
 *
23
 
 * Method inspired by lighttpd <http://lighttpd.net/>
24
 
 * Code inspired by mod_headers, mod_rewrite and such
25
 
 *
26
 
 * Installation:
27
 
 *     apxs2 -cia mod_xsendfile.c
28
 
 */
29
 
 
30
 
/*
31
 
 * v0.12 (peer-review still required)
32
 
 *
33
 
 * $Id$
34
 
 */
35
 
 
36
 
#include "apr.h"
37
 
#include "apr_lib.h"
38
 
#include "apr_strings.h"
39
 
#include "apr_buckets.h"
40
 
#include "apr_file_io.h"
41
 
 
42
 
#include "apr_hash.h"
43
 
#define APR_WANT_IOVEC
44
 
#define APR_WANT_STRFUNC
45
 
#include "apr_want.h"
46
 
 
47
 
#include "httpd.h"
48
 
#include "http_log.h"
49
 
#include "http_config.h"
50
 
#include "http_log.h"
51
 
#define CORE_PRIVATE
52
 
#include "http_request.h"
53
 
#include "http_core.h" /* needed for per-directory core-config */
54
 
#include "util_filter.h"
55
 
#include "http_protocol.h" /* ap_hook_insert_error_filter */
56
 
 
57
 
#define AP_XSENDFILE_HEADER "X-SENDFILE"
58
 
 
59
 
module AP_MODULE_DECLARE_DATA xsendfile_module;
60
 
 
61
 
typedef enum {
62
 
  XSENDFILE_UNSET = 0,
63
 
  XSENDFILE_ENABLED = 1<<0,
64
 
  XSENDFILE_DISABLED = 1<<1
65
 
} xsendfile_conf_active_t;
66
 
 
67
 
typedef struct xsendfile_conf_t {
68
 
  xsendfile_conf_active_t enabled;
69
 
  xsendfile_conf_active_t ignoreETag;
70
 
  xsendfile_conf_active_t ignoreLM;
71
 
  apr_array_header_t *paths;
72
 
} xsendfile_conf_t;
73
 
 
74
 
static xsendfile_conf_t *xsendfile_config_create(apr_pool_t *p) {
75
 
  xsendfile_conf_t *conf;
76
 
 
77
 
  conf = (xsendfile_conf_t *) apr_pcalloc(p, sizeof(xsendfile_conf_t));
78
 
  conf->ignoreETag =
79
 
    conf->ignoreLM =
80
 
    conf->enabled =
81
 
    XSENDFILE_UNSET;
82
 
 
83
 
  conf->paths = apr_array_make(p, 1, sizeof(char*));
84
 
 
85
 
  return conf;
86
 
}
87
 
 
88
 
static void *xsendfile_config_server_create(apr_pool_t *p, server_rec *s) {
89
 
  return (void*)xsendfile_config_create(p);
90
 
}
91
 
 
92
 
#define XSENDFILE_CFLAG(x) conf->x = overrides->x != XSENDFILE_UNSET ? overrides->x : base->x
93
 
 
94
 
static void *xsendfile_config_merge(apr_pool_t *p, void *basev, void *overridesv) {
95
 
  xsendfile_conf_t *base = (xsendfile_conf_t *)basev;
96
 
  xsendfile_conf_t *overrides = (xsendfile_conf_t *)overridesv;
97
 
  xsendfile_conf_t *conf;
98
 
 
99
 
  conf = (xsendfile_conf_t *) apr_pcalloc(p, sizeof(xsendfile_conf_t));
100
 
 
101
 
  XSENDFILE_CFLAG(enabled);
102
 
  XSENDFILE_CFLAG(ignoreETag);
103
 
  XSENDFILE_CFLAG(ignoreLM);
104
 
 
105
 
  conf->paths = apr_array_append(p, overrides->paths, base->paths);
106
 
 
107
 
  return (void*)conf;
108
 
}
109
 
 
110
 
static void *xsendfile_config_perdir_create(apr_pool_t *p, char *path) {
111
 
  return (void*)xsendfile_config_create(p);
112
 
}
113
 
#undef XSENDFILE_CFLAG
114
 
 
115
 
static const char *xsendfile_cmd_flag(cmd_parms *cmd, void *perdir_confv, int flag) {
116
 
  xsendfile_conf_t *conf = (xsendfile_conf_t *)perdir_confv;
117
 
  if (cmd->path == NULL) {
118
 
    conf = (xsendfile_conf_t*)ap_get_module_config(
119
 
      cmd->server->module_config,
120
 
      &xsendfile_module
121
 
      );
122
 
  }
123
 
  if (!conf) {
124
 
    return "Cannot get configuration object";
125
 
  }
126
 
  if (!strcasecmp(cmd->cmd->name, "xsendfile")) {
127
 
    conf->enabled = flag ? XSENDFILE_ENABLED : XSENDFILE_DISABLED;
128
 
  }
129
 
  else if (!strcasecmp(cmd->cmd->name, "xsendfileignoreetag")) {
130
 
    conf->ignoreETag = flag ? XSENDFILE_ENABLED: XSENDFILE_DISABLED;
131
 
  }
132
 
  else if (!strcasecmp(cmd->cmd->name, "xsendfileignorelastmodified")) {
133
 
    conf->ignoreLM = flag ? XSENDFILE_ENABLED: XSENDFILE_DISABLED;
134
 
  }
135
 
  else {
136
 
    return apr_psprintf(cmd->pool, "Not a valid command in this context: %s %s", cmd->cmd->name, flag ? "On": "Off");
137
 
  }
138
 
 
139
 
  return NULL;
140
 
}
141
 
 
142
 
static const char *xsendfile_cmd_path(cmd_parms *cmd, void *pdc, const char *arg) {
143
 
  xsendfile_conf_t *conf = (xsendfile_conf_t*)ap_get_module_config(
144
 
    cmd->server->module_config,
145
 
    &xsendfile_module
146
 
    );
147
 
  char **newpath = (char**)apr_array_push(conf->paths);
148
 
  *newpath = apr_pstrdup(cmd->pool, arg);
149
 
 
150
 
  return NULL;
151
 
}
152
 
 
153
 
/*
154
 
  little helper function to get the original request path
155
 
  code borrowed from request.c and util_script.c
156
 
*/
157
 
static const char *ap_xsendfile_get_orginal_path(request_rec *rec) {
158
 
  const char
159
 
    *rv = rec->the_request,
160
 
    *last;
161
 
 
162
 
  int dir = 0;
163
 
  size_t uri_len;
164
 
 
165
 
  /* skip method && spaces */
166
 
  while (*rv && !apr_isspace(*rv)) {
167
 
    ++rv;
168
 
  }
169
 
  while (apr_isspace(*rv)) {
170
 
    ++rv;
171
 
  }
172
 
  /* first space is the request end */
173
 
  last = rv;
174
 
  while (*last && !apr_isspace(*last)) {
175
 
    ++last;
176
 
  }
177
 
  uri_len = last - rv;
178
 
  if (!uri_len) {
179
 
    return NULL;
180
 
  }
181
 
 
182
 
  /* alright, lets see if the request_uri changed! */
183
 
  if (strncmp(rv, rec->uri, uri_len) == 0) {
184
 
    rv = apr_pstrdup(rec->pool, rec->filename);
185
 
    dir = rec->finfo.filetype == APR_DIR;
186
 
  }
187
 
  else {
188
 
    /* need to lookup the url again as it changed */
189
 
    request_rec *sr = ap_sub_req_lookup_uri(
190
 
      apr_pstrmemdup(rec->pool, rv, uri_len),
191
 
      rec,
192
 
      NULL
193
 
      );
194
 
    if (!sr) {
195
 
      return NULL;
196
 
    }
197
 
    rv = apr_pstrdup(rec->pool, sr->filename);
198
 
    dir = rec->finfo.filetype == APR_DIR;
199
 
    ap_destroy_sub_req(sr);
200
 
  }
201
 
 
202
 
  /* now we need to truncate so we only have the directory */
203
 
  if (!dir && (last = ap_strrchr(rv, '/')) != NULL) {
204
 
    *((char*)last + 1) = '\0';
205
 
  }
206
 
  return rv;
207
 
}
208
 
 
209
 
/*
210
 
  little helper function to build the file path if available
211
 
*/
212
 
static apr_status_t ap_xsendfile_get_filepath(request_rec *r, xsendfile_conf_t *conf, const char *file, /* out */ char **path) {
213
 
 
214
 
  const char *root = ap_xsendfile_get_orginal_path(r);
215
 
  apr_status_t rv;
216
 
 
217
 
  apr_array_header_t *patharr;
218
 
  const char **paths;
219
 
  int i;
220
 
 
221
 
 
222
 
#ifdef _DEBUG
223
 
  ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "xsendfile: path is %s", root);
224
 
#endif
225
 
 
226
 
  /* merge the array */
227
 
  if (root) {
228
 
    patharr = apr_array_make(r->pool, conf->paths->nelts + 1, sizeof(char*));
229
 
    *(const char**)(apr_array_push(patharr)) = root;
230
 
    apr_array_cat(patharr, conf->paths);
231
 
  } else {
232
 
    patharr = conf->paths;
233
 
  }
234
 
  if (patharr->nelts == 0) {
235
 
    return APR_EBADPATH;
236
 
  }
237
 
  paths = (const char**)patharr->elts;
238
 
 
239
 
  for (i = 0; i < patharr->nelts; ++i) {
240
 
    if ((rv = apr_filepath_merge(
241
 
      path,
242
 
      paths[i],
243
 
      file,
244
 
      APR_FILEPATH_TRUENAME | APR_FILEPATH_NOTABOVEROOT,
245
 
      r->pool
246
 
    )) == OK) {
247
 
      break;
248
 
    }
249
 
  }
250
 
  if (rv != OK) {
251
 
    *path = NULL;
252
 
  }
253
 
  return rv;
254
 
}
255
 
 
256
 
static apr_status_t ap_xsendfile_output_filter(ap_filter_t *f, apr_bucket_brigade *in) {
257
 
  request_rec *r = f->r, *sr = NULL;
258
 
 
259
 
  xsendfile_conf_t
260
 
    *dconf = (xsendfile_conf_t *)ap_get_module_config(r->per_dir_config, &xsendfile_module),
261
 
    *sconf = (xsendfile_conf_t *)ap_get_module_config(r->server->module_config, &xsendfile_module),
262
 
    *conf = xsendfile_config_merge(r->pool, sconf, dconf);
263
 
 
264
 
  core_dir_config *coreconf = (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
265
 
 
266
 
  apr_status_t rv;
267
 
  apr_bucket *e;
268
 
 
269
 
  apr_file_t *fd = NULL;
270
 
  apr_finfo_t finfo;
271
 
 
272
 
  const char *file = NULL;
273
 
  char *translated = NULL;
274
 
 
275
 
  int errcode;
276
 
 
277
 
#ifdef _DEBUG
278
 
  ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "xsendfile: output_filter for %s", r->the_request);
279
 
#endif
280
 
  /*
281
 
    should we proceed with this request?
282
 
 
283
 
    * sub-requests suck
284
 
    * furthermore default-handled requests suck, as they actually shouldn't be able to set headers
285
 
  */
286
 
  if (
287
 
    r->status != HTTP_OK
288
 
    || r->main
289
 
    || (r->handler && strcmp(r->handler, "default-handler") == 0) /* those table-keys are lower-case, right? */
290
 
  ) {
291
 
#ifdef _DEBUG
292
 
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "xsendfile: not met [%d]", r->status);
293
 
#endif
294
 
    ap_remove_output_filter(f);
295
 
    return ap_pass_brigade(f->next, in);
296
 
  }
297
 
 
298
 
  /*
299
 
    alright, look for x-sendfile
300
 
  */
301
 
  file = apr_table_get(r->headers_out, AP_XSENDFILE_HEADER);
302
 
  apr_table_unset(r->headers_out, AP_XSENDFILE_HEADER);
303
 
 
304
 
  /* cgi/fastcgi will put the stuff into err_headers_out */
305
 
  if (!file || !*file) {
306
 
    file = apr_table_get(r->err_headers_out, AP_XSENDFILE_HEADER);
307
 
    apr_table_unset(r->err_headers_out, AP_XSENDFILE_HEADER);
308
 
  }
309
 
 
310
 
  /* nothing there :p */
311
 
  if (!file || !*file) {
312
 
#ifdef _DEBUG
313
 
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "xsendfile: nothing found");
314
 
#endif
315
 
    ap_remove_output_filter(f);
316
 
    return ap_pass_brigade(f->next, in);
317
 
  }
318
 
 
319
 
  /*
320
 
    drop *everything*
321
 
    might be pretty expensive to generate content first that goes straight to the bitbucket,
322
 
    but actually the scripts that might set this flag won't output too much anyway
323
 
  */
324
 
  while (!APR_BRIGADE_EMPTY(in)) {
325
 
    e = APR_BRIGADE_FIRST(in);
326
 
    apr_bucket_delete(e);
327
 
  }
328
 
  r->eos_sent = 0;
329
 
 
330
 
  /* as we dropped all the content this field is not valid anymore! */
331
 
  apr_table_unset(r->headers_out, "Content-Length");
332
 
  apr_table_unset(r->err_headers_out, "Content-Length");
333
 
  apr_table_unset(r->headers_out, "Content-Encoding");
334
 
  apr_table_unset(r->err_headers_out, "Content-Encoding");
335
 
 
336
 
  rv = ap_xsendfile_get_filepath(r, conf, file, &translated);
337
 
  if (rv != OK) {
338
 
    ap_log_rerror(
339
 
      APLOG_MARK,
340
 
      APLOG_ERR,
341
 
      rv,
342
 
      r,
343
 
      "xsendfile: unable to find file: %s",
344
 
      file
345
 
      );
346
 
    ap_remove_output_filter(f);
347
 
    ap_die(HTTP_NOT_FOUND, r);
348
 
    return HTTP_NOT_FOUND;
349
 
  }
350
 
 
351
 
#ifdef _DEBUG
352
 
  ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "xsendfile: found %s", translated);
353
 
#endif
354
 
 
355
 
  /*
356
 
    try open the file
357
 
  */
358
 
  if ((rv = apr_file_open(
359
 
    &fd,
360
 
    translated,
361
 
    APR_READ | APR_BINARY
362
 
#if APR_HAS_SENDFILE
363
 
    | (coreconf->enable_sendfile != ENABLE_SENDFILE_OFF ? APR_SENDFILE_ENABLED : 0)
364
 
#endif
365
 
    ,
366
 
    0,
367
 
    r->pool
368
 
  )) != APR_SUCCESS) {
369
 
    ap_log_rerror(
370
 
      APLOG_MARK,
371
 
      APLOG_ERR,
372
 
      rv,
373
 
      r,
374
 
      "xsendfile: cannot open file: %s",
375
 
      translated
376
 
      );
377
 
    ap_remove_output_filter(f);
378
 
    ap_die(HTTP_NOT_FOUND, r);
379
 
    return HTTP_NOT_FOUND;
380
 
  }
381
 
#if APR_HAS_SENDFILE && defined(_DEBUG)
382
 
  if (coreconf->enable_sendfile == ENABLE_SENDFILE_OFF) {
383
 
    ap_log_error(
384
 
      APLOG_MARK,
385
 
      APLOG_WARNING,
386
 
      0,
387
 
      r->server,
388
 
      "xsendfile: sendfile configured, but not active %d",
389
 
      coreconf->enable_sendfile
390
 
      );
391
 
    }
392
 
#endif
393
 
  /* stat (for etag/cache/content-length stuff) */
394
 
  if ((rv = apr_file_info_get(&finfo, APR_FINFO_NORM, fd)) != APR_SUCCESS) {
395
 
    ap_log_rerror(
396
 
      APLOG_MARK,
397
 
      APLOG_ERR,
398
 
      rv,
399
 
      r,
400
 
      "xsendfile: unable to stat file: %s",
401
 
      translated
402
 
      );
403
 
    apr_file_close(fd);
404
 
    ap_remove_output_filter(f);
405
 
    ap_die(HTTP_FORBIDDEN, r);
406
 
    return HTTP_FORBIDDEN;
407
 
  }
408
 
  /* no inclusion of directories! we're serving files! */
409
 
  if (finfo.filetype != APR_REG) {
410
 
    ap_log_rerror(
411
 
      APLOG_MARK,
412
 
      APLOG_ERR,
413
 
      APR_EBADPATH,
414
 
      r,
415
 
      "xsendfile: not a file %s",
416
 
      translated
417
 
      );
418
 
    apr_file_close(fd);
419
 
    ap_remove_output_filter(f);
420
 
    ap_die(HTTP_NOT_FOUND, r);
421
 
    return HTTP_NOT_FOUND;
422
 
  }
423
 
 
424
 
  /*
425
 
    need to cheat here a bit
426
 
    as etag generator will use those ;)
427
 
    and we want local_copy and cache
428
 
  */
429
 
  r->finfo.inode = finfo.inode;
430
 
  r->finfo.size = finfo.size;
431
 
 
432
 
  /*
433
 
    caching? why not :p
434
 
  */
435
 
  r->no_cache = r->no_local_copy = 0;
436
 
 
437
 
  /* some script (f?cgi) place stuff in err_headers_out */
438
 
  if (
439
 
    conf->ignoreLM == XSENDFILE_ENABLED
440
 
    || (
441
 
      !apr_table_get(r->headers_out, "last-modified")
442
 
      && !apr_table_get(r->headers_out, "last-modified")
443
 
    )
444
 
  ) {
445
 
    apr_table_unset(r->err_headers_out, "last-modified");
446
 
    ap_update_mtime(r, finfo.mtime);
447
 
    ap_set_last_modified(r);
448
 
  }
449
 
  if (
450
 
    conf->ignoreETag == XSENDFILE_ENABLED
451
 
    || (
452
 
      !apr_table_get(r->headers_out, "etag")
453
 
      && !apr_table_get(r->err_headers_out, "etag")
454
 
    )
455
 
  ) {
456
 
    apr_table_unset(r->err_headers_out, "etag");
457
 
    ap_set_etag(r);
458
 
  }
459
 
 
460
 
  ap_set_content_length(r, finfo.size);
461
 
 
462
 
  /* cache or something? */
463
 
  if ((errcode = ap_meets_conditions(r)) != OK) {
464
 
#ifdef _DEBUG
465
 
    ap_log_error(
466
 
      APLOG_MARK,
467
 
      APLOG_DEBUG,
468
 
      0,
469
 
      r->server,
470
 
      "xsendfile: met condition %d for %s",
471
 
      errcode,
472
 
      file
473
 
      );
474
 
#endif
475
 
    apr_file_close(fd);
476
 
    r->status = errcode;
477
 
  }
478
 
  else {
479
 
    /* For platforms where the size of the file may be larger than
480
 
     * that which can be stored in a single bucket (where the
481
 
     * length field is an apr_size_t), split it into several
482
 
     * buckets: */
483
 
    if (sizeof(apr_off_t) > sizeof(apr_size_t)
484
 
        && finfo.size > AP_MAX_SENDFILE) {
485
 
        apr_off_t fsize = finfo.size;
486
 
        e = apr_bucket_file_create(fd, 0, AP_MAX_SENDFILE, r->pool,
487
 
                                   in->bucket_alloc);
488
 
        while (fsize > AP_MAX_SENDFILE) {
489
 
            apr_bucket *ce;
490
 
            apr_bucket_copy(e, &ce);
491
 
            APR_BRIGADE_INSERT_TAIL(in, ce);
492
 
            e->start += AP_MAX_SENDFILE;
493
 
            fsize -= AP_MAX_SENDFILE;
494
 
        }
495
 
        e->length = (apr_size_t)fsize; /* Resize just the last bucket */
496
 
    }
497
 
    else {
498
 
        e = apr_bucket_file_create(fd, 0, (apr_size_t)finfo.size,
499
 
                                   r->pool, in->bucket_alloc);
500
 
    }
501
 
 
502
 
 
503
 
#if APR_HAS_MMAP
504
 
    if (coreconf->enable_mmap == ENABLE_MMAP_ON) {
505
 
      apr_bucket_file_enable_mmap(e, 0);
506
 
    }
507
 
#if defined(_DEBUG)
508
 
    else {
509
 
      ap_log_error(
510
 
        APLOG_MARK,
511
 
        APLOG_WARNING,
512
 
        0,
513
 
        r->server,
514
 
        "xsendfile: mmap configured, but not active %d",
515
 
        coreconf->enable_mmap
516
 
        );
517
 
      }
518
 
#endif /* _DEBUG */
519
 
#endif /* APR_HAS_MMAP */
520
 
    APR_BRIGADE_INSERT_TAIL(in, e);
521
 
  }
522
 
 
523
 
  e = apr_bucket_eos_create(in->bucket_alloc);
524
 
  APR_BRIGADE_INSERT_TAIL(in, e);
525
 
 
526
 
  /* remove ourselves from the filter chain */
527
 
  ap_remove_output_filter(f);
528
 
 
529
 
#ifdef _DEBUG
530
 
  ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "xsendfile: sending %d bytes", (int)finfo.size);
531
 
#endif
532
 
 
533
 
  /* send the data up the stack */
534
 
  return ap_pass_brigade(f->next, in);
535
 
}
536
 
 
537
 
static void ap_xsendfile_insert_output_filter(request_rec *r) {
538
 
  xsendfile_conf_active_t enabled = ((xsendfile_conf_t *)ap_get_module_config(r->per_dir_config, &xsendfile_module))->enabled;
539
 
  if (XSENDFILE_UNSET == enabled) {
540
 
    enabled = ((xsendfile_conf_t*)ap_get_module_config(r->server->module_config, &xsendfile_module))->enabled;
541
 
  }
542
 
 
543
 
  if (XSENDFILE_ENABLED != enabled) {
544
 
    return;
545
 
  }
546
 
 
547
 
  ap_add_output_filter(
548
 
    "XSENDFILE",
549
 
    NULL,
550
 
    r,
551
 
    r->connection
552
 
          );
553
 
}
554
 
static const command_rec xsendfile_command_table[] = {
555
 
  AP_INIT_FLAG(
556
 
    "XSendFile",
557
 
    xsendfile_cmd_flag,
558
 
    NULL,
559
 
    OR_FILEINFO,
560
 
    "On|Off - Enable/disable(default) processing"
561
 
    ),
562
 
  AP_INIT_FLAG(
563
 
    "XSendFileIgnoreEtag",
564
 
    xsendfile_cmd_flag,
565
 
    NULL,
566
 
    OR_FILEINFO,
567
 
    "On|Off - Ignore script provided Etag headers (default: Off)"
568
 
    ),
569
 
  AP_INIT_FLAG(
570
 
    "XSendFileIgnoreLastModified",
571
 
    xsendfile_cmd_flag,
572
 
    NULL,
573
 
    OR_FILEINFO,
574
 
    "On|Off - Ignore script provided Last-Modified headers (default: Off)"
575
 
    ),
576
 
  AP_INIT_TAKE1(
577
 
    "XSendFilePath",
578
 
    xsendfile_cmd_path,
579
 
    NULL,
580
 
    RSRC_CONF|ACCESS_CONF,
581
 
    "Allow to serve files from that Path. Must be absolute"
582
 
    ),
583
 
  { NULL }
584
 
};
585
 
static void xsendfile_register_hooks(apr_pool_t *p) {
586
 
  ap_register_output_filter(
587
 
    "XSENDFILE",
588
 
    ap_xsendfile_output_filter,
589
 
    NULL,
590
 
    AP_FTYPE_CONTENT_SET
591
 
    );
592
 
 
593
 
  ap_hook_insert_filter(
594
 
    ap_xsendfile_insert_output_filter,
595
 
    NULL,
596
 
    NULL,
597
 
    APR_HOOK_LAST + 1
598
 
    );
599
 
}
600
 
module AP_MODULE_DECLARE_DATA xsendfile_module = {
601
 
  STANDARD20_MODULE_STUFF,
602
 
  xsendfile_config_perdir_create,
603
 
  xsendfile_config_merge,
604
 
  xsendfile_config_server_create,
605
 
  xsendfile_config_merge,
606
 
  xsendfile_command_table,
607
 
  xsendfile_register_hooks
608
 
};