📄 mod_negotiation.c
字号:
* Dealing with header lines ... * * Accept, Accept-Charset, Accept-Language and Accept-Encoding * are handled by do_header_line() - they all have the same * basic structure of a list of items of the format * name; q=N; charset=TEXT * * where q is only valid in Accept, Accept-Charset and Accept-Languages, * and charset is only valid in Accept. */static array_header *do_header_line(pool *p, const char *accept_line){ array_header *accept_recs = ap_make_array(p, 40, sizeof(accept_rec)); if (!accept_line) { return accept_recs; } while (*accept_line) { accept_rec *new = (accept_rec *) ap_push_array(accept_recs); accept_line = get_entry(p, new, accept_line); } return accept_recs;}/* Given the text of the Content-Languages: line from the var map file, * return an array containing the languages of this variant */static array_header *do_languages_line(pool *p, const char **lang_line){ array_header *lang_recs = ap_make_array(p, 2, sizeof(char *)); if (!lang_line) { return lang_recs; } while (**lang_line) { char **new = (char **) ap_push_array(lang_recs); *new = ap_get_token(p, lang_line, 0); ap_str_tolower(*new); if (**lang_line == ',' || **lang_line == ';') { ++(*lang_line); } } return lang_recs;}/***************************************************************** * * Handling header lines from clients... */static negotiation_state *parse_accept_headers(request_rec *r){ negotiation_state *new = (negotiation_state *) ap_pcalloc(r->pool, sizeof(negotiation_state)); accept_rec *elts; table *hdrs = r->headers_in; int i; const char *hdr; new->pool = r->pool; new->r = r; new->dir_name = ap_make_dirstr_parent(r->pool, r->filename); new->accepts = do_header_line(r->pool, ap_table_get(hdrs, "Accept")); hdr = ap_table_get(hdrs, "Accept-encoding"); if (hdr) { new->have_accept_header = 1; } new->accept_encodings = do_header_line(r->pool, hdr); new->accept_langs = do_header_line(r->pool, ap_table_get(hdrs, "Accept-language")); new->accept_charsets = do_header_line(r->pool, ap_table_get(hdrs, "Accept-charset")); new->avail_vars = ap_make_array(r->pool, 40, sizeof(var_rec));#ifdef TCN_02 if (ap_table_get(r->headers_in, "Negotiate")) { /* Negotiate: header tells us UA does transparent negotiation * We have to decide whether we want to ... for now, yes, * we do */ new->ua_can_negotiate = 1; if (r->method_number == M_GET) { new->use_transparent_neg = 1; /* should be configurable */ } /* Check for 'Short Accept', ie either no Accept: header, * or just "Accept: * / *" */ if (new->accepts->nelts == 0 || (new->accepts->nelts == 1 && (!strcmp(((accept_rec *) new->accepts->elts)[0].type_name, "*/*")))) { /* Using short accept header */ new->short_accept_headers = 1; } }#endif if (!new->use_transparent_neg) { /* Now we check for q-values. If they're all 1.0, we assume the * client is "broken", and we are allowed to fiddle with the * values later. Otherwise, we leave them alone. */ elts = (accept_rec *) new->accepts->elts; for (i = 0; i < new->accepts->nelts; ++i) { if (elts[i].quality < 1.0) { new->accept_q = 1; } } } else { new->accept_q = 1; } return new;}/* Sometimes clients will give us no Accept info at all; this routine sets * up the standard default for that case, and also arranges for us to be * willing to run a CGI script if we find one. (In fact, we set up to * dramatically prefer CGI scripts in cases where that's appropriate, * e.g., POST). */static void maybe_add_default_encodings(negotiation_state *neg, int prefer_scripts){ accept_rec *new_accept = (accept_rec *) ap_push_array(neg->accepts); new_accept->type_name = CGI_MAGIC_TYPE; new_accept->quality = prefer_scripts ? 1e-20f : 1e20f; new_accept->level = 0.0f; new_accept->max_bytes = 0.0f; if (neg->accepts->nelts > 1) { return; } new_accept = (accept_rec *) ap_push_array(neg->accepts); new_accept->type_name = "*/*"; new_accept->quality = 1.0f; new_accept->level = 0.0f; new_accept->max_bytes = 0.0f;}/***************************************************************** * * Parsing type-map files, in Roy's meta/http format augmented with * #-comments. *//* Reading RFC822-style header lines, ignoring #-comments and * handling continuations. */enum header_state { header_eof, header_seen, header_sep};static enum header_state get_header_line(char *buffer, int len, FILE *map){ char *buf_end = buffer + len; char *cp; int c; /* Get a noncommented line */ do { if (fgets(buffer, MAX_STRING_LEN, map) == NULL) { return header_eof; } } while (buffer[0] == '#'); /* If blank, just return it --- this ends information on this variant */ for (cp = buffer; (*cp && ap_isspace(*cp)); ++cp) { continue; } if (*cp == '\0') { return header_sep; } /* If non-blank, go looking for header lines, but note that we still * have to treat comments specially... */ cp += strlen(cp); while ((c = getc(map)) != EOF) { if (c == '#') { /* Comment line */ while ((c = getc(map)) != EOF && c != '\n') { continue; } } else if (ap_isspace(c)) { /* Leading whitespace. POSSIBLE continuation line * Also, possibly blank --- if so, we ungetc() the final newline * so that we will pick up the blank line the next time 'round. */ while (c != EOF && c != '\n' && ap_isspace(c)) { c = getc(map); } ungetc(c, map); if (c == '\n') { return header_seen; /* Blank line */ } /* Continuation */ while (cp < buf_end - 2 && (c = getc(map)) != EOF && c != '\n') { *cp++ = c; } *cp++ = '\n'; *cp = '\0'; } else { /* Line beginning with something other than whitespace */ ungetc(c, map); return header_seen; } } return header_seen;}/* Stripping out RFC822 comments */static void strip_paren_comments(char *hdr){ /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */ while (*hdr) { if (*hdr == '"') { hdr = strchr(hdr, '"'); if (hdr == NULL) { return; } ++hdr; } else if (*hdr == '(') { while (*hdr && *hdr != ')') { *hdr++ = ' '; } if (*hdr) { *hdr++ = ' '; } } else { ++hdr; } }}/* Getting to a header body from the header */static char *lcase_header_name_return_body(char *header, request_rec *r){ char *cp = header; for ( ; *cp && *cp != ':' ; ++cp) { *cp = ap_tolower(*cp); } if (!*cp) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Syntax error in type map --- no ':': %s", r->filename); return NULL; } do { ++cp; } while (*cp && ap_isspace(*cp)); if (!*cp) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Syntax error in type map --- no header body: %s", r->filename); return NULL; } return cp;}static int read_type_map(negotiation_state *neg, request_rec *rr){ request_rec *r = neg->r; FILE *map; char buffer[MAX_STRING_LEN]; enum header_state hstate; struct var_rec mime_info; /* We are not using multiviews */ neg->count_multiviews_variants = 0; map = ap_pfopen(neg->pool, rr->filename, "r"); if (map == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "cannot access type map file: %s", rr->filename); return HTTP_FORBIDDEN; } clean_var_rec(&mime_info); do { hstate = get_header_line(buffer, MAX_STRING_LEN, map); if (hstate == header_seen) { char *body1 = lcase_header_name_return_body(buffer, neg->r); const char *body; if (body1 == NULL) { return SERVER_ERROR; } strip_paren_comments(body1); body=body1; if (!strncmp(buffer, "uri:", 4)) { mime_info.file_name = ap_get_token(neg->pool, &body, 0); } else if (!strncmp(buffer, "content-type:", 13)) { struct accept_rec accept_info; get_entry(neg->pool, &accept_info, body); set_mime_fields(&mime_info, &accept_info); } else if (!strncmp(buffer, "content-length:", 15)) { mime_info.bytes = atof(body); } else if (!strncmp(buffer, "content-language:", 17)) { mime_info.content_languages = do_languages_line(neg->pool, &body); } else if (!strncmp(buffer, "content-encoding:", 17)) { mime_info.content_encoding = ap_get_token(neg->pool, &body, 0); } else if (!strncmp(buffer, "description:", 12)) { mime_info.description = ap_get_token(neg->pool, &body, 0); } } else { if (mime_info.type_quality > 0 && *mime_info.file_name) { void *new_var = ap_push_array(neg->avail_vars); memcpy(new_var, (void *) &mime_info, sizeof(var_rec)); } clean_var_rec(&mime_info); } } while (hstate != header_eof); ap_pfclose(neg->pool, map); return OK;}/***************************************************************** * * Same, except we use a filtered directory listing as the map... */static int read_types_multi(negotiation_state *neg){ request_rec *r = neg->r; char *filp; int prefix_len; DIR *dirp; struct DIR_TYPE *dir_entry; struct var_rec mime_info; struct accept_rec accept_info; void *new_var; clean_var_rec(&mime_info); if (!(filp = strrchr(r->filename, '/'))) { return DECLINED; /* Weird... */ } if (strncmp(r->filename, "proxy:", 6) == 0) { return DECLINED; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -