mod_session.c revision 563787eb542f06298242d9e681c18be6611a6214
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy/* Licensed to the Apache Software Foundation (ASF) under one or more
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * contributor license agreements. See the NOTICE file distributed with
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * this work for additional information regarding copyright ownership.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * The ASF licenses this file to You under the Apache License, Version 2.0
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * (the "License"); you may not use this file except in compliance with
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * the License. You may obtain a copy of the License at
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy *
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * http://www.apache.org/licenses/LICENSE-2.0
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy *
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * Unless required by applicable law or agreed to in writing, software
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * distributed under the License is distributed on an "AS IS" BASIS,
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * See the License for the specific language governing permissions and
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * limitations under the License.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy#include "mod_session.h"
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy#include "apr_lib.h"
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy#include "apr_strings.h"
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy#include "util_filter.h"
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy#include "http_log.h"
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy#include "http_request.h"
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy#include "http_protocol.h"
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy#define SESSION_EXPIRY "expiry"
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy#define HTTP_SESSION "HTTP_SESSION"
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien RoyAPR_HOOK_STRUCT(
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy APR_HOOK_LINK(session_load)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy APR_HOOK_LINK(session_save)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy APR_HOOK_LINK(session_encode)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy APR_HOOK_LINK(session_decode)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien RoyAPR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ap, SESSION, int, session_load,
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy (request_rec * r, session_rec ** z), (r, z), DECLINED)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien RoyAPR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ap, SESSION, int, session_save,
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy (request_rec * r, session_rec * z), (r, z), DECLINED)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien RoyAPR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, SESSION, int, session_encode,
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy (request_rec * r, session_rec * z), (r, z), OK, DECLINED)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien RoyAPR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, SESSION, int, session_decode,
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy (request_rec * r, session_rec * z), (r, z), OK, DECLINED)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roystatic int session_identity_encode(request_rec * r, session_rec * z);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roystatic int session_identity_decode(request_rec * r, session_rec * z);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roystatic int session_fixups(request_rec * r);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy/**
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * Should the session be included within this URL.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy *
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * This function tests whether a session is valid for this URL. It uses the
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * include and exclude arrays to determine whether they should be included.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roystatic int session_included(request_rec * r, session_dir_conf * conf)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy{
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy const char **includes = (const char **) conf->includes->elts;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy const char **excludes = (const char **) conf->excludes->elts;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy int included = 1; /* defaults to included */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy int i;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (conf->includes->nelts) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy included = 0;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy for (i = 0; !included && i < conf->includes->nelts; i++) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy const char *include = includes[i];
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (strncmp(r->uri, include, strlen(include))) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy included = 1;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (conf->excludes->nelts) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy for (i = 0; included && i < conf->includes->nelts; i++) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy const char *exclude = excludes[i];
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (strncmp(r->uri, exclude, strlen(exclude))) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy included = 0;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return included;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy}
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy/**
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * Load the session.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy *
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * If the session doesn't exist, a blank one will be created.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy *
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * @param r The request
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * @param z A pointer to where the session will be written.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roystatic apr_status_t ap_session_load(request_rec * r, session_rec ** z)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy{
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy session_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy &session_module);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy apr_time_t now;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy session_rec *zz = NULL;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy int rv = 0;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy /* is the session enabled? */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (!dconf || !dconf->enabled) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return APR_SUCCESS;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy /* should the session be loaded at all? */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (!session_included(r, dconf)) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01814)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "excluded by configuration for: %s", r->uri);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return APR_SUCCESS;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy /* load the session from the session hook */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy rv = ap_run_session_load(r, &zz);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (DECLINED == rv) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01815)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "session is enabled but no session modules have been configured, "
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "session not loaded: %s", r->uri);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return APR_EGENERAL;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy else if (OK != rv) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01816)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "error while loading the session, "
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "session not loaded: %s", r->uri);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return rv;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy /* found a session that hasn't expired? */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy now = apr_time_now();
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (!zz || (zz->expiry && zz->expiry < now)) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy /* no luck, create a blank session */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy zz = (session_rec *) apr_pcalloc(r->pool, sizeof(session_rec));
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy zz->pool = r->pool;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy zz->entries = apr_table_make(zz->pool, 10);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy zz->uuid = (apr_uuid_t *) apr_pcalloc(zz->pool, sizeof(apr_uuid_t));
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy apr_uuid_get(zz->uuid);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy else {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy rv = ap_run_session_decode(r, zz);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (OK != rv) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01817)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "error while decoding the session, "
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "session not loaded: %s", r->uri);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return rv;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy /* make sure the expiry is set, if present */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (!zz->expiry && dconf->maxage) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy zz->expiry = now + dconf->maxage * APR_USEC_PER_SEC;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy zz->maxage = dconf->maxage;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy *z = zz;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return APR_SUCCESS;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy}
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy/**
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * Save the session.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy *
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * In most implementations the session is only saved if the dirty flag is
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * true. This prevents the session being saved unnecessarily.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy *
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * @param r The request
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * @param z A pointer to where the session will be written.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roystatic apr_status_t ap_session_save(request_rec * r, session_rec * z)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy{
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (z) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy apr_time_t now = apr_time_now();
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy int rv = 0;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy session_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy &session_module);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy /* sanity checks, should we try save at all? */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (z->written) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01818)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "attempt made to save the session twice, "
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "session not saved: %s", r->uri);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return APR_EGENERAL;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (z->expiry && z->expiry < now) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01819)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "attempt made to save a session when the session had already expired, "
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "session not saved: %s", r->uri);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return APR_EGENERAL;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy /* reset the expiry back to maxage, if the expiry is present */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (dconf->maxage) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy z->expiry = now + dconf->maxage * APR_USEC_PER_SEC;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy z->maxage = dconf->maxage;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy /* encode the session */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy rv = ap_run_session_encode(r, z);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (OK != rv) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01820)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "error while encoding the session, "
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "session not saved: %s", r->uri);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return rv;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy /* try the save */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy rv = ap_run_session_save(r, z);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (DECLINED == rv) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01821)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "session is enabled but no session modules have been configured, "
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "session not saved: %s", r->uri);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return APR_EGENERAL;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy else if (OK != rv) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01822)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "error while saving the session, "
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy "session not saved: %s", r->uri);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return rv;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy else {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy z->written = 1;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return APR_SUCCESS;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy}
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy/**
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * Get a particular value from the session.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * @param r The current request.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * @param z The current session. If this value is NULL, the session will be
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * looked up in the request, created if necessary, and saved to the request
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * notes.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * @param key The key to get.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * @param value The buffer to write the value to.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roystatic apr_status_t ap_session_get(request_rec * r, session_rec * z,
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy const char *key, const char **value)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy{
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (!z) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy apr_status_t rv;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy rv = ap_session_load(r, &z);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (APR_SUCCESS != rv) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return rv;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (z && z->entries) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy *value = apr_table_get(z->entries, key);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return OK;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy}
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy/**
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * Set a particular value to the session.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy *
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * Using this method ensures that the dirty flag is set correctly, so that
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * the session can be saved efficiently.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * @param r The current request.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * @param z The current session. If this value is NULL, the session will be
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * looked up in the request, created if necessary, and saved to the request
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * notes.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * @param key The key to set. The existing key value will be replaced.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy * @param value The value to set.
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy */
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roystatic apr_status_t ap_session_set(request_rec * r, session_rec * z,
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy const char *key, const char *value)
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy{
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (!z) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy apr_status_t rv;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy rv = ap_session_load(r, &z);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (APR_SUCCESS != rv) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy return rv;
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (z) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy if (value) {
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy apr_table_set(z->entries, key, value);
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy }
2b24ab6b3865caeede9eeb9db6b83e1d89dcd1eaSebastien Roy else {
apr_table_unset(z->entries, key);
}
z->dirty = 1;
}
return APR_SUCCESS;
}
static int identity_count(int *count, const char *key, const char *val)
{
*count += strlen(key) * 3 + strlen(val) * 3 + 1;
return 1;
}
static int identity_concat(char *buffer, const char *key, const char *val)
{
char *slider = buffer;
int length = strlen(slider);
slider += length;
if (length) {
*slider = '&';
slider++;
}
ap_escape_urlencoded_buffer(slider, key);
slider += strlen(slider);
*slider = '=';
slider++;
ap_escape_urlencoded_buffer(slider, val);
return 1;
}
/**
* Default identity encoding for the session.
*
* By default, the name value pairs in the session are URLEncoded, separated
* by equals, and then in turn separated by ampersand, in the format of an
* html form.
*
* This was chosen to make it easy for external code to unpack a session,
* should there be a need to do so.
*
* @param r The request pointer.
* @param z A pointer to where the session will be written.
*/
static apr_status_t session_identity_encode(request_rec * r, session_rec * z)
{
char *buffer = NULL;
int length = 0;
if (z->expiry) {
char *expiry = apr_psprintf(z->pool, "%" APR_INT64_T_FMT, z->expiry);
apr_table_setn(z->entries, SESSION_EXPIRY, expiry);
}
apr_table_do((int (*) (void *, const char *, const char *))
identity_count, &length, z->entries, NULL);;
buffer = apr_pcalloc(r->pool, length + 1);
apr_table_do((int (*) (void *, const char *, const char *))
identity_concat, buffer, z->entries, NULL);
z->encoded = buffer;
return OK;
}
/**
* Default identity decoding for the session.
*
* By default, the name value pairs in the session are URLEncoded, separated
* by equals, and then in turn separated by ampersand, in the format of an
* html form.
*
* This was chosen to make it easy for external code to unpack a session,
* should there be a need to do so.
*
* This function reverses that process, and populates the session table.
*
* Name / value pairs that are not encoded properly are ignored.
*
* @param r The request pointer.
* @param z A pointer to where the session will be written.
*/
static apr_status_t session_identity_decode(request_rec * r, session_rec * z)
{
char *last = NULL;
char *encoded, *pair;
const char *sep = "&";
/* sanity check - anything to decode? */
if (!z->encoded) {
return OK;
}
/* decode what we have */
encoded = apr_pstrcat(r->pool, z->encoded, NULL);
pair = apr_strtok(encoded, sep, &last);
while (pair && pair[0]) {
char *plast = NULL;
const char *psep = "=";
char *key = apr_strtok(pair, psep, &plast);
char *val = apr_strtok(NULL, psep, &plast);
if (key && *key) {
if (!val || !*val) {
apr_table_unset(z->entries, key);
}
else if (!ap_unescape_urlencoded(key) && !ap_unescape_urlencoded(val)) {
if (!strcmp(SESSION_EXPIRY, key)) {
z->expiry = (apr_time_t) apr_atoi64(val);
}
else {
apr_table_set(z->entries, key, val);
}
}
}
pair = apr_strtok(NULL, sep, &last);
}
z->encoded = NULL;
return OK;
}
/**
* Ensure any changes to the session are committed.
*
* This is done in an output filter so that our options for where to
* store the session can include storing the session within a cookie:
* As an HTTP header, the cookie must be set before the output is
* written, but after the handler is run.
*
* NOTE: It is possible for internal redirects to cause more than one
* request to be present, and each request might have a session
* defined. We need to go through each session in turn, and save each
* one.
*
* The same session might appear in more than one request. The first
* attempt to save the session will be called
*/
static apr_status_t session_output_filter(ap_filter_t * f,
apr_bucket_brigade * in)
{
/* save all the sessions in all the requests */
request_rec *r = f->r->main;
if (!r) {
r = f->r;
}
while (r) {
session_rec *z = NULL;
session_dir_conf *conf = ap_get_module_config(r->per_dir_config,
&session_module);
/* load the session, or create one if necessary */
/* when unset or on error, z will be NULL */
ap_session_load(r, &z);
if (!z || z->written) {
r = r->next;
continue;
}
/* if a header was specified, insert the new values from the header */
if (conf->header_set) {
const char *override = apr_table_get(r->err_headers_out, conf->header);
if (!override) {
override = apr_table_get(r->headers_out, conf->header);
}
if (override) {
z->encoded = override;
session_identity_decode(r, z);
}
}
/* save away the session, and we're done */
/* when unset or on error, we've complained to the log */
ap_session_save(r, z);
r = r->next;
}
/* remove ourselves from the filter chain */
ap_remove_output_filter(f);
/* send the data up the stack */
return ap_pass_brigade(f->next, in);
}
/**
* Insert the output filter.
*/
static void session_insert_output_filter(request_rec * r)
{
ap_add_output_filter("MOD_SESSION_OUT", NULL, r, r->connection);
}
/**
* Fixups hook.
*
* Load the session within a fixup - this ensures that the session is
* properly loaded prior to the handler being called.
*
* The fixup is also responsible for injecting the session into the CGI
* environment, should the admin have configured it so.
*
* @param r The request
*/
static int session_fixups(request_rec * r)
{
session_dir_conf *conf = ap_get_module_config(r->per_dir_config,
&session_module);
session_rec *z = NULL;
/* if an error occurs or no session has been configured, we ignore
* the broken session and allow it to be recreated from scratch on save
* if necessary.
*/
ap_session_load(r, &z);
if (z && conf->env) {
session_identity_encode(r, z);
if (z->encoded) {
apr_table_set(r->subprocess_env, HTTP_SESSION, z->encoded);
z->encoded = NULL;
}
}
return OK;
}
static void *create_session_dir_config(apr_pool_t * p, char *dummy)
{
session_dir_conf *new =
(session_dir_conf *) apr_pcalloc(p, sizeof(session_dir_conf));
new->includes = apr_array_make(p, 10, sizeof(const char **));
new->excludes = apr_array_make(p, 10, sizeof(const char **));
return (void *) new;
}
static void *merge_session_dir_config(apr_pool_t * p, void *basev, void *addv)
{
session_dir_conf *new = (session_dir_conf *) apr_pcalloc(p, sizeof(session_dir_conf));
session_dir_conf *add = (session_dir_conf *) addv;
session_dir_conf *base = (session_dir_conf *) basev;
new->enabled = (add->enabled_set == 0) ? base->enabled : add->enabled;
new->enabled_set = add->enabled_set || base->enabled_set;
new->maxage = (add->maxage_set == 0) ? base->maxage : add->maxage;
new->maxage_set = add->maxage_set || base->maxage_set;
new->header = (add->header_set == 0) ? base->header : add->header;
new->header_set = add->header_set || base->header_set;
new->env = (add->env_set == 0) ? base->env : add->env;
new->env_set = add->env_set || base->env_set;
new->includes = apr_array_append(p, base->includes, add->includes);
new->excludes = apr_array_append(p, base->excludes, add->excludes);
return new;
}
static const char *
set_session_enable(cmd_parms * parms, void *dconf, int flag)
{
session_dir_conf *conf = dconf;
conf->enabled = flag;
conf->enabled_set = 1;
return NULL;
}
static const char *
set_session_maxage(cmd_parms * parms, void *dconf, const char *arg)
{
session_dir_conf *conf = dconf;
conf->maxage = atol(arg);
conf->maxage_set = 1;
return NULL;
}
static const char *
set_session_header(cmd_parms * parms, void *dconf, const char *arg)
{
session_dir_conf *conf = dconf;
conf->header = arg;
conf->header_set = 1;
return NULL;
}
static const char *
set_session_env(cmd_parms * parms, void *dconf, int flag)
{
session_dir_conf *conf = dconf;
conf->env = flag;
conf->env_set = 1;
return NULL;
}
static const char *add_session_include(cmd_parms * cmd, void *dconf, const char *f)
{
session_dir_conf *conf = dconf;
const char **new = apr_array_push(conf->includes);
*new = f;
return NULL;
}
static const char *add_session_exclude(cmd_parms * cmd, void *dconf, const char *f)
{
session_dir_conf *conf = dconf;
const char **new = apr_array_push(conf->excludes);
*new = f;
return NULL;
}
static const command_rec session_cmds[] =
{
AP_INIT_FLAG("Session", set_session_enable, NULL, RSRC_CONF|OR_AUTHCFG,
"on if a session should be maintained for these URLs"),
AP_INIT_TAKE1("SessionMaxAge", set_session_maxage, NULL, RSRC_CONF|OR_AUTHCFG,
"length of time for which a session should be valid. Zero to disable"),
AP_INIT_TAKE1("SessionHeader", set_session_header, NULL, RSRC_CONF|OR_AUTHCFG,
"output header, if present, whose contents will be injected into the session."),
AP_INIT_FLAG("SessionEnv", set_session_env, NULL, RSRC_CONF|OR_AUTHCFG,
"on if a session should be written to the CGI environment. Defaults to off"),
AP_INIT_TAKE1("SessionInclude", add_session_include, NULL, RSRC_CONF|OR_AUTHCFG,
"URL prefixes to include in the session. Defaults to all URLs"),
AP_INIT_TAKE1("SessionExclude", add_session_exclude, NULL, RSRC_CONF|OR_AUTHCFG,
"URL prefixes to exclude from the session. Defaults to no URLs"),
{NULL}
};
static void register_hooks(apr_pool_t * p)
{
ap_register_output_filter("MOD_SESSION_OUT", session_output_filter,
NULL, AP_FTYPE_CONTENT_SET);
ap_hook_insert_filter(session_insert_output_filter, NULL, NULL,
APR_HOOK_MIDDLE);
ap_hook_insert_error_filter(session_insert_output_filter,
NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_fixups(session_fixups, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_session_encode(session_identity_encode, NULL, NULL,
APR_HOOK_REALLY_FIRST);
ap_hook_session_decode(session_identity_decode, NULL, NULL,
APR_HOOK_REALLY_LAST);
APR_REGISTER_OPTIONAL_FN(ap_session_get);
APR_REGISTER_OPTIONAL_FN(ap_session_set);
APR_REGISTER_OPTIONAL_FN(ap_session_load);
APR_REGISTER_OPTIONAL_FN(ap_session_save);
}
AP_DECLARE_MODULE(session) =
{
STANDARD20_MODULE_STUFF,
create_session_dir_config, /* dir config creater */
merge_session_dir_config, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
session_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};