📄 mod_charset_lite.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* * simple hokey charset recoding configuration module * * See mod_ebcdic and mod_charset for more thought-out examples. This * one is just so Jeff can learn how a module works and experiment with * basic character set recoding configuration. * * !!!This is an extremely cheap ripoff of mod_charset.c from Russian Apache!!! */#include "httpd.h"#include "http_config.h"#define CORE_PRIVATE#include "http_core.h"#include "http_log.h"#include "http_main.h"#include "http_protocol.h"#include "http_request.h"#include "util_charset.h"#include "apr_buckets.h"#include "util_filter.h"#include "apr_strings.h"#include "apr_lib.h"#include "apr_xlate.h"#define APR_WANT_STRFUNC#include "apr_want.h"#define OUTPUT_XLATE_BUF_SIZE (16*1024) /* size of translation buffer used on output */#define INPUT_XLATE_BUF_SIZE (8*1024) /* size of translation buffer used on input */#define XLATE_MIN_BUFF_LEFT 128 /* flush once there is no more than this much * space left in the translation buffer */#define FATTEST_CHAR 8 /* we don't handle chars wider than this that straddle * two buckets *//* extended error status codes; this is used in addition to an apr_status_t to * track errors in the translation filter */typedef enum { EES_INIT = 0, /* no error info yet; value must be 0 for easy init */ EES_LIMIT, /* built-in restriction encountered */ EES_INCOMPLETE_CHAR, /* incomplete multi-byte char at end of content */ EES_BUCKET_READ, EES_DOWNSTREAM, /* something bad happened in a filter below xlate */ EES_BAD_INPUT /* input data invalid */} ees_t;/* registered name of the output translation filter */#define XLATEOUT_FILTER_NAME "XLATEOUT"/* registered name of input translation filter */#define XLATEIN_FILTER_NAME "XLATEIN"typedef struct charset_dir_t { /** debug level; -1 means uninitialized, 0 means no debug */ int debug; const char *charset_source; /* source encoding */ const char *charset_default; /* how to ship on wire */ /** module does ap_add_*_filter()? */ enum {IA_INIT, IA_IMPADD, IA_NOIMPADD} implicit_add; /** treat all mimetypes as text? */ enum {FX_INIT, FX_FORCE, FX_NOFORCE} force_xlate;} charset_dir_t;/* charset_filter_ctx_t is created for each filter instance; because the same * filter code is used for translating in both directions, we need this context * data to tell the filter which translation handle to use; it also can hold a * character which was split between buckets */typedef struct charset_filter_ctx_t { apr_xlate_t *xlate; int is_sb; /* single-byte translation? */ charset_dir_t *dc; ees_t ees; /* extended error status */ apr_size_t saved; char buf[FATTEST_CHAR]; /* we want to be able to build a complete char here */ int ran; /* has filter instance run before? */ int noop; /* should we pass brigades through unchanged? */ char *tmp; /* buffer for input filtering */ apr_bucket_brigade *bb; /* input buckets we couldn't finish translating */ apr_bucket_brigade *tmpbb; /* used for passing downstream */} charset_filter_ctx_t;/* charset_req_t is available via r->request_config if any translation is * being performed */typedef struct charset_req_t { charset_dir_t *dc; charset_filter_ctx_t *output_ctx, *input_ctx;} charset_req_t;/* debug level definitions */#define DBGLVL_GORY 9 /* gory details */#define DBGLVL_FLOW 4 /* enough messages to see what happens on * each request */#define DBGLVL_PMC 2 /* messages about possible misconfiguration */module AP_MODULE_DECLARE_DATA charset_lite_module;static void *create_charset_dir_conf(apr_pool_t *p,char *dummy){ charset_dir_t *dc = (charset_dir_t *)apr_pcalloc(p,sizeof(charset_dir_t)); dc->debug = -1; return dc;}static void *merge_charset_dir_conf(apr_pool_t *p, void *basev, void *overridesv){ charset_dir_t *a = (charset_dir_t *)apr_pcalloc (p, sizeof(charset_dir_t)); charset_dir_t *base = (charset_dir_t *)basev, *over = (charset_dir_t *)overridesv; /* If it is defined in the current container, use it. Otherwise, use the one * from the enclosing container. */ a->debug = over->debug != -1 ? over->debug : base->debug; a->charset_default = over->charset_default ? over->charset_default : base->charset_default; a->charset_source = over->charset_source ? over->charset_source : base->charset_source; a->implicit_add = over->implicit_add != IA_INIT ? over->implicit_add : base->implicit_add; a->force_xlate= over->force_xlate != FX_INIT ? over->force_xlate : base->force_xlate; return a;}/* CharsetSourceEnc charset */static const char *add_charset_source(cmd_parms *cmd, void *in_dc, const char *name){ charset_dir_t *dc = in_dc; dc->charset_source = name; return NULL;}/* CharsetDefault charset */static const char *add_charset_default(cmd_parms *cmd, void *in_dc, const char *name){ charset_dir_t *dc = in_dc; dc->charset_default = name; return NULL;}/* CharsetOptions optionflag... */static const char *add_charset_options(cmd_parms *cmd, void *in_dc, const char *flag){ charset_dir_t *dc = in_dc; if (!strcasecmp(flag, "ImplicitAdd")) { dc->implicit_add = IA_IMPADD; } else if (!strcasecmp(flag, "NoImplicitAdd")) { dc->implicit_add = IA_NOIMPADD; } if (!strcasecmp(flag, "TranslateAllMimeTypes")) { dc->force_xlate = FX_FORCE; } else if (!strcasecmp(flag, "NoTranslateAllMimeTypes")) { dc->force_xlate = FX_NOFORCE; } else if (!strncasecmp(flag, "DebugLevel=", 11)) { dc->debug = atoi(flag + 11); } else { return apr_pstrcat(cmd->temp_pool, "Invalid CharsetOptions option: ", flag, NULL); } return NULL;}/* find_code_page() is a fixup hook that checks if the module is * configured and the input or output potentially need to be translated. * If so, context is initialized for the filters. */static int find_code_page(request_rec *r){ charset_dir_t *dc = ap_get_module_config(r->per_dir_config, &charset_lite_module); charset_req_t *reqinfo; charset_filter_ctx_t *input_ctx, *output_ctx; apr_status_t rv; if (dc->debug >= DBGLVL_FLOW) { ap_log_rerror(APLOG_MARK,APLOG_DEBUG, 0, r, "uri: %s file: %s method: %d " "imt: %s flags: %s%s%s %s->%s", r->uri, r->filename ? r->filename : "(none)", r->method_number, r->content_type ? r->content_type : "(unknown)", r->main ? "S" : "", /* S if subrequest */ r->prev ? "R" : "", /* R if redirect */ r->proxyreq ? "P" : "", /* P if proxy */ dc->charset_source, dc->charset_default); } /* If we don't have a full directory configuration, bail out. */ if (!dc->charset_source || !dc->charset_default) { if (dc->debug >= DBGLVL_PMC) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "incomplete configuration: src %s, dst %s", dc->charset_source ? dc->charset_source : "unspecified", dc->charset_default ? dc->charset_default : "unspecified"); } return DECLINED; } /* catch proxy requests */ if (r->proxyreq) { return DECLINED; } /* mod_rewrite indicators */ if (r->filename && (!strncmp(r->filename, "redirect:", 9) || !strncmp(r->filename, "gone:", 5) || !strncmp(r->filename, "passthrough:", 12) || !strncmp(r->filename, "forbidden:", 10))) { return DECLINED; } /* no translation when server and network charsets are set to the same value */ if (!strcasecmp(dc->charset_source, dc->charset_default)) { return DECLINED; } /* Get storage for the request data and the output filter context. * We rarely need the input filter context, so allocate that separately. */ reqinfo = (charset_req_t *)apr_pcalloc(r->pool, sizeof(charset_req_t) + sizeof(charset_filter_ctx_t)); output_ctx = (charset_filter_ctx_t *)(reqinfo + 1); reqinfo->dc = dc; output_ctx->dc = dc; output_ctx->tmpbb = apr_brigade_create(r->pool, r->connection->bucket_alloc); ap_set_module_config(r->request_config, &charset_lite_module, reqinfo); reqinfo->output_ctx = output_ctx; switch (r->method_number) { case M_PUT: case M_POST: /* Set up input translation. Note: A request body can be included * with the OPTIONS method, but for now we don't set up translation * of it. */ input_ctx = apr_pcalloc(r->pool, sizeof(charset_filter_ctx_t)); input_ctx->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); input_ctx->tmp = apr_palloc(r->pool, INPUT_XLATE_BUF_SIZE); input_ctx->dc = dc; reqinfo->input_ctx = input_ctx; rv = apr_xlate_open(&input_ctx->xlate, dc->charset_source, dc->charset_default, r->pool); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "can't open translation %s->%s", dc->charset_default, dc->charset_source); return HTTP_INTERNAL_SERVER_ERROR; } if (apr_xlate_sb_get(input_ctx->xlate, &input_ctx->is_sb) != APR_SUCCESS) { input_ctx->is_sb = 0; } } return DECLINED;}static int configured_in_list(request_rec *r, const char *filter_name, struct ap_filter_t *filter_list){ struct ap_filter_t *filter = filter_list; while (filter) { if (!strcasecmp(filter_name, filter->frec->name)) { return 1; } filter = filter->next; } return 0;}static int configured_on_input(request_rec *r, const char *filter_name){ return configured_in_list(r, filter_name, r->input_filters);}static int configured_on_output(request_rec *r, const char *filter_name){ return configured_in_list(r, filter_name, r->output_filters);}/* xlate_insert_filter() is a filter hook which decides whether or not * to insert a translation filter for the current request. */static void xlate_insert_filter(request_rec *r){ /* Hey... don't be so quick to use reqinfo->dc here; reqinfo may be NULL */ charset_req_t *reqinfo = ap_get_module_config(r->request_config, &charset_lite_module); charset_dir_t *dc = ap_get_module_config(r->per_dir_config, &charset_lite_module); if (reqinfo) { if (reqinfo->output_ctx && !configured_on_output(r, XLATEOUT_FILTER_NAME)) { ap_add_output_filter(XLATEOUT_FILTER_NAME, reqinfo->output_ctx, r, r->connection); } else if (dc->debug >= DBGLVL_FLOW) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "xlate output filter not added implicitly because %s", !reqinfo->output_ctx ? "no output configuration available" : "another module added the filter"); } if (reqinfo->input_ctx && !configured_on_input(r, XLATEIN_FILTER_NAME)) { ap_add_input_filter(XLATEIN_FILTER_NAME, reqinfo->input_ctx, r, r->connection); } else if (dc->debug >= DBGLVL_FLOW) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "xlate input filter not added implicitly because %s", !reqinfo->input_ctx ? "no input configuration available" : "another module added the filter"); } }}/* stuff that sucks that I know of: * * bucket handling: * why create an eos bucket when we see it come down the stream? just send the one * passed as input... news flash: this will be fixed when xlate_out_filter() starts * using the more generic xlate_brigade() * * translation mechanics: * we don't handle characters that straddle more than two buckets; an error * will be generated */static apr_status_t send_bucket_downstream(ap_filter_t *f, apr_bucket *b){ charset_filter_ctx_t *ctx = f->ctx; apr_status_t rv; APR_BRIGADE_INSERT_TAIL(ctx->tmpbb, b); rv = ap_pass_brigade(f->next, ctx->tmpbb); if (rv != APR_SUCCESS) { ctx->ees = EES_DOWNSTREAM; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -