/**
 * Простой модуль к веб-серверу Apache, использующий
 * фильтры для приведения тегов HTML к верхнему регистру.
 *
 * Этот модуль был написан в качестве простого примера
 * специально для статьи на персональном блоге автора
 * http://mikhailstadnik.com, не используйте его для
 * решения подобной реальной жизненной задачи без четкого
 * представления о том что он делает и как. Если вы
 * все же решите его использовать, помните - вы
 * действуете исключительно НА СВОЙ СТРАХ И РИСК,
 * без каких либо последующих претензий к автору.
 *
 * Автор НЕ НЕСЕТ ответственности за работу модуля,
 * НЕ ДАЕТ никаких гарантий и НЕ РАССМАТРИВАЕТ претензии
 * о потере данных, материальных и нематериальных
 * утратах в связи с работой модуля и каких-либо других
 * исков.
 *
 * @author Михаил Стадник <http://mikhailstadnik.com>
 */
#include "apr_general.h"
#include "apr_lib.h"
#include "apr_buckets.h"
#include "apr_strings.h"
#include "ap_config.h"
#include "util_filter.h"
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_log.h"
#include "http_main.h"
#include "util_script.h"
#include "http_core.h"
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/stat.h>

module AP_MODULE_DECLARE_DATA uptags_module;

typedef struct uptags_cfg {
    int engine;
} uptags_cfg;

/**
 * Данная функция возвращет структуру конфигурации директории
 * данного модуля для текущего запроса
 */
static uptags_cfg *uptags_dconfig( const request_rec *r) {
    return (uptags_cfg *) ap_get_module_config( r->per_dir_config, &uptags_module);
}

/**
 * Данная функция возвращет структуру конфигурации сервера
 * данного модуля
 */
static uptags_cfg *uptags_sconfig( const server_rec *s) {
    return (uptags_cfg *) ap_get_module_config( s->module_config, &uptags_module);
}

/**
 * Инициализация настроек по-умолчанию для директории
 */
static void *uptags_create_dir_config( apr_pool_t *p, char *dirspec) {
    uptags_cfg *cfg;

    /**
     * Выделим память в пуле соединения для структуры настроек
     */
    cfg = (uptags_cfg *) apr_pcalloc( p, sizeof( uptags_cfg));

    /**
     * Теперь определим значения по умолчанию для наших настроек
     */
    cfg->engine = 1;
 
    return (void *) cfg;
}

/**
 * Для серверных настроек определим аналогичную функцию
 */
static void *uptags_create_server_config( apr_pool_t *p, server_rec *s) {
    uptags_cfg *cfg;
    cfg = (uptags_cfg *) apr_pcalloc( p, sizeof( uptags_cfg));

    cfg->engine = 1;

    return (void *) cfg;
}

static command_rec uptags_directives[] = {
    AP_INIT_FLAG(
        "UptagsEngine",
        ap_set_flag_slot,
        (void *) APR_OFFSETOF( uptags_cfg, engine),
        OR_OPTIONS,
        "uptags module switcher"
    ),

    {NULL}
};

/**
 * Функция преобразует тэги в строке к верхнему регистру
 */
void uptags_tags_to_uppercase( const char *data, char *str) {
    int i, s = strlen( data), tag_opened = 0;
    for (i = 0; i < s; i++) {
        str[i] = data[i];
        if (str[i] == '<') {
            tag_opened = 1;
        } else if (str[i] == '>') {
            tag_opened = 0;
        }
        if (tag_opened && str[i] != '\0') {
            str[i] = apr_toupper( str[i]);
        }
    }
}

/**
 * Входной фильтр
 */
static void uptags_in_filter( request_rec *r) {
    /**
     * На входе нам не нужно ничего делать, просто добавляем выходной фильтр к стеку фильтров
     */
    ap_add_output_filter( "Uptags", NULL, r, r->connection);
}

/**
 * Выходной фильтр - именно он будет делать все полезную работу
 */
static apr_status_t uptags_out_filter( ap_filter_t *f, apr_bucket_brigade *pbbIn) {

    request_rec        *r = f->r;
    conn_rec           *c = r->connection;
    apr_bucket         *pbktIn;
    apr_bucket_brigade *pbbOut;
    uptags_cfg         *cfg = uptags_dconfig( f->r);

    /**
     * Проверяем, включена ли работа фильтра в настройках. Если нет - ничего не делаем с исходными данными
     * Также не следует ничего делать, если данный контент не является HTML
     */
    if (!cfg->engine || strcmp( r->content_type, "text/html") != 0) {
        return ap_pass_brigade( f->next, pbbIn);
    }

    /* инициализируем пустую выходную бригаду */
    pbbOut = apr_brigade_create( r->pool, c->bucket_alloc);

    /**
     * Читаем корзины данных из входящей бригады
     */
    for (pbktIn = APR_BRIGADE_FIRST( pbbIn); pbktIn != APR_BRIGADE_SENTINEL( pbbIn); pbktIn = APR_BUCKET_NEXT( pbktIn)) {
        const char *data;
        apr_size_t len;
        char *buf;
        apr_bucket *pbktOut;

        /* если текущая "корзина" - это метаданные-признак конца - просто перемещаем корзину в конец бригады и продолжаем обход данных */
        if(APR_BUCKET_IS_EOS( pbktIn)) {
            apr_bucket *pbktEOS = apr_bucket_eos_create( c->bucket_alloc);
            APR_BRIGADE_INSERT_TAIL( pbbOut,pbktEOS);
            continue;
        }

        /* читаем данные */
        apr_bucket_read( pbktIn, &data, &len, APR_NONBLOCK_READ);

        /* производим полезную работу над данными */
        buf = apr_bucket_alloc( len, c->bucket_alloc);
        memset( buf, 0, sizeof( buf));

        uptags_tags_to_uppercase( data, buf);

        /* записываем новые данные */
        pbktOut = apr_bucket_heap_create( buf, len, apr_bucket_free, c->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL( pbbOut, pbktOut);
    }

    apr_brigade_cleanup( pbbIn);
    return ap_pass_brigade( f->next, pbbOut);
}

/**
 * Регистрируем фильтры модуля в обработчиках
 */
static void uptags_register_hooks( apr_pool_t *p) {
    ap_hook_insert_filter( uptags_in_filter, NULL, NULL, APR_HOOK_MIDDLE);
    ap_register_output_filter( "Uptags", uptags_out_filter, NULL, AP_FTYPE_RESOURCE);
}

/**
 * Описываем структуру модуля Apache
 */
module AP_MODULE_DECLARE_DATA uptags_module = {
    STANDARD20_MODULE_STUFF,
    uptags_create_dir_config,      /* создание конфигурации директории */
    NULL,                          /* объединение конфигураций директории */
    uptags_create_server_config,   /* создание серверной конфигурации */
    NULL,                          /* объединение серверной конфигурации */
    uptags_directives,             /* маппинг директив конфигурации */
    uptags_register_hooks          /* регистрируем обработчики */
};

