/******************************************************************************
 * Copyright (C) 2006 Tetsuya Kimata <kimata@acapulco.dyndns.org>
 *
 * All rights reserved.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any
 * damages arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any
 * purpose, including commercial applications, and to alter it and
 * redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must
 *    not claim that you wrote the original software. If you use this
 *    software in a product, an acknowledgment in the product
 *    documentation would be appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and must
 *    not be misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source
 *    distribution.
 *
 * $Id: mod_uploader.cpp 2179 2006-12-07 12:47:20Z svn $
 *****************************************************************************/

/**
 * @file
 * @brief Apache モジュールのエントリ関数群．
 */

// Apache の config.h を先に処理させるため，httpd.h のインクルードはこの位置
#include "httpd.h"

#include "Environment.h"

#include "http_main.h"
#include "http_config.h"
#include "http_request.h"

#include "apr_pools.h"
#include "apr_strings.h"

#include "mod_uploader.h"
#include "uploader_handler.h"

#include "ApacheResponse.h"
#include "ApacheLogger.h"
#include "UploaderConfig.h"
#include "Auxiliary.h"
#include "Macro.h"
#include "SourceInfo.h"

#define TEMPLATE_INSTANTIATION
#define RESPONSE_TYPE_APACHE
#include "uploader_handler.cpp"

SOURCE_INFO_ADD("$Id: mod_uploader.cpp 2179 2006-12-07 12:47:20Z svn $");

static const char *HANDLER_NAME = "uploader";

#define DIRECTIVE_ENTRY(directive, member)                              \
    AP_INIT_TAKE1(UploaderConfig::directive.param,                      \
                  reinterpret_cast<const char*(*)()>(set_ ## member),   \
                  NULL, RSRC_CONF, UploaderConfig::directive.desc)

#define AS_UCONFIG(pointer)         reinterpret_cast<UploaderConfig *>(pointer)

#define DIRECTIVE_HANDLER(member, value)                                \
    static const char *set_ ## member(cmd_parms *parms, void *mconfig,  \
                                      char *arg)                        \
    {                                                                   \
        UploaderConfig *config;                                         \
                                                                        \
        config = AS_UCONFIG(ap_get_module_config(parms->server->module_config, \
                                                 &uploader_module));    \
        config->member = value;                                         \
                                                                        \
        return NULL;                                                    \
    }
#define TEMPLATE_DIRECTIVE_HANDLER(type, tmpl_file_path)                \
    static const char *set_ ## type(cmd_parms *parms, void *mconfig,    \
                                    char *arg)                          \
    {                                                                   \
        UploaderConfig *config;                                         \
                                                                        \
        config = AS_UCONFIG(ap_get_module_config(parms->server->module_config, \
                                                 &uploader_module));    \
        config->set_template(UploaderConfig::type, tmpl_file_path);     \
                                                                        \
        return NULL;                                                    \
    }

UploaderConfig *get_config(request_rec *r)
{
    return AS_UCONFIG(ap_get_module_config(r->server->module_config,
                                           &uploader_module));
}

static apr_status_t finalize_uploader_config(void *data)
{
    UploaderConfig *config;

    config = AS_UCONFIG(data);
    config->finalize();

    return APR_SUCCESS;
}

static void *uploader_create_server_config(apr_pool_t *p, server_rec *s)
{
    UploaderConfig *config;

    APR_PCALLOC(config, UploaderConfig *, s->process->pool, sizeof(UploaderConfig));

    new(config) UploaderConfig(s->process->pool);

    return config;
}

static bool get_apache_debug_mode()
{
    const char **defs;

    defs = AS_CONST_CHAR_P(ap_server_config_defines->elts);

    for (int i = 0; i < ap_server_config_defines->nelts; i++) {
        // -X オプション付きで起動されたときは DEBUG が定義されている
        if (strcmp(defs[i], "DEBUG") == 0) {
            return true;
        }
    }

    return false;
}

static int uploader_post_config_server(apr_pool_t *p, server_rec *s)
{
    UploaderConfig *config;

    config = AS_UCONFIG(ap_get_module_config(s->module_config,
                                             &uploader_module));
    try {
        config->init();
        config->is_debug_mode = get_apache_debug_mode();

        apr_pool_cleanup_register(p, config, finalize_uploader_config,
                                  apr_pool_cleanup_null);
    } catch(const char *message) {
        logger.error(__FILE__, __LINE__, s,
                     "(%s) %s", "UploaderConfig::init", message);
        return HTTP_INTERNAL_SERVER_ERROR;
    }

    return OK;
}

static int uploader_post_config(apr_pool_t *pconf, apr_pool_t *plog,
                                apr_pool_t *ptemp, server_rec *s)
{
    void *user_data;
    apr_status_t status;

    apr_pool_userdata_get(&user_data, PACKAGE_NAME, s->process->pool);

    // 2 回呼ばれるので，1 回目は何もせずに return する
    if (user_data == NULL) {
        apr_pool_userdata_set(reinterpret_cast<const void *>(1), PACKAGE_NAME,
                              apr_pool_cleanup_null, s->process->pool);

        return OK;
    }

    do {
        status = uploader_post_config_server(pconf, s);
        if (status != OK) {
            return status;
        }
    } while ((s = s->next) != NULL);

#ifndef WIN32
    // Windows でこれをするとサービス名が変わってしまうらしい
    ap_add_version_component(pconf, PACKAGE_NAME "/" PACKAGE_VERSION);
#endif

    logger.info(__FILE__, __LINE__, s, "SUCCESS: %s", "post config");

    return OK;
}

static void uploader_child_init_server(apr_pool_t *pool, server_rec *server)
{
    UploaderConfig *config;

    config = AS_UCONFIG(ap_get_module_config(server->module_config,
                                             &uploader_module));
    try {
        config->child_init();
    } catch(const char *message) {
        logger.error(__FILE__, __LINE__, server,
                     "(%s) %s: %s", "UploaderConfig::child_init", message,
                     get_last_error_message(pool));
    }
}

static void uploader_child_init(apr_pool_t *p, server_rec *s)
{
    do {
        uploader_child_init_server(p, s);
    } while ((s = s->next) != NULL);

    logger.info(__FILE__, __LINE__, s, "SUCCESS: %s", "child init");
}

static int uploader_handler(request_rec *r)
{
    UploaderConfig *config;

    if (strcmp(r->handler, HANDLER_NAME)) {
        return DECLINED;
    }

    if (UNLIKELY(!ap_is_initial_req(r))) {
        return DECLINED;
    }

    config = get_config(r);

    if (UNLIKELY(!config->is_active())) {
        return DECLINED;
    }

    try {
        return uploader_command_handler<ApacheResponse>(r, config,
                                                        r->path_info);
        return OK;
    } catch(const char *message) {
        logger.error(__FILE__, __LINE__, r, "Exception: %s", message);

        return HTTP_INTERNAL_SERVER_ERROR;
    }
}

static void uploader_register_hooks(apr_pool_t *pool)
{
    ap_hook_post_config(uploader_post_config, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_child_init(uploader_child_init, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_handler(uploader_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

DIRECTIVE_HANDLER(base_url, apr_pstrdup(parms->pool, arg));
DIRECTIVE_HANDLER(data_dir_path, apr_pstrdup(parms->pool, arg));
DIRECTIVE_HANDLER(file_dir_path, apr_pstrdup(parms->pool, arg));
DIRECTIVE_HANDLER(thumb_dir_path, apr_pstrdup(parms->pool, arg));
DIRECTIVE_HANDLER(temp_dir_path, apr_pstrdup(parms->pool, arg));

DIRECTIVE_HANDLER(total_file_size_limit, apr_atoi64(arg) * 1024);
DIRECTIVE_HANDLER(total_file_number_limit, atoi(arg));
DIRECTIVE_HANDLER(file_size_limit, apr_atoi64(arg) * 1024);
DIRECTIVE_HANDLER(per_page_item_number, atoi(arg));

TEMPLATE_DIRECTIVE_HANDLER(INDEX_VIEW, apr_pstrdup(parms->pool, arg));
TEMPLATE_DIRECTIVE_HANDLER(PROGRESS_VIEW, apr_pstrdup(parms->pool, arg));
TEMPLATE_DIRECTIVE_HANDLER(DOWNLOAD_VIEW, apr_pstrdup(parms->pool, arg));
TEMPLATE_DIRECTIVE_HANDLER(THUMBNAIL_VIEW, apr_pstrdup(parms->pool, arg));
TEMPLATE_DIRECTIVE_HANDLER(ADMIN_VIEW, apr_pstrdup(parms->pool, arg));
TEMPLATE_DIRECTIVE_HANDLER(ERROR_VIEW, apr_pstrdup(parms->pool, arg));

static const command_rec uploader_cmds[] = {
    DIRECTIVE_ENTRY(BASE_URL, base_url),
    DIRECTIVE_ENTRY(DATA_DIRECTORY, data_dir_path),
    DIRECTIVE_ENTRY(FILE_DIRECTORY, file_dir_path),
    DIRECTIVE_ENTRY(THUMB_DIRECTORY, thumb_dir_path),
    DIRECTIVE_ENTRY(TEMP_DIRECTORY, temp_dir_path),

    DIRECTIVE_ENTRY(TOTAL_FILE_SIZE, total_file_size_limit),
    DIRECTIVE_ENTRY(TOTAL_FILE_NUMBER, total_file_number_limit),
    DIRECTIVE_ENTRY(FILE_SIZE_LIMIT, file_size_limit),
    DIRECTIVE_ENTRY(PER_PAGE_ITEM_NUMBER, per_page_item_number),

    DIRECTIVE_ENTRY(INDEX_VIEW_TEMPLATE, INDEX_VIEW),
    DIRECTIVE_ENTRY(PROGRESS_VIEW_TEMPLATE, PROGRESS_VIEW),
    DIRECTIVE_ENTRY(DOWNLOAD_VIEW_TEMPLATE, DOWNLOAD_VIEW),
    DIRECTIVE_ENTRY(THUMBNAIL_VIEW_TEMPLATE, THUMBNAIL_VIEW),
    DIRECTIVE_ENTRY(ADMIN_VIEW_TEMPLATE, ADMIN_VIEW),
    DIRECTIVE_ENTRY(ERROR_VIEW_TEMPLATE, ERROR_VIEW),
    { NULL }
};

extern "C" {
module EXPORT AP_MODULE_DECLARE_DATA uploader_module = {
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    uploader_create_server_config,
    NULL,
    uploader_cmds,
    uploader_register_hooks
};
}

// Local Variables:
// mode: c++
// coding: utf-8-dos
// End:
