static int find_cookie(request_rec *r,char **user,uint8_t *secret,int secretLen) { char *cookie=0l; char *cookie_expire=0l; char *cookie_valid=0l; ap_regmatch_t regmatch[AP_MAX_REG_MATCH]; authn_google_config_rec *conf = ap_get_module_config(r->per_dir_config, &authn_google_module); cookie = (char *) apr_table_get(r->headers_in, "Cookie"); if (cookie) { if (conf->debugLevel) ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Found cookie \"%s\"",cookie); if (!ap_regexec(cookie_regexp, cookie, AP_MAX_REG_MATCH, regmatch, 0)) { if (user) *user = ap_pregsub(r->pool, "$2", cookie,AP_MAX_REG_MATCH,regmatch); cookie_expire = ap_pregsub(r->pool, "$3", cookie,AP_MAX_REG_MATCH,regmatch); cookie_valid = ap_pregsub(r->pool, "$4", cookie,AP_MAX_REG_MATCH,regmatch); if (conf->debugLevel) ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Found cookie Expires \"%s\" Valid \"%s\"",cookie_expire,cookie_valid); if (cookie_expire && cookie_valid && *user) { long unsigned int exp = apr_atoi64(cookie_expire); long unsigned int now = apr_time_now()/1000000; if (exp < now) { if (conf->debugLevel) ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Expired. Now=%lu Expire=%lu\n",now,exp); return 0; /* Expired */ } if (!secret) { secret = getUserSecret(r,*user,&secretLen,0L); } char *h = hash_cookie(r->pool,secret,secretLen,exp); if (conf->debugLevel) ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Match cookie \"%s\" vs \"%s\"",h,cookie_valid); if (apr_strnatcmp(h,cookie_valid)==0) return 1; /* Valid Cookie */ else { if (conf->debugLevel) ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "MISMATCHED cookie \"%s\" vs \"%s\"",h,cookie_valid); return 0; /* Mismatched */ } } } } return 0; /* Not found */ }
void cookie_test(apr_pool_t *pool,char *cookie) { char *user=0l; char *cookie_expire=0l; char *cookie_valid=0l; ap_regmatch_t regmatch[AP_MAX_REG_MATCH]; //authn_google_config_rec *conf = ap_get_module_config(r->per_dir_config, &authn_google_module); if (!ap_regexec(cookie_regexp, cookie, AP_MAX_REG_MATCH, regmatch, 0)) { user = ap_pregsub(pool, "$2", cookie,AP_MAX_REG_MATCH,regmatch); cookie_expire = ap_pregsub(pool, "$3", cookie,AP_MAX_REG_MATCH,regmatch); cookie_valid = ap_pregsub(pool, "$4", cookie,AP_MAX_REG_MATCH,regmatch); printf("COOKIE GOOD: \"%s\" \"%s\" \"%s\"\n",user,cookie_expire,cookie_valid); } else printf ("INVALID COOKIE: \"%s\"\n",cookie); }
static int spot_cookie(request_rec *r) { cookie_dir_rec *dcfg = ap_get_module_config(r->per_dir_config, &usertrack_module); const char *cookie_header; ap_regmatch_t regm[NUM_SUBS]; /* Do not run in subrequests */ if (!dcfg->enabled || r->main) { return DECLINED; } if ((cookie_header = apr_table_get(r->headers_in, "Cookie"))) { if (!ap_regexec(dcfg->regexp, cookie_header, NUM_SUBS, regm, 0)) { char *cookieval = NULL; int err = 0; /* Our regexp, * ^cookie_name=([^;]+)|;[ \t]+cookie_name=([^;]+) * only allows for $1 or $2 to be available. ($0 is always * filled with the entire matched expression, not just * the part in parentheses.) So just check for either one * and assign to cookieval if present. */ if (regm[1].rm_so != -1) { cookieval = ap_pregsub(r->pool, "$1", cookie_header, NUM_SUBS, regm); if (cookieval == NULL) err = 1; } if (regm[2].rm_so != -1) { cookieval = ap_pregsub(r->pool, "$2", cookie_header, NUM_SUBS, regm); if (cookieval == NULL) err = 1; } if (err) { ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01499) "Failed to extract cookie value (out of mem?)"); return HTTP_INTERNAL_SERVER_ERROR; } /* Set the cookie in a note, for logging */ apr_table_setn(r->notes, "cookie", cookieval); return DECLINED; /* There's already a cookie, no new one */ } } make_cookie(r); return OK; /* We set our cookie */ }
/** * The device is specified from UserAgent. * @param r Request_rec is appointed. * @param userAgent UserAgent is appointed here, * @return The style which corresponds is returned. */ static device_table * s_specified_device_from_xml(request_rec *r, mod_chxj_config * conf, const char *user_agent) { ap_regmatch_t match[10]; device_table *returnType = &UNKNOWN_DEVICE; device_table_list *dtl; device_table *dt; char *device_id; DBG(r, "REQ[%X] start %s()", TO_ADDR(r),__func__); if (! user_agent) { DBG(r, "REQ[%X] end %s() (user_agent is not set)", TO_ADDR(r),__func__); return returnType; } if (! conf->devices) { ERR(r, "REQ[%X] device_data.xml load failure", TO_ADDR(r)); DBG(r, "REQ[%X] end %s()", TO_ADDR(r), __func__); return returnType; } for (dtl = conf->devices; dtl; dtl = dtl->next) { if (! dtl->pattern) { continue; } /* DBG(r, "pattern is [%s]", dtl->pattern); */ if (! dtl->regexp) { ERR(r,"REQ[%X] compile failed.", TO_ADDR(r)); DBG(r, "REQ[%X] %s()", TO_ADDR(r),__func__); return returnType; } if (ap_regexec((const ap_regex_t *)dtl->regexp, user_agent, (apr_size_t)dtl->regexp->re_nsub + 1, match, 0) == 0) { device_id = ap_pregsub(r->pool, "$1", user_agent, dtl->regexp->re_nsub + 1, match); DBG(r, "REQ[%X] device_id:[%s]", TO_ADDR(r), device_id); dt = s_get_device_data(r, device_id, dtl); if (! dt) { dt = dtl->tail; if (dt){ if (conf->detect_device_type > CHXJ_ADD_DETECT_DEVICE_TYPE_NONE ){ dt->device_id = device_id; } DBG(r,"REQ[%X] end %s() (Not Found) use [%s]", TO_ADDR(r), __func__, dt->device_id); return dt; } } DBG(r,"REQ[%X] end %s() (Found) use [%s]", TO_ADDR(r), __func__, dt->device_id); return dt; } } DBG(r,"REQ[%X] end %s()", TO_ADDR(r), __func__); return returnType; }
int mod_dirsize_handler(request_rec *r) { BUFF *pipe_output; char buf[MAX_STRING_LEN]; char *sizeink=NULL; regmatch_t pmatch[2]; r->path_info = ap_make_dirstr_parent(r->pool, r->filename); if(!ap_bspawn_child(r->pool,dirsize, (void *) r, kill_after_timeout, NULL, &pipe_output, NULL)) { ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "problems with dirsize subprocess"); return HTTP_INTERNAL_SERVER_ERROR; } ap_bgets(buf, sizeof(buf), pipe_output); regex_t *cpat = ap_pregcomp(r->pool, "^(.+)\t", REG_EXTENDED); if(regexec(cpat, buf, cpat->re_nsub+1, pmatch, 0) == 0) { sizeink = ap_pregsub(r->pool, "$1", buf, cpat->re_nsub+1, pmatch); } #ifdef DEBUG r->content_type = "text/html"; ap_send_http_header(r); ap_rprintf(r, "<html>\n"); ap_rprintf(r, "<head>\n"); ap_rprintf(r, "<title>mod_dirsize</title>\n"); ap_rprintf(r, "</head>\n"); ap_rprintf(r, "<body>\n"); ap_rprintf(r, "Request: %s<br>\n", r->the_request); ap_rprintf(r, "Server Hostname: %s<br>\n", r->server->server_hostname); ap_rprintf(r, "Server Admin: %s<br>\n", r->server->server_admin); ap_rprintf(r, "Filename: %s<br>\n", r->filename); ap_rprintf(r, "ServerRoot: %s<br>\n", ap_server_root_relative(r->pool, "")); ap_rprintf(r, "Path Info: %s<br>\n", r->path_info); ap_send_fb(pipe_output, r); ap_rprintf(r, "</body>\n"); ap_rprintf(r, "</html>\n"); #else r->content_type = "text/xml"; ap_send_http_header(r); ap_rprintf(r, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); ap_rprintf(r, "<dirsize xmlns:html=\"http://www.w3.org/1999/html\">\n"); ap_rprintf(r, "<sizeink>%s</sizeink>\n", sizeink); ap_rprintf(r, "</dirsize>"); #endif ap_bclose(pipe_output); return(OK); }
void password_test(apr_pool_t *pool,char *cookie) { char *user=0l; ap_regmatch_t regmatch[AP_MAX_REG_MATCH]; printf("will regexec \n"); if (!ap_regexec(passwd_regexp, cookie, AP_MAX_REG_MATCH, regmatch, 0)) { printf("regexec done\n"); user = ap_pregsub(pool, "$1", cookie,AP_MAX_REG_MATCH,regmatch); printf("PASSWORD GOOD: \"%s\"\n",user); } else printf ("INVALID PASSWORD: \"%s\"\n",cookie); }
static apr_status_t parse_request_uri(request_rec *r, char **uri, char **size) { int nmatch = 10; ap_regmatch_t match[nmatch]; ap_regex_t *reg; reg = ap_pregcomp(r->pool, "^/(original|large|medium|small)/(.*)$", AP_REG_EXTENDED); if (ap_regexec(reg, r->unparsed_uri, nmatch, match, 0) == 0) { *size = ap_pregsub(r->pool, "$1", r->unparsed_uri, nmatch, match); *uri = ap_pregsub(r->pool, "$2", r->unparsed_uri, nmatch, match); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Parse URI: %s, %s", *size, *uri); ap_pregfree(r->pool, reg); return APR_SUCCESS; } else { return APR_EGENERAL; } }
static void dump_content(saxctxt *ctx) { urlmap *m; char *found; size_t s_from, s_to; size_t match; char c = 0; int nmatch; ap_regmatch_t pmatch[10]; char *subs; size_t len, offs; urlmap *themap = ctx->map; #ifndef GO_FASTER int verbose = APLOGrtrace1(ctx->f->r); #endif pappend(ctx, &c, 1); /* append null byte */ /* parse the text for URLs */ for (m = themap; m; m = m->next) { if (!(m->flags & M_CDATA)) continue; if (m->flags & M_REGEX) { nmatch = 10; offs = 0; while (!ap_regexec(m->from.r, ctx->buf+offs, nmatch, pmatch, 0)) { match = pmatch[0].rm_so; s_from = pmatch[0].rm_eo - match; subs = ap_pregsub(ctx->f->r->pool, m->to, ctx->buf+offs, nmatch, pmatch); s_to = strlen(subs); len = strlen(ctx->buf); offs += match; VERBOSEB( const char *f = apr_pstrndup(ctx->f->r->pool, ctx->buf + offs, s_from); ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, ctx->f->r, "C/RX: match at %s, substituting %s", f, subs); ) if (s_to > s_from) { preserve(ctx, s_to - s_from); memmove(ctx->buf+offs+s_to, ctx->buf+offs+s_from, len + 1 - s_from - offs); memcpy(ctx->buf+offs, subs, s_to); } else { memcpy(ctx->buf + offs, subs, s_to); memmove(ctx->buf+offs+s_to, ctx->buf+offs+s_from, len + 1 - s_from - offs); } offs += s_to; } }
static char *getSharedKey(request_rec *r,char *filename, char **static_pw) { ap_regmatch_t regmatch[AP_MAX_REG_MATCH]; char l[MAX_STRING_LEN]; char *sharedKey = 0L; apr_status_t status; ap_configfile_t *f; authn_google_config_rec *conf = ap_get_module_config(r->per_dir_config, &authn_google_module); status = ap_pcfg_openfile(&f, r->pool, filename); ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, "OPENING FILENAME %s",filename); if (status != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, "check_password: Couldn't open password file: %s", filename); return 0L; } while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) { /* If we have a password entry - return it, if the caller has asked for it */ if ((static_pw) && (!ap_regexec(passwd_regexp, l, AP_MAX_REG_MATCH, regmatch, 0))) { *static_pw = ap_pregsub(r->pool, "$1", l,AP_MAX_REG_MATCH,regmatch); if (conf->debugLevel) ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Using static password \"%s\"",*static_pw); } /* Skip comment or blank lines. */ if ((l[0] == '"') || (!l[0])) { continue; } if (!sharedKey) { sharedKey = apr_pstrdup(r->pool,l); } /* Scratch codes to follow */ } ap_cfg_closefile(f); return sharedKey; }
static void do_pattmatch(ap_filter_t *f, apr_bucket *inb, apr_bucket_brigade *mybb, apr_pool_t *tmp_pool) { int i; int force_quick = 0; ap_regmatch_t regm[AP_MAX_REG_MATCH]; apr_size_t bytes; apr_size_t len; apr_size_t fbytes; const char *buff; const char *repl; char *scratch; char *p; char *s1; char *s2; apr_bucket *b; apr_bucket *tmp_b; apr_pool_t *tpool; subst_dir_conf *cfg = (subst_dir_conf *) ap_get_module_config(f->r->per_dir_config, &substitute_module); subst_pattern_t *script; APR_BRIGADE_INSERT_TAIL(mybb, inb); script = (subst_pattern_t *) cfg->patterns->elts; apr_pool_create(&tpool, tmp_pool); scratch = NULL; fbytes = 0; /* * Simple optimization. If we only have one pattern, then * we can safely avoid the overhead of flattening */ if (cfg->patterns->nelts == 1) { force_quick = 1; } for (i = 0; i < cfg->patterns->nelts; i++) { for (b = APR_BRIGADE_FIRST(mybb); b != APR_BRIGADE_SENTINEL(mybb); b = APR_BUCKET_NEXT(b)) { if (APR_BUCKET_IS_METADATA(b)) { /* * we should NEVER see this, because we should never * be passed any, but "handle" it just in case. */ continue; } if (apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ) == APR_SUCCESS) { s1 = NULL; if (script->pattern) { while ((repl = apr_strmatch(script->pattern, buff, bytes))) { /* get offset into buff for pattern */ len = (apr_size_t) (repl - buff); if (script->flatten && !force_quick) { /* * We are flattening the buckets here, meaning * that we don't do the fast bucket splits. * Instead we copy over what the buckets would * contain and use them. This is slow, since we * are constanting allocing space and copying * strings. */ SEDSCAT(s1, s2, tmp_pool, buff, len, script->replacement); } else { /* * We now split off the stuff before the regex * as its own bucket, then isolate the pattern * and delete it. */ SEDRMPATBCKT(b, len, tmp_b, script->patlen); /* * Finally, we create a bucket that contains the * replacement... */ tmp_b = apr_bucket_transient_create(script->replacement, script->replen, f->r->connection->bucket_alloc); /* ... and insert it */ APR_BUCKET_INSERT_BEFORE(b, tmp_b); } /* now we need to adjust buff for all these changes */ len += script->patlen; bytes -= len; buff += len; } if (script->flatten && s1 && !force_quick) { /* * we've finished looking at the bucket, so remove the * old one and add in our new one */ s2 = apr_pstrmemdup(tmp_pool, buff, bytes); s1 = apr_pstrcat(tmp_pool, s1, s2, NULL); tmp_b = apr_bucket_transient_create(s1, strlen(s1), f->r->connection->bucket_alloc); APR_BUCKET_INSERT_BEFORE(b, tmp_b); apr_bucket_delete(b); b = tmp_b; } } else if (script->regexp) { /* * we need a null terminated string here :(. To hopefully * save time and memory, we don't alloc for each run * through, but only if we need to have a larger chunk * to save the string to. So we keep track of how much * we've allocated and only re-alloc when we need it. * NOTE: this screams for a macro. */ if (!scratch || (bytes > (fbytes + 1))) { fbytes = bytes + 1; scratch = apr_palloc(tpool, fbytes); } /* reset pointer to the scratch space */ p = scratch; memcpy(p, buff, bytes); p[bytes] = '\0'; while (!ap_regexec(script->regexp, p, AP_MAX_REG_MATCH, regm, 0)) { /* first, grab the replacement string */ repl = ap_pregsub(tmp_pool, script->replacement, p, AP_MAX_REG_MATCH, regm); if (script->flatten && !force_quick) { SEDSCAT(s1, s2, tmp_pool, p, regm[0].rm_so, repl); } else { len = (apr_size_t) (regm[0].rm_eo - regm[0].rm_so); SEDRMPATBCKT(b, regm[0].rm_so, tmp_b, len); tmp_b = apr_bucket_transient_create(repl, strlen(repl), f->r->connection->bucket_alloc); APR_BUCKET_INSERT_BEFORE(b, tmp_b); } /* * reset to past what we just did. buff now maps to b * again */ p += regm[0].rm_eo; } if (script->flatten && s1 && !force_quick) { s1 = apr_pstrcat(tmp_pool, s1, p, NULL); tmp_b = apr_bucket_transient_create(s1, strlen(s1), f->r->connection->bucket_alloc); APR_BUCKET_INSERT_BEFORE(b, tmp_b); apr_bucket_delete(b); b = tmp_b; } } else { /* huh? */ continue; } } } script++; } apr_pool_destroy(tpool); return; }
// Find the cookie and figure out what to do static int spot_cookie(request_rec *r) { cookietrack_settings_rec *dcfg = ap_get_module_config(r->per_dir_config, &cookietrack_module); const char *cookie_header; ap_regmatch_t regm[NUM_SUBS]; /* Do not run in subrequests */ if (!dcfg->enabled || r->main) { return DECLINED; } /* Is DNT set? */ const char *dnt_is_set = apr_table_get( r->headers_in, "DNT" ); _DEBUG && fprintf( stderr, "DNT: %s\n", dnt_is_set ); /* Do we already have a cookie? */ char *cur_cookie_value = NULL; if( (cookie_header = apr_table_get(r->headers_in, "Cookie")) ){ // this will match the FIRST occurance of the cookiename, not // subsequent ones. if( !ap_regexec(dcfg->regexp, cookie_header, NUM_SUBS, regm, 0) ) { /* Our regexp, * ^cookie_name=([^;]+)|;[ \t]+cookie_name=([^;]+) * only allows for $1 or $2 to be available. ($0 is always * filled with the entire matched expression, not just * the part in parentheses.) So just check for either one * and assign to cookieval if present. */ if( regm[1].rm_so != -1 ) { cur_cookie_value = ap_pregsub(r->pool, "$1", cookie_header, NUM_SUBS, regm); } if( regm[2].rm_so != -1 ) { cur_cookie_value = ap_pregsub(r->pool, "$2", cookie_header, NUM_SUBS, regm); } } } _DEBUG && fprintf( stderr, "Current Cookie: %s\n", cur_cookie_value ); /* XFF support inspired by this patch: http://www.mail-archive.com/[email protected]/msg17378.html And this implementation for scanning for remote ip: http://apache.wirebrain.de/lxr/source/modules/metadata/mod_remoteip.c?v=2.3-trunk#267 */ // Get the IP address of the originating request const char *rname = NULL; // Originating IP address char *xff = NULL; // X-Forwarded-For, or equivalent header type // Should we look at a header? if( xff = apr_table_get(r->headers_in, dcfg->cookie_ip_header) ) { // There might be multiple addresses in the header // Check if there's a comma in there somewhere // no comma, this is the address we can use if( (rname = strrchr(xff, ',')) == NULL ) { rname = xff; // whitespace/commas left, remove 'm } else { // move past the comma rname++; // and any whitespace we might find while( *rname == ' ' ) { rname++; } } // otherwise, get it from the remote host } else { rname = ap_get_remote_host( r->connection, r->per_dir_config, REMOTE_NAME, NULL ); } _DEBUG && fprintf( stderr, "Remote Address: %s\n", rname ); /* Determine the value of the cookie we're going to set: */ /* Make sure we have enough room here... */ char new_cookie_value[ _MAX_COOKIE_LENGTH ]; // dnt is set, and we care about that if( dnt_is_set && dcfg->comply_with_dnt ) { // you don't want us to set a cookie, alright then our work is done. if( !dcfg->set_dnt_cookie ) { return DECLINED; } char *dnt_value = dcfg->dnt_value; // you already ahve a cookie, but it might be whitelisted if( cur_cookie_value ) { // you might have whitelisted this value; let's check // Following tutorial code here again: // http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial-19.html int i; for( i = 0; i < dcfg->dnt_exempt->nelts; i++ ) { _DEBUG && fprintf( stderr, "e: %d\n", i ); char *exempt = ((char **)dcfg->dnt_exempt->elts)[i]; //it's indeed whiteliested, we should use this value instead if( strcasecmp( cur_cookie_value, exempt ) == 0 ) { _DEBUG && fprintf( stderr, "Cookie %s is DNT exempt\n", cur_cookie_value ); dnt_value = exempt; } } } // dnt_value is a pointer, hence the sprintf sprintf( new_cookie_value, "%s", dnt_value ); // No DNT header, so we need a cookie value to set } else { // there already is a cookie set if( cur_cookie_value ) { // but it's set to the DNT cookie if( strcasecmp( cur_cookie_value, dcfg->dnt_value ) == 0 ) { // if we have some sort of library that's generating the // UID, call that with the cookie we would be setting if( _EXTERNAL_UID_FUNCTION ) { char ts[ _MAX_COOKIE_LENGTH ]; sprintf( ts, "%" APR_TIME_T_FMT, apr_time_now() ); gen_uid( new_cookie_value, ts, rname ); // otherwise, just set it } else { sprintf( new_cookie_value, "%s.%" APR_TIME_T_FMT, rname, apr_time_now() ); } // it's set to something reasonable - note we're still setting // a new cookie, even when there's no expires requested, because // we don't know if there's an expires on the /current/ cookie. // this could be added, but this seems to work for now. } else { // XXX we use a apr_pstrndup instead, so we can't overflow // the buffer if we get sent garbage // The return value is a sprintf( new_cookie_value, "%s", apr_pstrndup( r->pool, cur_cookie_value, _MAX_COOKIE_LENGTH ) ); } // it's either carbage, or not set; either way, // we need to generate a new one } else { // if we have some sort of library that's generating the // UID, call that with the cookie we would be setting if( _EXTERNAL_UID_FUNCTION ) { char ts[ _MAX_COOKIE_LENGTH ]; sprintf( ts, "%" APR_TIME_T_FMT, apr_time_now() ); gen_uid( new_cookie_value, ts, rname ); // otherwise, just set it } else { sprintf( new_cookie_value, "%s.%" APR_TIME_T_FMT, rname, apr_time_now() ); } } } _DEBUG && fprintf( stderr, "New cookie: %s\n", new_cookie_value ); /* Set the cookie in a note, for logging */ apr_table_setn(r->notes, dcfg->note_name, new_cookie_value); make_cookie(r, new_cookie_value, cur_cookie_value, (dnt_is_set && dcfg->comply_with_dnt) // should we use dnt expires? ); // We need to flush the stream for messages to appear right away. // Performing an fflush() in a production system is not good for // performance - don't do this for real. _DEBUG && fflush(stderr); return OK; /* We set our cookie */ }
static apr_status_t line_edit_filter(ap_filter_t* f, apr_bucket_brigade* bb) { int i, j; unsigned int match ; unsigned int nmatch = 10 ; ap_regmatch_t pmatch[10] ; const char* bufp; const char* subs ; apr_size_t bytes ; apr_size_t fbytes ; apr_size_t offs ; const char* buf ; const char* le = NULL ; const char* le_n ; const char* le_r ; char* fbuf ; apr_bucket* b = APR_BRIGADE_FIRST(bb) ; apr_bucket* b1 ; int found = 0 ; apr_status_t rv ; apr_bucket_brigade* bbline ; line_edit_cfg* cfg = ap_get_module_config(f->r->per_dir_config, &line_edit_module) ; rewriterule* rules = (rewriterule*) cfg->rewriterules->elts ; rewriterule* newrule; line_edit_ctx* ctx = f->ctx ; if (ctx == NULL) { /* check env to see if we're wanted, to give basic control with 2.0 */ buf = apr_table_get(f->r->subprocess_env, "LineEdit"); if (buf && f->r->content_type) { char* lcbuf = apr_pstrdup(f->r->pool, buf) ; char* lctype = apr_pstrdup(f->r->pool, f->r->content_type) ; char* c ; for (c = lcbuf; *c; ++c) if (isupper(*c)) *c = tolower(*c) ; for (c = lctype; *c; ++c) if (isupper(*c)) *c = tolower(*c) ; else if (*c == ';') { *c = 0 ; break ; } if (!strstr(lcbuf, lctype)) { /* don't filter this content type */ ap_filter_t* fnext = f->next ; ap_remove_output_filter(f) ; return ap_pass_brigade(fnext, bb) ; } } ctx = f->ctx = apr_palloc(f->r->pool, sizeof(line_edit_ctx)) ; ctx->bbsave = apr_brigade_create(f->r->pool, f->c->bucket_alloc) ; /* If we have any regex matches, we'll need to copy everything, so we * have null-terminated strings to parse. That's a lot of memory if * we're streaming anything big. So we'll use (and reuse) a local * subpool. Fall back to the request pool if anything bad happens. */ ctx->lpool = f->r->pool ; for (i = 0; i < cfg->rewriterules->nelts; ++i) { if ( rules[i].flags & M_REGEX ) { if (apr_pool_create(&ctx->lpool, f->r->pool) != APR_SUCCESS) { ctx->lpool = f->r->pool ; } break ; } } /* If we have env interpolation, we'll need a private copy of * our rewrite rules with this requests env. Otherwise we can * save processing time by using the original. * * If one ENV is found, we also have to copy all previous and * subsequent rules, even those with no interpolation. */ ctx->rewriterules = cfg->rewriterules; for (i = 0; i < cfg->rewriterules->nelts; ++i) { found |= (rules[i].flags & M_ENV) ; if ( found ) { if (ctx->rewriterules == cfg->rewriterules) { ctx->rewriterules = apr_array_make(f->r->pool, cfg->rewriterules->nelts, sizeof(rewriterule)); for (j = 0; j < i; ++j) { newrule = apr_array_push (((line_edit_ctx*)ctx)->rewriterules) ; newrule->from = rules[j].from; newrule->to = rules[j].to; newrule->flags = rules[j].flags; newrule->length = rules[j].length; } } /* this rule needs to be interpolated */ newrule = apr_array_push (((line_edit_ctx*)ctx)->rewriterules) ; newrule->from = rules[i].from; if (rules[i].flags & M_ENV) { newrule->to = interpolate_env(f->r, rules[i].to); } else { newrule->to = rules[i].to ; } newrule->flags = rules[i].flags; newrule->length = rules[i].length; } } /* for back-compatibility with Apache 2.0, set some protocol stuff */ apr_table_unset(f->r->headers_out, "Content-Length") ; apr_table_unset(f->r->headers_out, "Content-MD5") ; apr_table_unset(f->r->headers_out, "Accept-Ranges") ; } /* by now our rules are in ctx->rewriterules */ rules = (rewriterule*) ctx->rewriterules->elts ; /* bbline is what goes to the next filter, * so we (can) have a new one each time. */ bbline = apr_brigade_create(f->r->pool, f->c->bucket_alloc) ; /* first ensure we have no mid-line breaks that might be in the * middle of a search string causing us to miss it! At the same * time we split into lines to avoid pattern-matching over big * chunks of memory. */ while ( b != APR_BRIGADE_SENTINEL(bb) ) { if ( !APR_BUCKET_IS_METADATA(b) ) { if ( apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) == APR_SUCCESS ) { if ( bytes == 0 ) { APR_BUCKET_REMOVE(b) ; } else while ( bytes > 0 ) { switch (cfg->lineend) { case LINEEND_UNIX: le = memchr(buf, '\n', bytes) ; break ; case LINEEND_MAC: le = memchr(buf, '\r', bytes) ; break ; case LINEEND_DOS: /* Edge-case issue: if a \r\n spans buckets it'll get missed. * Not a problem for present purposes, but would be an issue * if we claimed to support pattern matching on the lineends. */ found = 0 ; le = memchr(buf+1, '\n', bytes-1) ; while ( le && !found ) { if ( le[-1] == '\r' ) { found = 1 ; } else { le = memchr(le+1, '\n', bytes-1 - (le+1 - buf)) ; } } if ( !found ) le = 0 ; break; case LINEEND_ANY: case LINEEND_UNSET: /* Edge-case notabug: if a \r\n spans buckets it'll get seen as * two line-ends. It'll insert the \n as a one-byte bucket. */ le_n = memchr(buf, '\n', bytes) ; le_r = memchr(buf, '\r', bytes) ; if ( le_n != NULL ) if ( le_n == le_r + sizeof(char)) le = le_n ; else if ( (le_r < le_n) && (le_r != NULL) ) le = le_r ; else le = le_n ; else le = le_r ; break; case LINEEND_NONE: le = 0 ; break; case LINEEND_CUSTOM: le = memchr(buf, cfg->lechar, bytes) ; break; } if ( le ) { /* found a lineend in this bucket. */ offs = 1 + ((unsigned int)le-(unsigned int)buf) / sizeof(char) ; apr_bucket_split(b, offs) ; bytes -= offs ; buf += offs ; b1 = APR_BUCKET_NEXT(b) ; APR_BUCKET_REMOVE(b); /* Is there any previous unterminated content ? */ if ( !APR_BRIGADE_EMPTY(ctx->bbsave) ) { /* append this to any content waiting for a lineend */ APR_BRIGADE_INSERT_TAIL(ctx->bbsave, b) ; rv = apr_brigade_pflatten(ctx->bbsave, &fbuf, &fbytes, f->r->pool) ; /* make b a new bucket of the flattened stuff */ b = apr_bucket_pool_create(fbuf, fbytes, f->r->pool, f->r->connection->bucket_alloc) ; /* bbsave has been consumed, so clear it */ apr_brigade_cleanup(ctx->bbsave) ; } /* b now contains exactly one line */ APR_BRIGADE_INSERT_TAIL(bbline, b); b = b1 ; } else { /* no lineend found. Remember the dangling content */ APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->bbsave, b); bytes = 0 ; } } /* while bytes > 0 */ } else { /* bucket read failed - oops ! Let's remove it. */ APR_BUCKET_REMOVE(b); } } else if ( APR_BUCKET_IS_EOS(b) ) { /* If there's data to pass, send it in one bucket */ if ( !APR_BRIGADE_EMPTY(ctx->bbsave) ) { rv = apr_brigade_pflatten(ctx->bbsave, &fbuf, &fbytes, f->r->pool) ; b1 = apr_bucket_pool_create(fbuf, fbytes, f->r->pool, f->r->connection->bucket_alloc) ; APR_BRIGADE_INSERT_TAIL(bbline, b1); } apr_brigade_cleanup(ctx->bbsave) ; /* start again rather than segfault if a seriously buggy * filter in front of us sent a bogus EOS */ f->ctx = NULL ; /* move the EOS to the new brigade */ APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(bbline, b); } else { /* chop flush or unknown metadata bucket types */ apr_bucket_delete(b); } /* OK, reset pointer to what's left (since we're not in a for-loop) */ b = APR_BRIGADE_FIRST(bb) ; } /* OK, now we have a bunch of complete lines in bbline, * so we can apply our edit rules */ /* When we get a match, we split the line into before+match+after. * To flatten that back into one buf every time would be inefficient. * So we treat it as three separate bufs to apply future rules. * * We can only reasonably do that by looping over buckets *inside* * the loop over rules. * * That means concepts like one-match-per-line or start-of-line-only * won't work, except for the first rule. So we won't pretend. */ for (i = 0; i < ctx->rewriterules->nelts; ++i) { for ( b = APR_BRIGADE_FIRST(bbline) ; b != APR_BRIGADE_SENTINEL(bbline) ; b = APR_BUCKET_NEXT(b) ) { if ( !APR_BUCKET_IS_METADATA(b) && (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ) == APR_SUCCESS)) { if ( rules[i].flags & M_REGEX ) { bufp = apr_pstrmemdup(ctx->lpool, buf, bytes) ; while ( ! ap_regexec(rules[i].from.r, bufp, nmatch, pmatch, 0) ) { match = pmatch[0].rm_so ; subs = ap_pregsub(f->r->pool, rules[i].to, bufp, nmatch, pmatch) ; apr_bucket_split(b, match) ; b1 = APR_BUCKET_NEXT(b) ; apr_bucket_split(b1, pmatch[0].rm_eo - match) ; b = APR_BUCKET_NEXT(b1) ; apr_bucket_delete(b1) ; b1 = apr_bucket_pool_create(subs, strlen(subs), f->r->pool, f->r->connection->bucket_alloc) ; APR_BUCKET_INSERT_BEFORE(b, b1) ; bufp += pmatch[0].rm_eo ; } } else { bufp = buf ; while (subs = apr_strmatch(rules[i].from.s, bufp, bytes), subs != NULL) { match = ((unsigned int)subs - (unsigned int)bufp) / sizeof(char) ; bytes -= match ; bufp += match ; apr_bucket_split(b, match) ; b1 = APR_BUCKET_NEXT(b) ; apr_bucket_split(b1, rules[i].length) ; b = APR_BUCKET_NEXT(b1) ; apr_bucket_delete(b1) ; bytes -= rules[i].length ; bufp += rules[i].length ; b1 = apr_bucket_immortal_create(rules[i].to, strlen(rules[i].to), f->r->connection->bucket_alloc) ; APR_BUCKET_INSERT_BEFORE(b, b1) ; } } } } /* If we used a local pool, clear it now */ if ( (ctx->lpool != f->r->pool) && (rules[i].flags & M_REGEX) ) { apr_pool_clear(ctx->lpool) ; } } /* now pass it down the chain */ rv = ap_pass_brigade(f->next, bbline) ; /* if we have leftover data, don't risk it going out of scope */ for ( b = APR_BRIGADE_FIRST(ctx->bbsave) ; b != APR_BRIGADE_SENTINEL(ctx->bbsave) ; b = APR_BUCKET_NEXT(b)) { apr_bucket_setaside(b, f->r->pool) ; } return rv ; }