/* 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.
 */

static MP_INLINE
int mpxs_Apache2__RequestRec_push_handlers(pTHX_ request_rec *r,
                                          const char *name,
                                          SV *sv)
{
    return modperl_handler_perl_add_handlers(aTHX_
                                             r, NULL, r->server, r->pool,
                                             name, sv,
                                             MP_HANDLER_ACTION_PUSH);

}

static MP_INLINE
int mpxs_Apache2__RequestRec_set_handlers(pTHX_ request_rec *r,
                                         const char *name,
                                         SV *sv)
{
    return modperl_handler_perl_add_handlers(aTHX_
                                             r, NULL, r->server, r->pool,
                                             name, sv,
                                             MP_HANDLER_ACTION_SET);
}

static MP_INLINE
SV *mpxs_Apache2__RequestRec_get_handlers(pTHX_ request_rec *r,
                                         const char *name)
{
    MpAV **handp =
        modperl_handler_get_handlers(r, NULL, r->server,
                                     r->pool, name,
                                     MP_HANDLER_ACTION_GET);

    return modperl_handler_perl_get_handlers(aTHX_ handp, r->pool);
}

/*
 * XXX: these three should be part of the apache api
 * for protocol module helpers
 */

static MP_INLINE
SV *mpxs_Apache2__RequestRec_new(pTHX_ SV *classname,
                                conn_rec *c,
                                SV *base_pool_sv)
{
    apr_pool_t *p, *base_pool;
    request_rec *r;
    server_rec *s = c->base_server;
    SV *r_sv;

    /* see: httpd-2.0/server/protocol.c:ap_read_request */

    if (base_pool_sv) {
        base_pool = mp_xs_sv2_APR__Pool(base_pool_sv);
    }
    else {
        base_pool = c->pool;
    }

    apr_pool_create(&p, base_pool);
    r = apr_pcalloc(p, sizeof(request_rec));

    r->pool       = p;
    r->connection = c;
    r->server     = s;

    r->request_time = apr_time_now();

    r->user            = NULL;
    r->ap_auth_type    = NULL;

    r->allowed_methods = ap_make_method_list(p, 1);

    r->headers_in      = apr_table_make(p, 1);
    r->subprocess_env  = apr_table_make(r->pool, 1);
    r->headers_out     = apr_table_make(p, 1);
    r->err_headers_out = apr_table_make(p, 1);
    r->notes           = apr_table_make(p, 1);

    r->request_config = ap_create_request_config(p);

    r->proto_output_filters = c->output_filters;
    r->output_filters       = r->proto_output_filters;
    r->proto_input_filters  = c->input_filters;
    r->input_filters        = r->proto_input_filters;

    ap_run_create_request(r);

    r->per_dir_config = s->lookup_defaults;

    r->sent_bodyct     = 0;
    r->read_length     = 0;
    r->read_body       = REQUEST_NO_BODY;
    r->status          = HTTP_OK;
    r->the_request     = "UNKNOWN";

    r->hostname = s->server_hostname;

    r->method          = "GET";
    r->method_number   = M_GET;
    r->uri             = "/";
    r->filename        = (char *)ap_server_root_relative(p, r->uri);

    r->assbackwards    = 1;
    r->protocol        = "UNKNOWN";

    r_sv = sv_setref_pv(NEWSV(0, 0), "Apache2::RequestRec", (void*)r);

    if (base_pool_sv) {
        mpxs_add_pool_magic(r_sv, base_pool_sv);
    }

    return r_sv;
}

static MP_INLINE
request_rec *mpxs_Apache2__RequestUtil_request(pTHX_ SV *classname, SV *svr)
{
    /* ignore classname */
    return modperl_global_request(aTHX_ svr);
}

static MP_INLINE
int mpxs_Apache2__RequestRec_location_merge(request_rec *r,
                                           char *location)
{
    apr_pool_t *p = r->pool;
    server_rec *s = r->server;
    core_server_config *sconf = ap_get_module_config(s->module_config,
                                                     &core_module);
    ap_conf_vector_t **sec = (ap_conf_vector_t **)sconf->sec_url->elts;
    int num_sec = sconf->sec_url->nelts;
    int i;

    for (i=0; i<num_sec; i++) {
        core_dir_config *entry =
            (core_dir_config *)ap_get_module_config(sec[i],
                                                    &core_module);

        if (strEQ(entry->d, location)) {
            if (!entry->ap_auth_type) {
                entry->ap_auth_type = "Basic";
            }
            if (!entry->ap_auth_name) {
                entry->ap_auth_name = apr_pstrdup(p, location);
            }
            r->per_dir_config =
                ap_merge_per_dir_configs(p, s->lookup_defaults, sec[i]);
            return 1;
        }
    }

    return 0;
}

static MP_INLINE
void mpxs_Apache2__RequestRec_set_basic_credentials(request_rec *r,
                                                   char *username,
                                                   char *password)
{
    char encoded[1024];
    int elen;
    char *auth_value, *auth_cat;

    auth_cat = apr_pstrcat(r->pool,
                           username, ":", password, NULL);
    elen = apr_base64_encode(encoded, auth_cat, strlen(auth_cat));
    encoded[elen] = '\0';

    auth_value = apr_pstrcat(r->pool, "Basic ", encoded, NULL);
    apr_table_setn(r->headers_in, "Authorization", auth_value);
}


static MP_INLINE
int mpxs_Apache2__RequestRec_no_cache(pTHX_ request_rec *r, SV *flag)
{
    int retval = r->no_cache;

    if (flag) {
        r->no_cache = (int)SvIV(flag);
    }

    if (r->no_cache) {
        apr_table_setn(r->headers_out, "Pragma", "no-cache");
        apr_table_setn(r->headers_out, "Cache-control", "no-cache");
    }
    else if (flag) { /* only unset if $r->no_cache(0) */
        apr_table_unset(r->headers_out, "Pragma");
        apr_table_unset(r->headers_out, "Cache-control");
    }

    return retval;
}

static MP_INLINE
SV *mpxs_Apache2__RequestRec_pnotes(pTHX_ request_rec *r, SV *key, SV *val)
{
    MP_dRCFG;

    if (!rcfg) {
        return &PL_sv_undef;
    }

    return modperl_pnotes(aTHX_ &rcfg->pnotes, key, val, r, NULL);
}

#define mpxs_Apache2__RequestRec_dir_config(r, key, sv_val) \
    modperl_dir_config(aTHX_ r, r->server, key, sv_val)

#define mpxs_Apache2__RequestRec_slurp_filename(r, tainted) \
    modperl_slurp_filename(aTHX_ r, tainted)

static MP_INLINE
char *mpxs_Apache2__RequestRec_location(request_rec *r)
{
    MP_dDCFG;

    return dcfg->location;
}

typedef struct {
    PerlInterpreter *perl;
    SV *sv;
} sv_str_header_t;

static int sv_str_header(void *arg, const char *k, const char *v)
{
    sv_str_header_t *svh = (sv_str_header_t *)arg;
    dTHXa(svh->perl);
    Perl_sv_catpvf(aTHX_ svh->sv, "%s: %s\n", k, v);
    return 1;
}

static MP_INLINE
SV *mpxs_Apache2__RequestRec_as_string(pTHX_ request_rec *r)
{
    sv_str_header_t svh;
#ifdef USE_ITHREADS
    svh.perl = aTHX;
#endif

    svh.sv = newSVpv(r->the_request, 0);

    sv_catpvn(svh.sv, "\n", 1);

    apr_table_do((int (*) (void *, const char *, const char *))
                 sv_str_header, (void *) &svh, r->headers_in, NULL);

    Perl_sv_catpvf(aTHX_ svh.sv, "\n%s %s\n", r->protocol, r->status_line);

    apr_table_do((int (*) (void *, const char *, const char *))
                 sv_str_header, (void *) &svh, r->headers_out, NULL);
    apr_table_do((int (*) (void *, const char *, const char *))
                 sv_str_header, (void *) &svh, r->err_headers_out, NULL);

    sv_catpvn(svh.sv, "\n", 1);

    return svh.sv;
}

static MP_INLINE
int mpxs_Apache2__RequestRec_is_perl_option_enabled(pTHX_ request_rec *r,
                                                   const char *name)
{
    return modperl_config_is_perl_option_enabled(aTHX_ r, r->server, name);
}

static MP_INLINE
void mpxs_Apache2__RequestRec_add_config(pTHX_ request_rec *r, SV *lines,
                                         int override, char *path,
                                         int override_options)
{
    const char *errmsg = modperl_config_insert_request(aTHX_ r, lines,
                                                       override, path,
                                                       override_options);
    if (errmsg) {
        Perl_croak(aTHX_ "$r->add_config() has failed: %s", errmsg);
    }
}

/* in order to ensure that s->document_root doesn't get corrupted by
 * modperl users setting its value, restore the original value at the
 * end of each request */
struct mp_docroot_info {
    const char **docroot;
    const char *original;
};

static apr_status_t restore_docroot(void *data)
{
    struct mp_docroot_info *di = (struct mp_docroot_info *)data;
    *di->docroot  = di->original;
    return APR_SUCCESS;
}

static MP_INLINE
const char *mpxs_Apache2__RequestRec_document_root(pTHX_ request_rec *r,
                                                  SV *new_root)
{
    const char *retval = ap_document_root(r);

    if (new_root) {
        struct mp_docroot_info *di;
        core_server_config *conf;
        MP_CROAK_IF_THREADS_STARTED("setting $r->document_root");
        conf = ap_get_module_config(r->server->module_config,
                                    &core_module);
        di = apr_palloc(r->pool, sizeof *di);
        di->docroot = &conf->ap_document_root;
        di->original = conf->ap_document_root;
        apr_pool_cleanup_register(r->pool, di, restore_docroot,
                                  restore_docroot);

        conf->ap_document_root = apr_pstrdup(r->pool, SvPV_nolen(new_root));
    }

    return retval;
}

static apr_status_t child_terminate(void *data) {
    apr_pool_t *pool = (apr_pool_t *)data;

    /* On the first pass, re-register so we end up last */
    if (data) {
        apr_pool_cleanup_register(pool, NULL, child_terminate,
                                  apr_pool_cleanup_null);
    }
    else {
        exit(0);
    }
    return APR_SUCCESS;
}

static MP_INLINE
void mpxs_Apache2__RequestRec_child_terminate(pTHX_ request_rec *r)
{
    MP_CROAK_IF_THREADED_MPM("$r->child_terminate")
    apr_pool_cleanup_register(r->pool, r->pool, child_terminate,
                              apr_pool_cleanup_null);
}
