/****************************************************************************

   mod_dav_pgsql (DASL) for apache 2.X 
   CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
   Author : sean kim (sean@solutionbox.co.kr)

   $Id: main.c,v 1.2 2007/01/25 06:52:16 elenoa Exp $

   Redistribution and use in source and binary forms, with or with out
   modification, are not permitted in outside of SolutionBox Inc.

 ****************************************************************************/

#include <httpd.h>
#include <http_config.h>
#include <http_protocol.h>
#include <http_log.h>
#include <http_core.h>		/* for ap_construct_url */
#include <http_request.h>
#include <http_connection.h>

#include <apr_strings.h>
#include <apr_hash.h>
#include <apr_base64.h>

#include "dav_repos.h"
#include "dbms.h"
#include "util.h"
#include "share_common.h"
#include "share_statistic.h"
#include "share_anonymous.h"
#include "transfer.h"
#include "inout_filter.h"

#define INHERIT_VALUE(parent, child, field) \
                ((child)->field ? (child)->field : (parent)->field)

#define DBMS_PRIVIDER_NAME "repos"

#define MaxSizeOfQueryBuffer 1024
/* Note: the "dav_repos" prefix is mandatory */
extern module AP_MODULE_DECLARE_DATA dav_repos_module;


#if APR_HAS_THREADS
apr_thread_rwlock_t *db_lock = NULL;
#endif
//apr_global_mutex_t *db_global_lock = NULL;

// 2012.07.04 dadamin
// FreeBSD 경우 getmntent 함수 쓰레드 지원하지 않기 때문에
// 관련 함수 사용 로직에 lock을 사용하여 이를 대처함
#ifdef __FreeBSD__
apr_global_mutex_t *local_disk_lock = NULL;
#endif // __FreeBSD__

static void *dav_repos_create_dir_config(apr_pool_t * p, char *dir)
{
    /* NOTE: dir==NULL creates the default per-dir config */
    dav_repos_dir_conf *conf;
    conf = (dav_repos_dir_conf *) apr_pcalloc(p, sizeof(*conf));

    return conf;

}

static void *dav_repos_merge_dir_config(apr_pool_t * p, void *base,
					void *overrides)
{
   
    dav_repos_dir_conf *newconf;
    newconf = apr_pcalloc(p, sizeof(*newconf));

    return newconf;
}

static void *dav_repos_create_server_config(apr_pool_t * p, server_rec * s)
{
    dav_repos_server_conf *newconf;

    newconf = (dav_repos_server_conf *) apr_pcalloc(p, sizeof(*newconf));  
 
	return newconf;
}

static void *dav_repos_merge_server_config(apr_pool_t * p, void *base, void *overrides)
{
    dav_repos_server_conf *parent = base;
    dav_repos_server_conf *child = overrides;
    dav_repos_server_conf *newconf;

	newconf = (dav_repos_server_conf *) apr_pcalloc(p, sizeof(*newconf));

	newconf->storage_dir = INHERIT_VALUE(parent, child, storage_dir);

    return newconf;
}


/*
 * Command handler for the DAV directive, which is TAKE1.
 */
static const char *dav_repos_storage_dir_cmd(cmd_parms * cmd, void *config, const char *arg1)
{
	dav_repos_server_conf *conf = ap_get_module_config(cmd->server->module_config, &dav_repos_module);
	char *pszSafeStr;

	pszSafeStr = apr_pcalloc(cmd->pool, strlen(arg1) + 1);
	dav_repos_replace_multi_slash_chars(arg1, pszSafeStr);
	conf->storage_dir = apr_pstrdup(cmd->pool, pszSafeStr);

	return NULL;
}

static const char *dav_repos_base_uri_cmd(cmd_parms * cmd, void *config, const char *arg1)
{
	dav_repos_server_conf *conf = ap_get_module_config(cmd->server->module_config, &dav_repos_module);
	conf->base_uri = apr_pstrdup(cmd->pool, arg1);

	return NULL;
}


static const char *dav_repos_host_name_cmd(cmd_parms * cmd, void *config, const char *arg1)
{
	dav_repos_server_conf *conf = ap_get_module_config(cmd->server->module_config, &dav_repos_module);
    conf->host_name = apr_pstrdup(cmd->pool, arg1);

    return NULL;
}

static const char *dav_repos_max_fhs(cmd_parms * cmd, void *config, const char *arg1)
{
	dav_repos_server_conf *conf = ap_get_module_config(cmd->server->module_config, &dav_repos_module);
    conf->max_fhs = apr_pstrdup(cmd->pool, arg1);

	return NULL;
}

static const char *dav_repos_max_anony(cmd_parms * cmd, void *config,
					 const char *arg1)
{
	dav_repos_server_conf *conf = ap_get_module_config(cmd->server->module_config, &dav_repos_module);
    conf->max_anony_realm = apr_pstrdup(cmd->pool, arg1);

	return NULL;
}

static const char *dav_repos_max_progess(cmd_parms * cmd, void *config, const char *arg1)
{
	dav_repos_server_conf *conf = ap_get_module_config(cmd->server->module_config, &dav_repos_module);

	conf->max_progress = atoi(arg1);
	
	return NULL;
}

// CHG 2019-09-24 huibong Bigbox FHS 장비 사용에 따른 mount 별 운영여유공간 계산방식 개선 (#32807)
// - 기존 conf 상의 DavProviderStorageLimit 설정은 제거 처리
/*
static const char *dav_repos_storage_limit(cmd_parms * cmd, void *config, const char *arg1)
{
	dav_repos_server_conf *conf = ap_get_module_config(cmd->server->module_config, &dav_repos_module);

	conf->storage_limit = atoi(arg1);

	return NULL;
}
*/

#define DEF_USER_CACHE	20

static const char *dav_repos_user_cache(cmd_parms * cmd, void *config, const char *arg1)
{
	dav_repos_server_conf *conf = ap_get_module_config(cmd->server->module_config, &dav_repos_module);

    conf->user_cache = atoi(arg1);
	if( conf->user_cache <= 0 ) 
	{
		conf->user_cache = DEF_USER_CACHE;
	}

	return NULL;
}

static const char *dav_repos_db_charset(cmd_parms * cmd, void *config, const char *arg1)
{
	dav_repos_server_conf *conf = ap_get_module_config(cmd->server->module_config, &dav_repos_module);
	conf->db_charset = apr_pstrdup(cmd->pool, arg1);

	if(conf->db_charset == NULL || strlen(conf->db_charset) <= 0 )
	{
		conf->db_charset = apr_pstrdup(cmd->pool, "UHC");
	}

	return NULL;
}

static const char *dav_repos_close_insert(cmd_parms * cmd, void *config, const char *arg1)
{
	dav_repos_server_conf *conf = ap_get_module_config(cmd->server->module_config, &dav_repos_module);
	
	conf->close_insertoff = strcasecmp(arg1, "on");

	return NULL;
}

// CHG 2012-06-27 dadamin
// sibling 및 sync_host 기능은 2011 년 BD-06 사내망 제거 이후 더 이상 사용하지 않는 기능임.
// 따라서 아래 관련 코드를 주석 처리함.
//static const char *dav_repos_sibling(cmd_parms * cmd, void *config, const char *arg1)
//{
//	dav_repos_server_conf *conf = ap_get_module_config(cmd->server->module_config, &dav_repos_module);
//	conf->sibling = apr_pstrdup(cmd->pool, arg1);
//
//	return NULL;
//}

//static const char *dav_repos_sync_host(cmd_parms * cmd, void *config, const char *arg1)
//{
//	dav_repos_server_conf *conf = ap_get_module_config(cmd->server->module_config, &dav_repos_module);
//  conf->sync_host = apr_pstrdup(cmd->pool, arg1);
//
//	return NULL;
//}

static const command_rec dav_repos_cmds[] = {

	/* per directory/location */
	/* how can I make it mandatory */
	/* RSRC_CONF : For server */
	/* ACCESS_CONF : For dir */
	AP_INIT_TAKE1("DavProviderBaseURI", dav_repos_base_uri_cmd, NULL, RSRC_CONF, "specify DavProviderBaseURI"),
	AP_INIT_TAKE1("DavProviderStorageDir", dav_repos_storage_dir_cmd, NULL, RSRC_CONF, "specify DavProviderStorageDir"),
	AP_INIT_TAKE1("DavProviderHostName", dav_repos_host_name_cmd, NULL, RSRC_CONF, "specify DavProviderHostName"),	
	AP_INIT_TAKE1("DavProviderMaxFHS", dav_repos_max_fhs, NULL, RSRC_CONF, "specify DavProviderMaxFHS"),
	AP_INIT_TAKE1("DavProviderMaxAnonymous", dav_repos_max_anony, NULL, RSRC_CONF, "specify DavProviderMaxAnonymous"),
	AP_INIT_TAKE1("DavProviderMaxAccount", dav_repos_user_cache, NULL, RSRC_CONF, "specify DavProviderMaxAccount"),
	AP_INIT_TAKE1("DavProviderMaxProgess", dav_repos_max_progess, NULL, RSRC_CONF, "specify DavProviderMaxProgess"),

	// CHG 2019-09-24 huibong Bigbox FHS 장비 사용에 따른 mount 별 운영여유공간 계산방식 개선 (#32807)
	// - 기존 conf 상의 DavProviderStorageLimit 설정은 제거 처리
	//AP_INIT_TAKE1("DavProviderStorageLimit", dav_repos_storage_limit, NULL, RSRC_CONF, "specify DavProviderStorageLimit"),
	
	// CHG 2012-06-27 dadamin
	// sibling 및 sync_host 기능은 2011 년 BD-06 사내망 제거 이후 더 이상 사용하지 않는 기능임.
	// 따라서 아래 관련 코드를 주석 처리함.
	//AP_INIT_TAKE1("DavProviderSibling", dav_repos_sibling, NULL, RSRC_CONF, "specify DavProviderSibling"),
	//AP_INIT_TAKE1("DavProviderSyncHost", dav_repos_sync_host, NULL, RSRC_CONF, "specify DavProviderSyncHost"),

	// 2012.06.27 dadamin
	// Database Client encoding
	AP_INIT_TAKE1("DavProviderDBCharset", dav_repos_db_charset, NULL, RSRC_CONF, "specify DavProviderDBCharset"),

	// 2012.07.13 dadamin
	// close 시 insert 유무 설정( On : Insert , off : insert X)
	AP_INIT_TAKE1("DavProviderCloseInsert", dav_repos_close_insert, NULL, RSRC_CONF, "specify dav_repos_close_insert"),

	{NULL}

};

static int dav_repos_init_handler(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * ptemp, server_rec * s)
{
	void *data;
	const char *userdata_key = "mod_dav_repos_init";

	//int dav_of_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s);

    ap_add_version_component(p,
			"OpenDisk/" VERSION
#ifdef __OPENDAV_DEBUG__
			",DEBUG"
#endif
#ifdef __OPENDAV_DEBUG_AUTH__
			",Da"
#endif
#ifdef __OPENDAV_DEBUG_SHARED__
			",Ds"
#endif
#ifdef __OPENDAV_DEBUG_USER_AUTH__
			",Da"
#endif
#ifdef __OPENDAV_DEBUG_TRAFFIC_CONTROL__
			",Dtc"
#endif
#ifdef __USE_TRANSFER_LOG__
			",tl"
#endif

#ifdef __OPENDAV_BAND_CONTROL__
#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
			",ct"
#else
			",nt"
#endif
#endif

#ifdef __ONLY_GET_SUCCESS_OUT__
			",so"
#endif
#ifdef __OPENDAV_TRANSFER_LOG_ANONYMOUS__
			",tla"
#endif
#ifdef __USE_MULTIPLE_STORAGE__
			",ms"
#endif
#ifdef __USE_OLD_AUTH__
			",oa"
#endif
#ifdef __NOT_USE_AUTH_TIMESLICE__
			",nat"
#endif
/* from here, not affected, i supposed */
#ifdef __OPENDAV_USER_AUTH_GET
			",uag"
#endif
#ifdef __NOT_USE_LOCKDB__
			",nul"
#endif
#ifdef __OPENDAV_OLDAUTH_LOG__
			",oal"
#endif
#ifdef __OPENDAV_SHARED_NOT_USE
			",snu"
#endif
#ifdef __OPENDISK_SOLUTION__
			",os"
#endif
#ifdef __OPENDISK_SOLUTION_NOT_USE_TS__
			",nut"
#endif // !__OPENDISK_SOLUTION_NOT_USE_TS__
#ifdef __UNUSE_FILELOCK__
			",nfl"
#endif // __UNUSE_FILELOCK__

#ifdef __USE_AUTH_SOFTLINE_ONLY__
            ",uso"
#endif // __USE_AUTH_SOFTLINE_ONLY__

/* until here, not affected, i supposed */
			);

	/* Elenoa: 2006. 1. 3. spawn new child */
	apr_pool_userdata_get(&data, userdata_key, s->process->pool);

	if( !data ) 
	{
		apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
		return OK;
	}

	ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "OPENDAV: initialize...");

	/* shared memory */
	// 2012.09.20 : dadamin
	// 공유 메모리 attach 실패 시 프로세스 종료되게 한다.
	if( dav_initialize_shared_memory(s, s->process->pool) != OK )
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "OPENDAV: dav_initialize_shared_memory failed. Exit Process.");
		return !OK;
	}
    apr_pool_cleanup_register(p, s->process->pool, dav_cleanup_shared_memory, dav_cleanup_shared_memory);

	/* transfer queue */
	if (dav_initialize_transfer_queue(s, s->process->pool) != OK)
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "OPENDAV: dav_initialize_transfer_queue failed. Exit Process.");
		return !OK;
	}
    apr_pool_cleanup_register(p, s->process->pool, dav_cleanup_transfer_queue, dav_cleanup_transfer_queue);

    return OK;
}

/* Provider interface */
static const dav_provider dav_repos_provider = {
	&dav_repos_hooks_repos,	/* repos */
	&dav_repos_hooks_propdb,	/* prop */

#ifdef __NOT_USE_LOCKDB__
	NULL,
#else
	&dav_repos_hooks_locks,	/* locks */
#endif

	NULL,			/* versioning */
	NULL,			/* binding */
	&dav_repos_hooks_search	/* search */
};

static void register_hooks(apr_pool_t * p)
{
	/* apache hooks */
	ap_hook_post_config(dav_repos_init_handler, NULL, NULL, APR_HOOK_MIDDLE);
	//ap_hook_child_init(dav_repos_child_init, NULL, NULL, APR_HOOK_MIDDLE);

	/* live property handling */
	dav_hook_find_liveprop(dav_repos_find_liveprop, NULL, NULL, APR_HOOK_MIDDLE);
	dav_hook_insert_all_liveprops(dav_repos_insert_all_liveprops, NULL, NULL, APR_HOOK_MIDDLE);

	/* register the namespace URIs */
	dav_register_liveprop_group(p, &dav_repos_liveprop_group);

	/* register our provider */
	dav_register_provider(p, DBMS_PRIVIDER_NAME, &dav_repos_provider);

	/* authentication / authorication */
#if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER >= 3
#else // 2.2.X
	ap_hook_auth_checker(dav_auth_authorization, NULL, NULL, APR_HOOK_FIRST);
#endif // 2.3 over
	ap_hook_check_user_id(dav_auth_authenticate, NULL, NULL, APR_HOOK_MIDDLE);

#ifndef __OPENDISK_SOLUTION_NOT_USE_TS__
	/* statistics */
	ap_hook_log_transaction(dav_shared_ts_access_statistic, NULL,NULL,APR_HOOK_FIRST);
#endif // !__OPENDISK_SOLUTION_NOT_USE_TS__
	ap_hook_log_transaction(dav_transfer_log_generate, NULL,NULL,APR_HOOK_LAST);

	/* anonymous realm */
	ap_hook_post_read_request(dav_shared_ar_check_request, NULL, NULL, APR_HOOK_FIRST);

	// 2015.7.15 dadamin
	// in,out 트래픽 필터 추가
	ap_register_input_filter("dav_input_statistic", dav_input_filter, NULL, AP_FTYPE_TRANSCODE);
	ap_register_output_filter("dav_bc_output", dav_bc_output_filter, NULL, AP_FTYPE_TRANSCODE);

	//apr_thread_rwlock_create(&db_lock, p);
	// 2012.07.09 dadamin
	// 암호화 관련 library 사용을 위한 lock 사용이지만, 
	// 현재 사용하고 있는 libmcrypt 는 thread safe 하기 때문에 관련 lock 삭제처리	
	//apr_global_mutex_create(&db_global_lock, "/tmp/dav_repos", APR_LOCK_DEFAULT, p);

	// 2012.07.04 dadamin
	// FreeBSD 경우 getmntent 함수 쓰레드 지원하지 않기 때문에
	// 관련 함수 사용 로직에 lock을 사용하여 이를 대처함
#ifdef __FreeBSD__
	apr_global_mutex_create(&local_disk_lock, "/tmp/dav_repos_disk", APR_LOCK_DEFAULT, p);
#endif // __FreeBSD__


}

module DAV_DECLARE_DATA dav_repos_module = {
	STANDARD20_MODULE_STUFF,
	dav_repos_create_dir_config,    /* dir config creater */
	dav_repos_merge_dir_config,	    /* dir merger --- default is to override */
	dav_repos_create_server_config, /* server config */
	dav_repos_merge_server_config,  /* merge server config */
	dav_repos_cmds,		    /* command table */
	register_hooks,		    /* register hooks */
};
