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

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

   $Id: repos.c,v 1.6 2007/03/19 08:43:12 elenoa Exp $

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

 ****************************************************************************/
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <httpd.h>
#include <http_config.h>
#include <http_protocol.h>
#include <http_log.h>
#include <http_request.h>
#define CORE_PRIVATE
#include <http_core.h>		/* for ap_construct_url */

#include <apr.h>
#include <apr_hash.h>
#include <apr_tables.h>
#include <apr_strings.h>
#include <apr_file_io.h>

#include "dav_repos.h"
#include "dbms.h"
#include "util.h"
#include "share_common.h"
#include "share_account.h"
#include "share_statistic.h"
#include "share_fhs_stat.h"
#include "transfer.h"


/*****************************/
/* common functions (static) */

/* to assist in debugging mod_dav's GET handling */
static const dav_hooks_liveprop dav_repos_hooks_liveprop;

/*
** The namespace URIs that we use. This list and the enumeration must
** stay in sync.
*/
static const char *const dav_repos_namespace_uris[] = {
    "DAV:",
    DEFAULT_NAMESPACE_URI,
    NULL			/* sentinel */
};

enum
{
    dav_repos_URI_DAV,		/* the DAV: namespace URI */
    dav_repos_URI_MYPROPS	/* the namespace URI for our custom props */
};

// DAV: namespace
#define DAV_PROPID_hidden DAV_PROPID_END +1

/*
** The single property that we define (in the DAV_FS_URI_MYPROPS namespace)
*/
#define DAV_PROPID_FS_executable        1
#define DAV_PROPID_FS_redhost           2

static const dav_liveprop_spec dav_repos_props[] = {
    {
     dav_repos_URI_DAV,
     "creationdate",
     DAV_PROPID_creationdate,
     0},
    {
     dav_repos_URI_DAV,
     "displayname",
     DAV_PROPID_displayname,
     0},
    {
     dav_repos_URI_DAV,
     "getcontentlanguage",
     DAV_PROPID_getcontentlanguage,
     0},
    {
     dav_repos_URI_DAV,
     "getcontentlength",
     DAV_PROPID_getcontentlength,
     0},
    {
     dav_repos_URI_DAV,
     "getcontenttype",
	 // 2013.08.14 dadamin
	 // 잘못설정된 값 수정 
     DAV_PROPID_getcontenttype,
     0},
    {
     dav_repos_URI_DAV,
     "getetag",
     DAV_PROPID_getetag,
     0},
    {
     dav_repos_URI_DAV,
     "getlastmodified",
     DAV_PROPID_getlastmodified,
     1},
    {
     dav_repos_URI_DAV,
     "resourcetype",
     DAV_PROPID_resourcetype,
     0},
    {
     dav_repos_URI_DAV,
     "source",
     DAV_PROPID_source,
     0				/* redirect host */
     },
	 // 2013.08.16 dadamin
	 // DAV: 네임스페이스 hidden 속성 추가
	 {
	dav_repos_URI_DAV,
	"hidden",
	DAV_PROPID_hidden,
	1				/* handled special in dav_fs_is_writable */
	 },
    {
     dav_repos_URI_MYPROPS,
     "executable",
     DAV_PROPID_FS_executable,
     0				/* handled special in dav_fs_is_writable */
     },

    {0}				/* sentinel */
};


const dav_liveprop_group dav_repos_liveprop_group = {
    dav_repos_props,
    dav_repos_namespace_uris,
    &dav_repos_hooks_liveprop
};


/* Find love prop and return its propid */
int dav_repos_find_liveprop( const dav_resource * resource, const char *ns_uri, const char *name, const dav_hooks_liveprop ** hooks )
{
	/* don't try to find any liveprops if this isn't "our" resource */
	if( resource->hooks != &dav_repos_hooks_repos )
		return 0;

	// 2013.08.14 dadamin
	// mod_dav_fs 와 동일하게 구현함 
	return dav_do_find_liveprop(ns_uri, name, &dav_repos_liveprop_group, hooks);

	/* Not found */
	return 0;
}


/* Inser all live props from live prop hash */
void dav_repos_insert_all_liveprops( request_rec * r, const dav_resource * resource, dav_prop_insert what, apr_text_header * phdr )
{
	apr_ssize_t klen;
	const char *s;
	const char *key, *val;
	apr_hash_index_t *hindex;
	dav_repos_resource *db_r;

	/* don't try to find any liveprops if this isn't "our" resource */
	if (resource->hooks != &dav_repos_hooks_repos)
		return;

	db_r = (dav_repos_resource *) resource->info->db_r;

	if( !resource->exists || db_r == NULL ) 
	{
		/* a lock-null resource */
		/*
		 ** ### technically, we should insert empty properties. dunno offhand
		 ** ### what part of the spec said this, but it was essentially thus:
		 ** ### "the properties should be defined, but may have no value".
		 */
		apr_text_append(r->pool, phdr, "<D:resourcetype/>");
		return;
	}

	/* Read live props from hash */
	for( hindex = apr_hash_first(r->pool, db_r->lpr_hash); hindex; hindex = apr_hash_next(hindex) ) 
	{
		apr_hash_this(hindex, (void *) &key, &klen, (void *) &val);
		s = apr_psprintf(r->pool, "<D:%s>%s</D:%s>" DEBUG_CR, key, val, key);
		apr_text_append(r->pool, phdr, s);
	}
}

/*
** get resource and return to result_resource
*/
// 2012.04.17 : dadamin, 리턴 코드 별 redirect head code setting
static void dav_repos_set_redirect_head(request_rec *r, dav_repos_resource *db_r, int respcode, const char * redirect_domain)
{
	if( redirect_domain )
	{
		char* sz_uri_tmp = NULL;
		char* szuri = NULL;
		char szsrc[4096] = { 0 };

		// 2013.01.11 dadamin
		// 무인증 요청에 대한 301 경우 /dav/서비스이름/파일URI로 리턴 해줘야함
		const char* redhost = NULL;
		redhost = apr_table_get(r->headers_in, "RedHost");
		
		// 2016.01.18 dadamin
		// SQL escape string 제거
		dav_repos_restore_unsafe_chars(db_r->m_uri, szsrc);

		if(redhost)
		{
			char *sztmp = apr_pstrcat(r->pool, "/dav/", redhost, szsrc + 4, NULL);
			szuri = ap_escape_uri(r->pool, sztmp);			
		}
		else
		{
			// 2012.07.06 : 사용하고 있는 m_uri 값은 unescape 한 값이므로 이를 리턴 시
			// 오동작하는 클라이언트 존재(NetCache - 공백오류) 하기 때문에 해당 값을 escape한 값으로 변경하여 리턴
			szuri = ap_escape_uri(r->pool, szsrc);
		}
		sz_uri_tmp = apr_pstrcat(r->pool, dav_http_scheme(r), "://", redirect_domain, szuri, NULL);
		
		if (r->parsed_uri.query)
		{
			sz_uri_tmp = apr_pstrcat(r->pool, sz_uri_tmp, "?", r->parsed_uri.query, NULL);
		}

		switch(respcode)
		{
		case HTTP_MOVED_PERMANENTLY:
			apr_table_setn(r->headers_out, "Location", sz_uri_tmp);
			break;
		case HTTP_NOT_FOUND:
			apr_table_setn(r->err_headers_out, "Location", sz_uri_tmp);
			apr_table_setn(r->err_headers_out, "Redirect", sz_uri_tmp);
			break;
		default:
			apr_table_setn(r->headers_out, "Location", sz_uri_tmp);
			break;
		}
	}
}

// 2012.04.16 : dadamin, get_resource함수에서 PUT 관련 처리
static dav_error *dav_repos_get_resource_put(int dbresult, request_rec * r, dav_repos_resource *db_r, dav_resource_private *ctx, bool is_uricaseignore)
{
	int res = 0;
	dav_repos_server_conf *dsc = dav_repos_get_server_conf(r->server);

	// 세션에 대한 input 트래픽 통계 필터 활성화
	ap_add_input_filter("dav_input_statistic", NULL, r, r->connection);

	// file exists
	if( dbresult > 0 )
	{
		const char *p_length        = apr_table_get(r->headers_in,    "Content-Length");
		const char *p_modify_time   = apr_table_get(r->headers_in,    "ModifyTime");

		// PUT 요청 중 SBFS, NetCache 지원을 위한 PUT 0 요청인지 검사
		if(p_length && p_modify_time)
		{
			long long ll_length = atoll(p_length);
			if(ll_length == 0)  // PUT 요청이지만.. Content length 값이 0 인 PUT 0 요청이고 기존 Content 가 존재.. file_lastmodified 값에 대한 업데이트 요청인 경우
			{
				// CHG 2010-01-18 huibong
				// ModifyTime info save function changed ( Do not use dbms_update_getlastmodified )

				// CHG 2012-03-23 huibong
				// 서비스별 URI 대소문자 구분/무시 기능 추가 관련 인자값 추가.
				int ret = dbms_update_file_lastmodified(db_r, atoll(p_modify_time), is_uricaseignore );

				if(ret == -1)
				{
					// Meta 정보 업데이트 실패시
					ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: PUT 0 [%s] ModifyTime[%s] update fail" , db_r->m_fake_uri, p_modify_time);
					return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "dav_repos_get_resource(dav_repos_get_resource_put ModifyTime fail)");
				}
				else
				{
					// 정상 처리시 HTTP 204 NO_CONTENT 값 반환 처리 
					return solbox_dav_new_error(r->pool, HTTP_NO_CONTENT, 0, "dav_repos_get_resource(dav_repos_get_resource_put ModifyTime success)");
				}
			}
		}

		// 기존 정보가 존재하지만 PUT 0 처리 요청이 아닌 경우
		if( strcmp(db_r->m_host_name, dsc->host_name) && !r->header_only) 
		{
			// 기존 존재하는 Content 에 대한 PUT 요청이지만... 해당 Content 를 저장한 원본 장비가 아닌 경우....
			// 해당 Content 를 가진 원본 장비로 redirect 처리.
			//dav_repos_set_redirect_head(r, db_r, HTTP_MOVED_PERMANENTLY, db_r->m_host_name);
			//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: request[PUT] redirect info set [%s]. local[%s]", db_r->m_host_name, dsc->host_name);
			//return solbox_dav_new_error(r->pool, HTTP_MOVED_PERMANENTLY, 0, "Moved Permanently");

			// 2018-02-27 CHG huibong
			// - NetCache 업로드 서버 관련 PUT 요청에 대해 
			// - 원본 서버가 죽은 경우 HTTP 301 을 주면 죽은 장비로 접속을 시도하면서 문제가 RC offline 문제가 발생해서
			// - 다른 업로드 역시 문제가 생김
			// - 이를 해결하기 위해서... NetCache 업로드 서버의 PUT 요청에서 원본 파일을 가진 FHS 가 죽은 경우
			// - 무조건 HTTP 301 이 아닌 HTTP 410 응답을 주도록 한다.
			// - 본 수정 사항에 의한 효과는 장애 탐지 약 10 sec 정도 걸리므로 거의 효과는 없는 것으로 판단됨.
			const char * userAgent = NULL;
			userAgent = apr_table_get( r->headers_in, "User-Agent" );

			// NetCache 업로드 서버인 경우
			if( userAgent != NULL && ( strncmp( userAgent, "NetCache", 8 ) == 0 ) )
			{
				// 원본 서버가 살아 있는지 검사한다.
				if( dav_shared_du_is_alive( db_r->m_host_name ) != 1 )
				{
					// 죽은 경우
					ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: request[PUT] source[%s] dead. return HTTP_GONE.", db_r->m_host_name );
					return solbox_dav_new_error( r->pool, HTTP_GONE, 0, "Source server dead" );
				}
			}

			// 그 외 상황인 경우.. 죽든 살았던 원본 FHS 로 Redirection 처리
			dav_repos_set_redirect_head( r, db_r, HTTP_MOVED_PERMANENTLY, db_r->m_host_name );
			ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: request[PUT] redirect info set [%s]. local[%s]", db_r->m_host_name, dsc->host_name );
			return solbox_dav_new_error( r->pool, HTTP_MOVED_PERMANENTLY, 0, "Moved Permanently" );
		}
	}
	else 
	{
		char *tmpPathName = apr_pcalloc(r->pool, MaxSizeOfPathLength);
		char *tmpSubPath = apr_pcalloc(r->pool, MaxSizeOfPathLength);
		char *tmpFileName = apr_pcalloc(r->pool, MaxSizeOfFileNameLength);
		char *pPlainPathName = NULL;
		char *pFileToBeHashed = NULL;
		char *pHashedPathName = NULL;

		int bPut0Mode			= 0;
		const char *p_mode		= NULL;
		const char *p_slength	= NULL;
		const char *force		= NULL;	
		long long slength		= -1;

		char *domain = NULL;

		/* Get plain filepath */
		/* Get pathname and filename from pPlainPathName */

		res = get_filename_in_path( db_r->m_fake_uri, tmpPathName, tmpFileName );
		if(res == -1) 
		{
			return solbox_dav_new_error(r->pool, HTTP_BAD_REQUEST, 0,"Bad request(dav_repos_get_resource_put,get_filename_in_path)");
		} 
		else if( res == -2 ) 
		{
			return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,"Internal server error(dav_repos_get_resource_put, get_filename_in_path)");
		}

		tmpSubPath[0] = '\0';
#ifdef __USE_MULTIPLE_STORAGE__
		if( get_writable_path(r->pool, dsc->storage_dir, tmpSubPath) == -1 )
		{
			// 2014.02.28 dadamin
			// 쓰기할 디스크 선택 실패 시 에러 리턴 : 507
			return solbox_dav_new_error(r->pool, HTTP_INSUFFICIENT_STORAGE, 0,"There is logically not enough storage to write to this resource.(get_writable_path error)");
		}
#endif
		pPlainPathName = apr_psprintf(r->pool, "%s/%" APR_INT64_T_FMT "/%s",
#ifdef __USE_MULTIPLE_STORAGE__
			(tmpSubPath[0] ? tmpSubPath :
#endif
			dsc->storage_dir
#ifdef __USE_MULTIPLE_STORAGE__
			)
#endif
			, ctx->user_id
			, tmpFileName);   

		// 2014.06.19 dadamin
		// 동시간대 동일한 uri에 대한 hash 충돌 최소화를 위해서 microseconds 단위 값으로 time 구함
		/* Get filename hashed */
		pFileToBeHashed  = apr_psprintf(r->pool, "%" APR_INT64_T_FMT "%s%s%s%" APR_TIME_T_FMT"", ctx->user_id
			, db_r->m_fake_uri
			, tmpFileName
			, MD5Private
			, apr_time_now());

		// CHG 2010-08-10 huibong
		// get_hashed_filepath 함수 정의 변경에 따른 코드 수정
		char szHashedFileName[APR_MD5_DIGESTSIZE * 2 + 1];	
		if( get_hashed_filepath( pFileToBeHashed, szHashedFileName, sizeof(szHashedFileName) ) == false )
		{		
			// Write error log
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource:get_hashed_filepath return false.[%s][%s][%zu]", pFileToBeHashed, szHashedFileName, sizeof(szHashedFileName));

			// Error Return.
			return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,"Internal server error(get_hashed_filepath)");
		}

		pHashedPathName = apr_psprintf(r->pool, "%s/%" APR_INT64_T_FMT "/%s",
#ifdef __USE_MULTIPLE_STORAGE__
			(tmpSubPath[0] ? tmpSubPath : dsc->storage_dir)
#else
			dsc->storage_dir
#endif
			, ctx->user_id, szHashedFileName );

		db_r->m_filename_org = pPlainPathName;
		db_r->m_filename_hash = pHashedPathName;

		ctx->pathname = pHashedPathName;
		// NEW 2011-11-29 huibong
		// request_rec 구조체 canonical_filename 필드상에 실제 Local 물리 파일 정보가 저장되도록 기능 추가
		// 이는 Transfer Log 정보상에 물리 파일 정보가 저장되도록 처리하기 위함임.
		// 본 코드는 Content 업로드 처리시 생성되는 물리 파일명을 설정하기 위한 코드임.
		r->canonical_filename = pHashedPathName;

		// PUT 0 Force Mode Check
		// PUT 0 Force mode 
		//  => Redirect 처리를 수행하지 않고 201을 반환 (Location 정보 처리)
		// PUT 0 mode 
		//  => Redirect 여부 검사, Redirect 대상이 자기자신인 경우 201 반환 (Location 정보처리)

		p_mode		= apr_table_get(r->headers_in,	"Mode");
		p_slength	= apr_table_get(r->headers_in,	"Content-Length");
		force		= apr_table_get(r->headers_in, "Force");

		if(p_slength)
			slength	= atoll(p_slength);

		// M_PUT method, Length=0 => PUT 0 MODE
		if( (slength == 0) && p_mode!=NULL && (strncmp(p_mode, "Create", 6) == 0))
			bPut0Mode = 1;


		// PUT 0 Force Mode
		if( bPut0Mode == 1 )
		{
			if(force)
			{
				// PUT 0 Location Setting function call => 201 Code & Location info return..
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: PUT0 force mode. return 201 & location[%s]", dsc->host_name);
			}
			else 
			{
				// Rediection 처리할 domain 정보 검색
				// 오류발생시 NULL 을 반환함.
				domain = dav_shared_du_put_target(r->pool, dsc->host_name);
				// 현재 장비의 host_name 이 존재하고
				// dav_shared_du_put_target() 함수의 반환값이 NULL 이 아니며
				// host_name 과 dav_shared_du_put_target() 함수의 반환값이 다른 경우 => 서로 다른 장비
				// => 기본 Redirection 처리
				if(dsc->host_name && domain && strncmp(dsc->host_name, domain, strlen(dsc->host_name)))
				{
					// Send redirect information & error code 301					
					dav_repos_set_redirect_head(r,db_r, HTTP_MOVED_PERMANENTLY, domain);
					ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: PUT0 redirect 301. target[%s]", domain);

					return solbox_dav_new_error(r->pool, HTTP_MOVED_PERMANENTLY, 0, "Moved Permanently");
				}
			}
		}
	}

	return NULL;
}

// 2012.04.16 : dadamin, get_resource 함수에서 GET 관련 처리
static dav_error *dav_repos_get_resource_get(int dbresult, request_rec * r, dav_repos_resource *db_r, dav_resource_private *ctx, bool is_uricaseignore)
{	
	const char * reqhost = NULL;
	dav_repos_server_conf *dsc = dav_repos_get_server_conf(r->server);
	int redirectmode  = 0;

	// file exists
	if( dbresult > 0)
	{
		reqhost = apr_table_get(r->headers_in, "Host");

		if(reqhost  && strcmp(reqhost, dsc->host_name) )
		{
			// 2012.04.26 dadamin : 무인증 요청 대한 판단
			// 무인증이면 램덤 선택하지 않고 기존과 동일하게 자신 가진 파일은 서비스함
			const char* redhost = NULL;
			redhost = apr_table_get(r->headers_in, "RedHost");
			if ( redhost )
			{
#ifdef __CLOUD_STREAMING__
				// 2013.08.06 : Head 요청인 경우 램덤처리
				//              StarPlayer 경우 RedHost 값을 전달 해주기 때문에
				//				램덤 처리 지원을 위해서 해당 요청이 HEAD 요청인 경우
				//              램덤 처리 한다.
				if(r->header_only)
					redirectmode = 1;
				else
#endif // __CLOUD_STREAMING__
					redirectmode = 3;
			}
			else
			{
				// 2012.04.19 dadamin : 해당 장비와 request header의 host 비교 다르면 램덤 처리
				redirectmode = 1;
			}
		}
		else if( strcmp(db_r->m_host_name, dsc->host_name) && !r->header_only )
		{
			// 기존 존재하는 Content 에 대한 GET 요청이지만.. 해당 Content 를 저장한 원본 장비가 아닌 경우....
			// replication, cache 된 Content 가 존재하는지 검사하여.. 해당 장비를 정보를 확인한다.			
			redirectmode = 2;
		}
		else 
		{
			redirectmode = 0;
		}

		// 2012.04.19 dadamin 
		// redirect mode ; 1 - 램덤, 2 - 해당 장비 검사 & 램덤, 3 - 무인증 요청, 해당 장비 검사 & 램덤, 0 - 해당 장비임		
		if(redirectmode > 0)
		{
			const char * redirect_domain = NULL;
			int error_reponse_code = 0;

			redirect_domain = dbms_get_redirect(db_r, is_uricaseignore, (redirectmode == 1), &error_reponse_code );
			if( redirect_domain == NULL )
			{
				// redirect 정보 조회 함수상에서 오류가 발생한 경우 : 전달받은 HTTP error code 반환
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: dbms_get_redirect func failed [mode:%d]. return reponse code [%d]", redirectmode, error_reponse_code );
				return solbox_dav_new_error(r->pool, error_reponse_code, 0, "Redirection target select fail");
			}

			if( strcmp( redirect_domain, dsc->host_name ) ) 
			{
				dav_repos_set_redirect_head(r,db_r, HTTP_MOVED_PERMANENTLY, redirect_domain);

				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: request[GET] redirect[mode:%d] info set [%s]. local[%s]", redirectmode, redirect_domain, dsc->host_name);
				return solbox_dav_new_error(r->pool, HTTP_MOVED_PERMANENTLY, 0, "Moved Permanently");
			}

		}

		// 2012.06.13 dadamin
		// 장비에 저장 위치가 다를 수 있기 때문에 m_filename_hash를 다시 설정
		r->canonical_filename = (char *)db_r->m_filename_hash;		
	}
	
	 return NULL;
}

// 2012.04.16 : dadamin, get_resource 함수에서 PROPFIND 관련 처리
static dav_error *dav_repos_get_resource_propfind(int dbresult, request_rec * r, dav_repos_resource *db_r, dav_resource_private *ctx, bool is_uricaseignore)
{
	dav_repos_server_conf *dsc = dav_repos_get_server_conf(r->server);

	if( dbresult == 0 )
	{
		char *domain = NULL;
		domain = dav_shared_du_put_target(r->pool, dsc->host_name);

		if (domain && strcmp(domain, dsc->host_name)) 
		{
			dav_repos_set_redirect_head(r,db_r, HTTP_NOT_FOUND, domain);
#ifdef __OPENDAV_DEBUG__
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: PROPFIND redirect target[%s]", domain);
#endif // __OPENDAV_DEBUG__
		}
		else 
		{
			// 2012.02.01 : Netcache 경우 Header에 Location 정보 설정되게 한다.
			// NetCache Agent 인지를 확인
			const char * userAgent = NULL;
			userAgent = apr_table_get( r->headers_in, "User-Agent");

			if( userAgent != NULL && (strncmp(userAgent, "NetCache", 8)==0) )
			{			
				dav_repos_set_redirect_head(r, db_r, HTTP_NOT_FOUND, dsc->host_name);
#ifdef __OPENDAV_DEBUG__
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: PROPFIND NetCache redirect target myself[%s]", dsc->host_name);
#endif // __OPENDAV_DEBUG__
			}
		}
	}
	else 
	{
		// NEW 2011-03-23 dadamin
		// 각 서비스 디렉토리 용량 정보 조회 기능을 추가한다.
		// Request 가 PROPFIND & HTTP Header 상에 Service-Quota: YES 가 설정되어 있는 경우
		// tran id 정보를 이용하여 서비스 용량( Total, 사용가능 용량) 정보를 RCDB 상에서 조회한다.
		// 조회된 결과는 HTTP Header 상에 Service-Quota: Total Byte - Used Byte 형식으로 조립되어 전달된다. 
		// 그리고 조회결과가 유효하지 않는 경우에는 에러를(HTTP_UNPROCESSABLE_ENTITY(422)) 리턴한다.
		const char * ServiceQuota = apr_table_get(r->headers_in, "Service-Quota");

		if(ServiceQuota != NULL && (strncmp(ServiceQuota, "YES", 3)==0))
		{
			// 값을 전달받기 위한 변수 초기화.
			apr_int64_t service_total_byte = 0;
			apr_int64_t used_byte = 0;
			char *ptran_id = NULL;
			ptran_id = apr_psprintf(r->pool, "%" APR_INT64_T_FMT "", ctx->user_id);
			if( dbms_get_service_quotaex( db_r, ptran_id, is_uricaseignore, &service_total_byte, &used_byte ) == 1)
			{
				// 각 변수값이 유효한지 여부 검사
				if( service_total_byte < 0 )     
					service_total_byte = 0;

				if( used_byte < 0 ) 
					used_byte = 0;

				// HTTP Header 정보 설정 : 정상인 경우에만 전달되도록 설정
				apr_table_setn( r->headers_out, "Service-Quota", apr_psprintf(db_r->p, "%" APR_INT64_T_FMT "-%" APR_INT64_T_FMT"", service_total_byte, used_byte));

				// 로깅 처리
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: Service-Quota result tranID[%s] uri[%s] total[%" APR_INT64_T_FMT "] used[%" APR_INT64_T_FMT "]", ptran_id, db_r->m_fake_uri, service_total_byte, used_byte );
			}
			else
			{
				// 서비스 용량 정보 조회 실패시 422 에러를 리턴한다.
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: Service-Quota fail. tranID[%s] uri[%s]" , ptran_id, db_r->m_fake_uri );

				// 없는 경우 에러를 리턴한다.            
				return solbox_dav_new_error(r->pool, HTTP_UNPROCESSABLE_ENTITY, 0,"Bad Request status(Service-Quota).");
			}
		}
				
		// 2012.04.26 dadamin : 응답 header에 해당 서비스에 대한 대소문자 구분 여부 코드 추가
		// X-SB-Case-Sensitive : yes (구분), no (무시)
		apr_table_setn( r->headers_out, "X-SB-Case-Sensitive", apr_psprintf(db_r->p, "%s", (is_uricaseignore ? "no" : "yes" )));

		// 2013.08.23 dadamin
		// X-SB-Show-hidden:no 이거나 없으면 파일(디렉토리) 경우 404 리턴함
		const char * showhidden = apr_table_get(r->headers_in, "X-SB-Show-hidden");

		if(showhidden == NULL || (strncmp(showhidden, "no", 2)==0))
		{
			if(db_r->m_source )
			{
				return solbox_dav_new_error(r->pool, HTTP_NOT_FOUND, 0,"The file(directory) is hidden.");
			}
		}

		// 2013.08.23 dadamin
		// namespace 정보 초기값(DAV:) 강제 지정한다.
		// 이는 하위 정보에 대해서 네임스페이스 해쉬테이블 중복 생성을 막기 위함
		if( db_r->ns_hash  == NULL)
		{
			dav_repos_property pr;
			pr.m_ns_id = 0;
			dbms_set_namespace(db_r, &pr, "DAV:");
			dbms_set_ns_id(db_r, "DAV:", &pr);
		}
	}

	return NULL;
}

// 2012.04.16 : dadamin, 기존 dav_repos_get_resource에 대한 정리
// 각 요청에 대한 최초 진입 함수..
static dav_error *dav_repos_get_resource( request_rec * r, const char *root_path, const char *label, int use_checked_in, dav_resource ** result_resource )
{
	int res = 0;
	dav_resource_private *ctx;
	dav_resource *resource;
	dav_repos_server_conf *dsc = NULL;
	char *pBaseURI = NULL;
	char *pOrgURI = NULL;
	char *new_uri = NULL;
	dav_repos_resource *db_r = NULL;
	acct_info user_login_info;
	dav_error *err = NULL;

	char *tmpstr = apr_pcalloc(r->pool, MaxSizeOfPathLength);
	char *tmpstr2 = apr_pcalloc(r->pool, MaxSizeOfPathLength);

	// NEW 2012-03-20 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
	bool is_uricaseignore = false;  
	
	dsc = dav_repos_get_server_conf(r->server);
	
	// 모든 메소드에서 대해서 자신의 상태을 체크 실패 시 500 리턴 하던 구조에서
	// User-Agent:NetCache  경우에 대해서 GET/ 단일 파일에 대한 PROPFIND 에 대해서
	// alive 상태된 장비가 없을 시 500 에러를 리턴되게 수정함
	// Check the status of self.
	//if( dav_shared_du_is_alive(dsc->host_name) ==  0 )
	//{
	//	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Check of alive failed. Send HTTP_INTERNAL_SERVER_ERROR. [%s]" , r->uri);
	//	return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,"Internal server error(My self isn't alive.)");
	//}

	// Delete the request URI handling multi-slash
	memset(tmpstr, 0, MaxSizeOfPathLength);
	strcpy(tmpstr, r->the_request);
	dav_repos_replace_multi_slash_chars(tmpstr, r->the_request);
	memset(tmpstr, 0, MaxSizeOfPathLength);
	strcpy(tmpstr, r->uri);
	dav_repos_replace_multi_slash_chars(tmpstr, r->uri);


	/* Create private resource context descriptor */
	ctx = apr_pcalloc(r->pool, sizeof(*ctx));
	ctx->finfo = r->finfo;

	db_r = apr_pcalloc(r->pool, sizeof(*db_r));
	db_r->p = r->pool;
	db_r->r = r;

	ctx->db_r = db_r;
	ctx->finfo = r->finfo;
	ctx->pool = r->pool;
	ctx->rec = r;

	/* webdrive sends "OPTION" to know which properties are supported by 
	 * server, it happens when "connect" button clicked on the login 
	 * configuration screen. */
	if( r->user == NULL ) 
	{
		resource = apr_pcalloc(r->pool, sizeof(*resource));
		resource->type = DAV_RESOURCE_TYPE_REGULAR;
		resource->info = ctx;
		resource->hooks = &dav_repos_hooks_repos;
		resource->pool = r->pool;
		resource->exists = 0;
		resource->uri = apr_psprintf(r->pool, "%s", "");
		*result_resource = resource;
		return NULL;
	}


	/* login info  */
	if( at_split(r->user, &user_login_info) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Login info parser (at_split).");
		return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,"Internal server error(at_split)");
	}

	if (atoi(user_login_info.szVolumeID) <= 0 || atoi(user_login_info.szUserID) <= 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Login info empty.");
		return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,"Internal server error(login info)");
	}

	ctx->user_id = atoll(user_login_info.szVolumeID);

	// NEW 2012-03-20 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
	res = dav_shared_is_uri_ignore_case_by_tranid( user_login_info.szVolumeID );
	if( res == 1 ) // URI 대소문자 무시 서비스인 경우
	{
		is_uricaseignore = true;
	}
	else // 양방향 기본 ( URI 대소문자 구분 )
	{
		// 만약 dav_shared_is_uri_ignore_case_by_tranid 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
		if( res == -1 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: dav_shared_is_uri_ignore_case_by_tranid func fail. tranID[%s]" , user_login_info.szVolumeID );
		}

		is_uricaseignore = false;
	}

	/*
	 ** If there is anything in the path_info, then this indicates that the
	 ** entire path was not used to specify the file/dir. We want to append
	 ** it onto the filename so that we get a "valid" pathname for null
	 ** resources.
	 */
	pBaseURI = apr_psprintf(r->pool, "%s", dsc->base_uri);
	dav_repos_no_trail(pBaseURI);

	/* get rid of base uri */
	pOrgURI = apr_pstrdup(r->pool, r->uri);
	dav_repos_no_trail(pOrgURI);

	memset(tmpstr, 0, MaxSizeOfPathLength);
	memset(tmpstr2, 0, MaxSizeOfPathLength);
	dav_repos_remove_base_uri(pBaseURI, r->uri, tmpstr);
	/* get rid of unsafe chars, which is like " ' ", " \ " */
	dav_repos_replace_unsafe_chars(tmpstr, tmpstr2);
	r->uri = apr_pstrdup(r->pool, tmpstr2);
	dav_repos_no_trail(r->uri);

	/* make sure the URI does not have a trailing "/" */
	new_uri = apr_psprintf(r->pool, "/%" APR_INT64_T_FMT "%s", ctx->user_id, r->uri);
	dav_repos_no_trail(new_uri);
	db_r->m_fake_uri = apr_pstrdup(r->pool, new_uri);
	
	memset(tmpstr, 0, MaxSizeOfPathLength);
	dav_repos_replace_unsafe_chars(pOrgURI, tmpstr);
	db_r->m_uri = apr_pstrdup(r->pool, tmpstr);

	// 2014-04-24 dadamin
	// depth - uri 기준의 인덱스 수행하기 위해서 현재 path의 depth를 구한다.
	/* Calculate depth */
	db_r->m_depth = ap_count_dirs(db_r->m_fake_uri) - 1; 
	
	/* Create resource descriptor */
	resource = apr_pcalloc(r->pool, sizeof(*resource));
	resource->type = DAV_RESOURCE_TYPE_REGULAR;
	resource->info = ctx;
	resource->hooks = &dav_repos_hooks_repos;
	resource->pool = r->pool;
	db_r->r = r;

	// 해당 URI 가 이미 존재하는지 확인
	res = dbms_get_property(db_r, is_uricaseignore );
	if ( res < 0)
	{
		// 2012-04-04 : dadamin
		// database에 대한 오류 처리하며,에러코드는 503 에러를 리턴함
		if(res == -2)
			return solbox_dav_new_error(r->pool, HTTP_SERVICE_UNAVAILABLE, 0, "dav_repos_get_resource : Database error.");
		else if (res == -5)
		{
			// 2012.06.13 : dadamin 
			// NetCahce 경우 alive 된 FHS 없을 경우 에러를 리턴함
			//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "NetCache PROPFIND random Error. return HTTP_INTERNAL_SERVER_ERROR. [%s]" , r->uri);
			//return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,"Internal server error(Empty Alive FHS)");
		
			// 2018-02-12 CHG huibong
			// - alive FHS 가 없는 경우 NetCache Client 한데.. HTTP 500 응답을 주면 RC 을 OFFLINE 처리하므로... 다른 content 관련 장애 발생
			// - 따라서 FHS 가 일시적으로 죽은 경우 HTTP 500 이 아닌 410 응답을 주도록 한다.
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "NetCache PROPFIND random Error. return HTTP_GONE. [%s]" , r->uri);
			return solbox_dav_new_error(r->pool, HTTP_GONE, 0, "Alive server not found" );
		}
		else 
			return solbox_dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, "dav_repos_get_resource : SQL error.");
	}

	// uri exists.
	if( res > 0 )
	{
		// 2012.04.16 : dadamin
		// 원본이 2개 이상인 경우 에러 처리하지 않으며, 단순히 error log에 로깅한다.
		if ( res > 1 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: There are two or more sources. uri [%s]" , db_r->m_fake_uri);
		}

		resource->exists = 1;
		ctx->pathname = db_r->m_filename_hash;
		// NEW 2011-11-29 huibong
		// request_rec 구조체 canonical_filename 필드상에 실제 Local 물리 파일 정보가 저장되도록 기능 추가
		// 이는 Transfer Log 정보상에 물리 파일 정보가 저장되도록 처리하기 위함임.
		// 본 코드는 기존 Content 가 존재하는 경우 RCDB 에서 조회된 물리 파일명을 저장하는 코드임.
		r->canonical_filename = (char *)db_r->m_filename_hash;

		/* Is collection ? */
		if( db_r->m_resource_type == dav_repos_COLLECTION ) 
		{
			resource->collection = APR_DIR;
		}
		
		ctx->finfo.fname = r->finfo.fname = db_r->m_filename_org;
		ctx->finfo.size  = r->finfo.size  = db_r->m_get_content_length;
	}
	else 
	{
		resource->exists = 0;

		/* FIXME: Hum. Deal locknull */
		r->path_info = "";
	}
		
	switch (r->method_number)
	{
	case M_PUT:
		{
				err = dav_repos_get_resource_put(res, r, db_r, ctx, is_uricaseignore);
		}
		break;
	case M_GET:
		{
			// 2012.07.26 dadamin
			// HTTP HEAD 요청에 대해서 apache 는 GET, header only로 설정된다.
			// 파일 경우 : 해당 로직 수행
			// 디렉토리 경우 : 해당 로직 수행하지 않음
			if(r->header_only && db_r->m_resource_type != 0)
			{
				// nothing and 200 return.
			}
			else if( db_r->m_resource_type != 0 )
			{
				// NEW 2019-11-21 huibong 폴더에 대한 GET 응답 관련 기능 개선 (#32860)
				//                - 폴더에 대한 GET 요청시 불필요한 HTTP 301 발생 후 HTTP 409 응답 발생
				//                - 최상위 폴더에 대한 GET 요청시 무한 HTTP 301 발생
				//                - 이를 해결하기 위해 폴더에 대한 GET 요청은 HTTP 400 Bad Request 로 응답하도록 수정
				ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "direcotory GET request. return HTTP_BAD_REQUEST. [%s]", r->uri );
				return solbox_dav_new_error( r->pool, HTTP_BAD_REQUEST, 0, "dav_repos_get_resource: directory GET request is bad request." );
			}
			else 
			{
				err = dav_repos_get_resource_get(res, r, db_r, ctx, is_uricaseignore);
			}
		}
		break;
	case M_PROPFIND:
		{
			err = dav_repos_get_resource_propfind(res, r, db_r, ctx, is_uricaseignore);
		}
		break;
	default:
		{
			err = NULL;
			if( res == 0)
			{
				// 2012.04.16 : dadamin 
				// move, copy 시 dest uri에 대하여 상위 로직에서 해당 값을 참조 시
				// Segmentation fault (11) 오류 방지하기 위해서 임시 파일명을 부여한다.
				ctx->pathname = apr_psprintf(r->pool,"/tmp/path%s",r->uri);
			}
		}
		break;
	}

	if(err != NULL)
		return err;

	//execute_remain:
	/* make sure the URI does not have a trailing "/" */
	char *s = apr_pstrdup(r->pool, db_r->m_uri);
	dav_repos_no_trail(s);
	resource->uri = s;

	/* Set return value */
	*result_resource = resource;  /* <- "/repos" */
	return NULL;
}


/* Should we return NULL, if no parent ? */

// 2012-03-21 huibong 함수 내역 추가
// 본 함수는 주어진 URI 에 대한 상위 경로 정보가 존재하는지 확인하기 위한 함수이다.
// 본 함수의 반환값은 현재 무조건 NULL 이며....
// result_parent 값에 상위 정보를 입력하여 반환처리한다...
static dav_error *dav_repos_get_parent_resource(const dav_resource * resource, dav_resource ** result_parent)
{
	char *szRealURI = apr_pcalloc(resource->pool, MaxSizeOfPathLength);
	dav_resource_private *ctx = resource->info;
	dav_resource_private *parent_ctx;
	dav_resource *parent_resource;
	dav_repos_resource *db_r;
	char *dirpath;
	char *parenturi = NULL;

	// NEW 2012-03-20 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
	int result = 0;
	bool is_uricaseignore = false;

	/* If given resource is root, then there is no parent */
	if( strcmp(resource->uri, "/") == 0 || strcmp(ctx->pathname, "/") == 0 ) 
	{
		*result_parent = NULL;
		return NULL;
	}

	if( resource->uri != NULL ) 
	{
		parenturi = ap_make_dirstr_parent(resource->pool, resource->info->db_r->m_fake_uri);
		dav_repos_no_trail(parenturi);	
	}	

	/* fill DBR to check collection type */
	db_r = apr_pcalloc(resource->pool, sizeof(*db_r));	
	db_r->r = resource->info->db_r->r;

	db_r->p = resource->pool;
	remove_user_root_dir_from_fake_uri(parenturi, szRealURI);
	db_r->m_uri = apr_pstrdup(resource->pool, szRealURI);
	db_r->m_fake_uri = apr_pstrdup(resource->pool, parenturi);

	// 2014-04-24 dadamin
	// depth - uri 기준의 인덱스 수행하기 위해서 현재 path의 depth를 구한다.
	/* Calculate depth */
	db_r->m_depth = ap_count_dirs(db_r->m_fake_uri) - 1; 

	// NEW 2012-03-20 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
	result = dav_shared_is_uri_ignore_case( resource );
	if( result == 1 ) // URI 대소문자 무시 서비스인 경우
	{
		is_uricaseignore = true;
	}
	else
	{
		// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
		if( result == -1 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_parent_resource: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , resource->info->user_id );
		}

		is_uricaseignore = false;
	}

	/* exist check */
	if( dbms_get_property( db_r, is_uricaseignore ) == 1 ) 
	{
		/* ### optimize this into a single allocation! */
		/* Create private resource context descriptor */
		parent_ctx = apr_pcalloc(resource->pool, sizeof(*parent_ctx));

		/* ### this should go away */
		parent_ctx->pool = resource->pool;

		//dirpath = ap_make_dirstr_parent(ctx->pool, ctx->pathname);
		dirpath = ap_make_dirstr_parent(resource->pool, ctx->pathname);
		dav_repos_no_trail(dirpath);
		parent_ctx->pathname = dirpath;

		/* Set parent resource */
		parent_resource = apr_pcalloc(resource->pool, sizeof(*parent_resource));

		parent_resource->info = parent_ctx;
		parent_resource->hooks = &dav_repos_hooks_repos;
		parent_resource->pool = resource->pool;

		if( resource->uri != NULL ) 
		{
			char *uri = ap_make_dirstr_parent(resource->pool, resource->info->db_r->m_fake_uri);
			dav_repos_no_trail(uri);
			parent_resource->uri = uri;
		}

		parent_ctx->db_r = db_r;
		parent_resource->exists = 1;

		/* Is collection ? */
		if (db_r->m_resource_type == dav_repos_COLLECTION) 
		{
			parent_resource->collection = APR_DIR;
		}
	} 
	else 
	{
		*result_parent = NULL;
		//parent_resource->exists = 0;
		return solbox_dav_new_error(resource->pool, HTTP_CONFLICT, 0, "dav_repos_get_parent_resource: No parent collection.");
	}

	*result_parent = parent_resource;
	return NULL;
}


// CHG 2012-03-26 huibong
// 서비스별 URI 대소문자 구분, 무시 여부 기능 추가.
// 본 함수는 MOVE, COPY 등의 작업시 상위 모듈인 mod_dav.c 의 dav_method_copymove() 함수에서
// 동일 URI 에 대한 요청인지 판단하기 위해 호출하기 위한 함수임.
// 두개의 URI 가 동일할 경우 상위 모듈에서 HTTP_FORBIDDEN 403 을 반환처리함.
static int dav_repos_is_same_resource(const dav_resource * res1, const dav_resource * res2)
{
	// NEW 2012-03-26 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
	int result = 0;

	if( res1->hooks != res2->hooks )
		return 0;
	
	// NEW 2012-03-26 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
	result = dav_shared_is_uri_ignore_case( res1 );
	if( result == 1 ) // URI 대소문자 무시 서비스인 경우
	{
		if( strcasecmp(res1->uri, res2->uri) == 0 )
		{
			// URI 대소문자를 구분하지 않는 서비스에서... 입력받은 2개의 URI 가 동일한 경우
			return 1;
		}
		else
		{
			// URI 대소문자를 구분하지 않는 서비스에서... 입력받은 2개의 URI 가 동일하지 경우
			return 0;
		}
	}
	else // URI 대소문자를 구분하는 서비스인 경우
	{
		// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
		if( result == -1 )
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_is_same_resource: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , res1->info->user_id );

		if( strcmp(res1->uri, res2->uri) == 0 )
		{
			// URI 대소문자를 구분하는  서비스에서... 입력받은 2개의 URI 가 동일한 경우
			return 1;
		}
		else
		{
			// URI 대소문자를 구분하는  서비스에서... 입력받은 2개의 URI 가 동일하지 경우
			return 0;
		}
	}
}


/* Check 
**   parent : res1
**   child  : res2
*/
// CHG 2012-03-26 huibong
// 서비스별 URI 대소문자 구분, 무시 여부 기능 추가.
// 본 함수는 MOVE, COPY 등의 작업시 상위 모듈인 mod_dav.c 의 dav_method_copymove() 함수에서 호출되는 함수로서
// 두개의 URI 가 동일할 경우 상위 모듈에서 HTTP_FORBIDDEN 403 을 반환처리함.
static int dav_repos_is_parent_resource(const dav_resource * res1, const dav_resource * res2)
{
	const char *parent_uri;

	// NEW 2012-03-26 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
	int result = 0;

	if (res1->hooks != res2->hooks)
		return 0;

	if (res1->exists == 0)
		return 0;

	parent_uri = ap_make_dirstr_parent(res2->pool, res2->uri);

	// NEW 2012-03-26 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
	result = dav_shared_is_uri_ignore_case( res1 );
	if( result == 1 ) // URI 대소문자 무시 서비스인 경우
	{
		if( strcasecmp( res1->uri, parent_uri ) == 0 )
		{
			// URI 대소문자를 구분하지 않는 서비스에서... 입력받은 2개의 URI 가 동일한 경우
			return 1;
		}
		else
		{
			// URI 대소문자를 구분하지 않는 서비스에서... 입력받은 2개의 URI 가 동일하지 경우
			return 0;
		}
	}
	else // URI 대소문자를 구분하는 서비스인 경우
	{
		// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
		if( result == -1 )
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_is_parent_resource: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , res1->info->user_id );

		if( strcmp( res1->uri, parent_uri ) == 0 )
		{
			// URI 대소문자를 구분하는  서비스에서... 입력받은 2개의 URI 가 동일한 경우
			return 1;
		}
		else
		{
			// URI 대소문자를 구분하는  서비스에서... 입력받은 2개의 URI 가 동일하지 경우
			return 0;
		}
	}
}



/** FIXME: */
/* Elenoa: 2006. 09. 18: make sync fd */
// CHG 2012-03-26 huibong
// dav_repos_sync_open() 함수는 미사용 함수이므로.. 2012-05-31 까지 유지 후 문제 없으면 제거 할 것.
//static void dav_repos_sync_open(const dav_stream *stream, apr_off_t offset)
//{
//	const dav_resource *resource = stream->resource;
//	request_rec *rec = resource->info->rec;
//	//apr_pool_t *pool = resource->info->pool;
//	dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;
//	const char *slength = NULL;
//	dav_repos_server_conf *dsc = NULL;
//
//	apr_sockaddr_t *sa;
//	apr_socket_t *sock;
//	apr_status_t rv;
//	char ebuf[128], *p;
//	sync_msg_header_t header;
//	apr_size_t length;
//	char *pszSafeStr;
//
//	dsc = dav_repos_get_server_conf(rec->server);
//
//	/* open socket */
//	rv = apr_sockaddr_info_get(&sa, dsc->sync_host, APR_INET, 13121, 0, db_r->p);
//	if (rv != APR_SUCCESS) {
//		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: failure to open sync host: %s", apr_strerror(rv, ebuf, sizeof ebuf));
//		return;
//	}
//	rv = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP, db_r->p);
//	if (rv != APR_SUCCESS) {
//		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: failure to create sync socket: %s", apr_strerror(rv, ebuf, sizeof ebuf));
//		return;
//	}
//	rv = apr_socket_timeout_set(sock, apr_time_from_sec(3));
//	if (rv != APR_SUCCESS) {
//		apr_socket_close(sock);
//		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: failure to set sync timeout: %s", apr_strerror(rv, ebuf, sizeof ebuf));
//		return;
//	}
//	apr_socket_connect(sock, sa);
//	db_r->sock = sock;
//
//	/* send header */
//	memset((char *)&header, 0, sizeof(sync_msg_header_t));
//	header.ulMode = htonl(0x0001);
//	if (!strncmp(dsc->storage_dir,stream->pathname,strlen(dsc->storage_dir))) {
//		//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: filename case #1 %s", stream->pathname);
//		pszSafeStr = apr_pcalloc(db_r->p, strlen(stream->pathname));
//		p = (char *)(stream->pathname + strlen(dsc->storage_dir));
//		if (*p == '/') p++;
//		strcpy(pszSafeStr, p);
//		//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: filename case #1 pszSafeStr %s", pszSafeStr);
//		p = strrchr(pszSafeStr, '/');
//		if (*p) {
//			*p = '\0';
//		}
//		strcpy(header.szFilePath, pszSafeStr);
//	} else {
//		//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: filename case #2 %s", stream->pathname);
//		pszSafeStr = apr_pcalloc(db_r->p, strlen(stream->pathname) + 1);
//		strcpy(pszSafeStr, stream->pathname);
//		p = strrchr(pszSafeStr, '/');
//		if (*p) {
//			*p = '\0';
//		}
//		strcpy(header.szFilePath, pszSafeStr);
//	}
//	if ((p = strrchr(stream->pathname, '/'))) p++;
//	strcpy(header.szFileName, (p ? p : stream->pathname));
//	slength = apr_table_get(rec->headers_in, "Content-Length");
//	header.fileSize = slength ? atoll(slength) : 0;
//	header.fileOffSet = offset;
//
//	/* send it */
//	length = sizeof(sync_msg_header_t);
//	rv = apr_socket_send(db_r->sock, (char *)&header, &length);
//	if (rv != APR_SUCCESS) {
//		apr_socket_close(sock);
//		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: failure to send sync header: %s", apr_strerror(rv, ebuf, sizeof ebuf));
//		db_r->sock = NULL;
//		return;
//	}
//}


static dav_error *dav_repos_open_stream(const dav_resource * resource, dav_stream_mode mode, dav_stream ** stream)
{	
	apr_int32_t flags = 0;
	request_rec *subrec = NULL;
	request_rec *rec = resource->info->rec;
	apr_pool_t *pool = resource->info->pool;
	dav_stream *ds = apr_pcalloc(pool, sizeof(*ds));
	dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;
	//dav_repos_server_conf *dsc = NULL;
	//char mode_str[128];

	// NEW 2012-03-20 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
	int result = 0;
	bool is_uricaseignore = false;
	
	// 2014.07.07 dadamin
	// 사용되지 않는 로직 주석 처리
	//switch( mode )
	//{
	//	case DAV_MODE_WRITE_SEEKABLE:
	//		sprintf(mode_str, "%s/%x/%c", "DAV_MODE_WRITE_SEEKABLE", mode, '\0');
	//		break;

	//	case DAV_MODE_WRITE_TRUNC:
	//		sprintf(mode_str, "%s/%x/%c", "DAV_MODE_WRITE_TRUNC", mode, '\0');
	//		break;

	//	default:
	//		sprintf(mode_str, "%s/%x/%c", "DAV_MODE_DEFAULT_READ", mode,'\0');
	//		break;
	//}

	//dsc = dav_repos_get_server_conf(rec->server);
	switch( mode ) 
	{
		default:
			flags = APR_READ | APR_BINARY;
			ds->is_write = 0;
			break;

		case DAV_MODE_WRITE_SEEKABLE:
			flags = APR_WRITE | APR_CREATE | APR_BINARY;

		case DAV_MODE_WRITE_TRUNC:
			if (flags == 0) 
			{
				flags = APR_WRITE | APR_CREATE | APR_BINARY | APR_TRUNCATE;
				db_r->m_get_content_length = 0;
			}

			/* get length */
			//slength = apr_table_get(rec->headers_in, "Content-Length");
			//db_r->m_get_content_length = slength ? atoll(slength) : 0;

			/* check remain */
/*
			// CHG 2012-02-29 trozan (#8347)
			// 계약 용량을 초과할 경우 HTTP 404 (HTTP_FORBIDDEN) 값을 반환 처리하는 부분을
			// HTTP 507 (HTTP_INSUFFICIENT_STORAGE) 반환 처리로 수정
			//////////////////////////////////////////////////////////////////////////
			dav_shared_vu_check() 함수에서 처리하는 HTTP_INSUFFICIENT_STORAGE (507) 관련 부분은
			실제 507을 보냈지만, 상위 모듈에서 403으로 다시 에러를 설정하여 보내서 access_log 에 507이 남지 않는다.
			그래서 dav_shared_vu_check() 함수를 dav_repos_write_stream() 에서 처리하도록 수정함.
			//////////////////////////////////////////////////////////////////////////

			if (dav_shared_vu_check(atol(rec->user), db_r->m_get_content_length) != OK)
			{
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL
					, "OPENDAV: Debug: dav_shared_vu_check() return error... Insufficient Storage of logically. (tranid '%d')", atoi(rec->user));

				return solbox_dav_new_error(pool, HTTP_INSUFFICIENT_STORAGE, 0
					, "There is logically not enough storage to write to this resource.");
			}
*/

			ds->is_write = 1;
#ifndef __OPENDISK_SOLUTION_NOT_USE_TS__
			dav_shared_ts_open_stream(rec, INB);
#endif // !__OPENDISK_SOLUTION_NOT_USE_TS__

			/* make time */
			db_r->m_get_lastmodified = apr_time_now();

			if( db_r->m_creation_date == 0 ) 
			{
				db_r->m_creation_date = db_r->m_get_lastmodified;
			}

			/* First , try to get the content-type from client */
			if( rec->content_type ) 
			{
				db_r->m_get_content_type = apr_pstrdup(pool, rec->content_type);
			} 
			else 
			{
				/* perform a "GET" on the resource's URI (note that the resource may not correspond to the current request!). */
				subrec = ap_sub_req_lookup_uri(rec->uri, rec, NULL);

				if( subrec && subrec->content_type ) 
				{
					db_r->m_get_content_type = apr_pstrdup(pool, subrec->content_type);
				}
			}

			/* No content type */
			if( db_r->m_get_content_type == NULL ) 
			{
				db_r->m_get_content_type = apr_pstrdup(pool, "application/octet-stream");
			}

			/* It must be upload (PUT Method) */
			/* Let's save db info here */

			// NEW 2012-03-23 유희곤
			// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
			result = dav_shared_is_uri_ignore_case( resource );
			if( result == 1 ) // URI 대소문자 무시 서비스인 경우
			{
				is_uricaseignore = true;
			}
			else
			{
				// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
				if( result == -1 )
				{
					ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_open_stream: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , resource->info->user_id );
				}

				is_uricaseignore = false;
			}

			// 2014.06.12 dadamin
			// display name 사용하는 형상 지원
			if(dav_use_display_name() == 1)
				db_r->m_displayname = db_r->m_fake_uri;
			
			if( (db_r->m_resource_id = dbms_set_property(db_r, resource, is_uricaseignore, false, flags, 0)) == -1)
			{
				/* Error handling */
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_open_stream: dbms_set_property func fail [%s]", db_r->m_fake_uri );
				return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "dav_repos_open_stream: meta info set failed.");
			}
		    
			// NEW 2010-03-24 webting
		    // dbms.h의 dbms_remove_replicate 함수 정의에서 내용 확인 요망.
			if( mode == DAV_MODE_WRITE_SEEKABLE )
				dbms_remove_replicate( db_r, is_uricaseignore);									
			
			break;
	}
		
	ds->p = pool;
	ds->pathname = resource->info->pathname;
	ds->resource = resource;
	// 2014.07.07 dadamin
	// setting write flags : 쓰기 완료 시 해당 flag 검사하여 trunc 된 경우 사이즈 오류 방지를 위해서 사용
	ds->test_flag = flags;

	/* set umask for new file permission, 644 */
	if( apr_file_open(&ds->f, ds->pathname, flags, APR_UWRITE|APR_UREAD|APR_GREAD|APR_WREAD, pool) != APR_SUCCESS )
	{
#ifndef __OPENDISK_SOLUTION_NOT_USE_TS__
		//if (mode == DAV_MODE_WRITE_SEEKABLE || mode == DAV_MODE_WRITE_TRUNC) {
		if( ds->is_write ) 
		{
			dav_shared_ts_close_stream(rec, INB);
		}
#endif // !__OPENDISK_SOLUTION_NOT_USE_TS__
	
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_open_stream: apr_file_open func fail [%s][%s]", ds->pathname, db_r->m_fake_uri );
		return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "dav_repos_open_stream: error occurred while opening resource.");
	}


	if( ds->is_write ) 
	{
	//if (mode == DAV_MODE_WRITE_SEEKABLE || mode == DAV_MODE_WRITE_TRUNC) {
#ifdef __UNUSE_FILELOCK__
		;	// nothing ~~~
#else
		if( apr_file_lock(ds->f, APR_FLOCK_NONBLOCK) != APR_SUCCESS ) 
		{
#ifndef __OPENDISK_SOLUTION_NOT_USE_TS__
			dav_shared_ts_close_stream(rec, INB);
#endif // !__OPENDISK_SOLUTION_NOT_USE_TS__
			apr_file_close(ds->f);
		
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_open_stream: apr_file_lock func fail [%s][%s]", ds->pathname, db_r->m_fake_uri );
			return solbox_dav_new_error(pool, HTTP_METHOD_NOT_ALLOWED, 0, "Write permission occupied by others.");
		}
#endif

		// CHG 2012-03-21 huibong
		// sibling 및 sync_host 기능은 2011 년 BD-06 사내망 제거 이후 더 이상 사용하지 않는 기능임.
		// 따라서 아래 관련 코드를 주석 처리함.
		///* Elenoa: 2006. 09. 18: make sync fd */
		//if( mode == DAV_MODE_WRITE_TRUNC && dsc->sync_host ) 
		//{
		//	dav_repos_sync_open(ds, 0);
		//
		//	if( db_r->sock == NULL )
		//		return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "dav_repos_open_stream: error occurred while opening a sync resource");
		//}

	}

	*stream = ds;
	return NULL;
}

static dav_error *dav_repos_close_stream(dav_stream * stream, int commit)
{
	const dav_resource *resource = stream->resource;
	dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;

	struct stat fs;
	int stat_ret = 0;

	// NEW 2012-03-23 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
	int result = 0;
	bool is_uricaseignore = false;

	// 2013.09.27 dadamin
	// 의미 없는 시스템콜 삭제 처리
	/* get file size */
	// 2014.01.09 dadamin
	// pool 획득에 따른 잘못된 파일 사이즈 업데이트 막기 위해서 pool 획득 이후 해당 크기를 구함
	//memset(&fs , 0x00 , sizeof(struct stat));
	//stat_ret = lstat ( stream->pathname , &fs ) ;

	if( stream->is_write ) 
	{
#ifndef __OPENDISK_SOLUTION_NOT_USE_TS__
		dav_shared_ts_close_stream(resource->info->rec, INB);
#endif // !__OPENDISK_SOLUTION_NOT_USE_TS__
		// 2013.09.27 dadamin
		// sibling 및 sync_host 기능 제거
		//if( db_r->sock ) 
		//{
		//	apr_socket_close(db_r->sock);
		//}

		resource->info->rec->read_length = stream->data_read;
		//db_r->m_get_content_length = fs.st_size;
		
		if( stat_ret < 0 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_close_stream: write lstat func fail[%d]. uri[%s][%" APR_INT64_T_FMT "] name[%s][%" APR_OFF_T_FMT "] err[%d][%s]"
					, stat_ret
					, db_r->m_fake_uri, db_r->m_get_content_length
					, stream->pathname, fs.st_size
					, errno, strerror(errno)
					);
					
			if( stream->is_write ) 
			{

#ifdef __UNUSE_FILELOCK__
				;	// nothing ~~~
#else
				apr_file_unlock(stream->f);
#endif
			}

			apr_file_close(stream->f);

			// CHG 2012-03-21 huibong
			// Write 실패가 발생하여 기존 RCDB 에 저장된 정보를 삭제 처리해야 하는 경우이므로...
			// 이런 경우에는 해당 파일에 대한 대소문자를 구분하여 처리토록 한다.
			if( dbms_remove_resource(db_r, false) != 0 ) 
			{
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_close_stream: dbms_remove_resource func fail [%s]", db_r->m_fake_uri );
				return solbox_dav_new_error(db_r->p, HTTP_INTERNAL_SERVER_ERROR, 0, "An error occurred while deleteing DBMS.");
			}

			if( apr_file_remove(stream->pathname, stream->p) != APR_SUCCESS ) 
			{
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_close_stream: apr_file_remove func fail [%s][%s]", db_r->m_fake_uri, stream->pathname );
				return solbox_dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, "dav_repos_close_stream:removing incompleted file failed");
			}

			/* Error handling */
			return solbox_dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, "Written file length and data length are diffrent");
		
		}
	}

	// NEW 2012-03-23 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
	result = dav_shared_is_uri_ignore_case( resource );
	if( result == 1 ) // URI 대소문자 무시 서비스인 경우
	{
		is_uricaseignore = true;
	}
	else
	{
		// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
		if( result == -1 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_close_stream: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , resource->info->user_id );
		}

		is_uricaseignore = false;
	}

	/* FIXME: Elenoa, Right? */
	/* Let's save db info here */
	if( (db_r->m_resource_id = dbms_set_property(db_r, resource, is_uricaseignore, true, stream->test_flag, (stream->start_pos + stream->data_read))) == -1 )
	{
		/* Error handling */
		if( stream->is_write ) 
		{
#ifdef __UNUSE_FILELOCK__
			;	// nothing ~~~
#else
			apr_file_unlock(stream->f);
#endif
		}

		apr_file_close(stream->f);

		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_close_stream: dbms_set_property func fail [%s]", db_r->m_fake_uri );
		return solbox_dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, "dav_repos_close_stream::");

	}

	if( stream->is_write ) 
	{
#ifdef __UNUSE_FILELOCK__
		;	// nothing ~~~
#else
		apr_file_unlock( stream->f );
#endif
	}
	
	apr_file_close( stream->f );

#ifdef __USE_AUTH_SOFTLINE_ONLY__
	/// 소프트 라인 - 파일 size 동기화를 휘한 처리 기능
	dav_auth_upload_end( resource->info );
#endif // __USE_AUTH_SOFTLINE_ONLY__


	return NULL;
}

static dav_error *dav_repos_write_stream(dav_stream * stream, const void *buf, apr_size_t bufsize)
{
	apr_status_t status;
	const dav_resource *resource = stream->resource;

	if( resource == NULL )
	{
		return solbox_dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0 , "An error occurred while writing to a resource. (resource empty)");
	}
			
	// 2015.05.14 dadamin
	// version 3.5 부터 서비스 사용량 체크 하지 않기함

	/* Keep counting the size */
	stream->data_read += bufsize;

	// 2015.07.15 dadamin
	// version 3.5 부터 input 필터에서 해당 통계 수집 처리
	//dav_transfer_progess_generate(rec, INB, bufsize, 0);
	
	status = apr_file_write_full(stream->f, buf, bufsize, NULL);

	if( APR_STATUS_IS_ENOSPC( status ) )
	{
		// CHG 2012-02-29 trozan (#8347)
		// 실 용량을 초과할 경우로 리턴 메시지 멘트에 physically 추가
		ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_write_stream: apr_file_write_full func fail. physically storage not enough." );
		return solbox_dav_new_error(stream->p, HTTP_INSUFFICIENT_STORAGE, 0 , "There is physically not enough storage to write to this resource.");
	}
	else if( status != APR_SUCCESS )
	{
		/* ### use something besides 500? */
		ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_write_stream: apr_file_write_full func fail[%d]", status );
		return solbox_dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, "An error occurred while writing to a resource.");
	}

	return NULL;
}


static dav_error *dav_repos_seek_stream(dav_stream * stream, apr_off_t abs_pos)
{
	//dav_repos_server_conf *dsc = NULL;
	//dsc = dav_repos_get_server_conf(stream->resource->info->rec->server);

	struct stat fs;
	int stat_ret = 0;
	int file_exist = 1;

	memset(&fs , 0x00 , sizeof(struct stat));
	stat_ret = lstat ( stream->pathname , &fs ) ;

	if( stat_ret < 0 )
		file_exist = 0;

	// seek the offset over ranges ~~~
	if( apr_file_seek(stream->f, APR_SET, &abs_pos) != APR_SUCCESS || ( 0 == file_exist) ) 
	{
		/* ### should check whether apr_file_seek set abs_pos was set to the
		 * correct position? */
		/* ### use something besides 500? */
		
		ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_seek_stream: apr_file_seek func fail [%s]", stream->pathname );
		return solbox_dav_new_error( stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, "Could not seek to specified position in the resource." );
	}

	stream->start_pos = abs_pos;

	// CHG 2012-03-21 huibong
	// sibling 및 sync_host 기능은 2011 년 BD-06 사내망 제거 이후 더 이상 사용하지 않는 기능임.
	// 따라서 아래 관련 코드를 주석 처리함.
	///* Elenoa: 2006. 09. 18: make sync fd */
	//if (stream->is_write && dsc->sync_host) 
	//{
	//	const dav_resource *resource = stream->resource;
	//	dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;
	//
	//	if (db_r->sock) 
	//	{
	//		apr_socket_close(db_r->sock);
	//		db_r->sock = NULL;
	//	}
	//
	//	dav_repos_sync_open(stream, abs_pos);
	//
	//	if (db_r->sock == NULL)
	//		return solbox_dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
	//			"dav_repos_seek_stream() : An error occurred while opening a resource.");
	//}

    return NULL;
}


static dav_error *dav_repos_set_headers(request_rec * r, const dav_resource * resource)
{
	dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;

	if( !resource->exists ) 
		return NULL;

	/* make sure the proper mtime is in the request record */
	ap_update_mtime(r, db_r->m_get_lastmodified);

	/* ### note that these use r->filename rather than <resource> */
	ap_set_last_modified(r);

	/* generate our etag and place it into the output */
	apr_table_setn(r->headers_out, "ETag", dav_repos_getetag(resource));

	/* we accept byte-ranges */
	apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");

	/* set up the Content-Length header */
	ap_set_content_length(r, (apr_off_t)db_r->m_get_content_length);

	r->content_type = db_r->m_get_content_type;
	apr_table_setn(r->headers_out, "Content-Type", db_r->m_get_content_type);

	return NULL;
}


static dav_error *dav_repos_deliver( const dav_resource * resource, ap_filter_t * output )
{
	apr_pool_t *pool = resource->pool;
	apr_bucket_brigade *bb = NULL;
	apr_file_t *fd = NULL;
	apr_status_t status;
	apr_bucket *bkt = NULL;
	dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;

	core_dir_config *conf =
		(core_dir_config *)ap_get_module_config(db_r->r->per_dir_config, &core_module);
	apr_int32_t flag = APR_READ | APR_BINARY;

#ifdef __OPENDAV_DEBUG_USER_AUTH__

	// CHG 2011-12-01 huibong
	// apr_table_get() 함수 반환 타입이 const char * 이므로 이에 맞도록 형 타입 수정처리.
	const char * range;
#endif

	/* Check resource type */
	if( resource->type != DAV_RESOURCE_TYPE_REGULAR
			&& resource->type != DAV_RESOURCE_TYPE_VERSION
			&& resource->type != DAV_RESOURCE_TYPE_WORKING) 
	{
		return solbox_dav_new_error(pool, HTTP_CONFLICT, 0, "Cannot GET this type of resource.");
	}

	if( resource->collection ) 
	{
		return solbox_dav_new_error(pool, HTTP_CONFLICT, 0, "There is no default response to GET for a collection.");
	}

	// 2012.07.06 dadamin
	// sendfile 지원
	if( conf->enable_sendfile == ENABLE_SENDFILE_ON )
		flag |= APR_SENDFILE_ENABLED;

	if( (status = apr_file_open(&fd, db_r->m_filename_hash, flag, 0, pool)) != APR_SUCCESS ) 
	{
		return solbox_dav_new_error(pool, HTTP_FORBIDDEN, 0, "File permissions deny server access.");
	}

	// 2015.07.15
	// output 트래픽 제어/통계 필터 활성화
	ap_add_output_filter("dav_bc_output", NULL, db_r->r, db_r->r->connection);
	dav_transfer_progess_generate(db_r->r, OUTB, 0, 0);

	bb = apr_brigade_create(pool, output->c->bucket_alloc);

#ifdef __OPENDAV_DEBUG_USER_AUTH__
	range = apr_table_get(db_r->r->headers_in, "Range");
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: DELIVER C %d RANGE %s:%s:%" APR_OFF_T_FMT "", getpid(), range, db_r->r->range, resource->info->finfo.size);
#endif

	if( (status = dav_auth_user_start(resource->info)) != OK ) 
	{
		if( status == HTTP_UNAUTHORIZED ) 
		{
			return solbox_dav_new_error(pool, HTTP_PRECONDITION_FAILED, 0, "User authorization from Service Provider FAILURE.");
		}
	}

	apr_brigade_insert_file(bb, fd, 0, resource->info->finfo.size, pool);

	bkt = apr_bucket_eos_create(output->c->bucket_alloc);
	APR_BRIGADE_INSERT_TAIL(bb, bkt);
	
#ifdef __OPENDAV_DEBUG_USER_AUTH__
	{
		apr_off_t length;
		apr_brigade_length(bb, 1, &length);
		range = apr_table_get(db_r->r->headers_in, "Range");
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: DELIVER B %d RANGE %s:%s:%" APR_OFF_T_FMT " BRIGADE %" APR_OFF_T_FMT "", getpid(), range, db_r->r->range, resource->info->finfo.size, length);
	}
#endif
	
	if( (status = ap_pass_brigade(output, bb)) != APR_SUCCESS )
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_deliver: ap_pass_brigade() return error. filter status [%d]", output->r->status );

		// 2012.07.03 dadamin
		// 로직에서 에러 리턴 후 같은 세션으로 메소드를 호출 시 
		// client 로 응답 메세지를 보내지 않는 현상이 확인하여(Could not parse response status line)
		// 해당 로직 에러시 로깅만하며, 에러를 리턴하지 않게 수정함
		return NULL;
	}

#ifdef __OPENDAV_DEBUG_USER_AUTH__
	{
		apr_off_t length = 0;
		apr_file_seek(fd, APR_CUR, &length);
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: DELIVER A %d RANGE %s BRIGADE %" APR_OFF_T_FMT "", getpid(), db_r->r->range, db_r->r->clength);
	}
#endif

	dav_auth_user_end(resource->info);

	return NULL;
}


static dav_error *dav_repos_create_collection(dav_resource * resource)
{
	dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;

	// NEW 2012-03-23 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
	int result = 0;
	bool is_uricaseignore = false;

	// NEW 2012-03-23 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
	result = dav_shared_is_uri_ignore_case( resource );
	if( result == 1 ) // URI 대소문자 무시 서비스인 경우
	{
		is_uricaseignore = true;
	}
	else
	{
		// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
		if( result == -1 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_create_collection: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , resource->info->user_id );
		}

		is_uricaseignore = false;
	}


	/* let's fill db_r  for collection */
	db_r->m_get_content_type = apr_pstrdup(db_r->p, DIR_MAGIC_TYPE);
	db_r->m_resource_type = dav_repos_COLLECTION;

	/* make time */
	db_r->m_creation_date = db_r->m_get_lastmodified = apr_time_now();

	// 2014.06.12 dadamin
	// display name 사용하는 형상 지원
	if(dav_use_display_name() == 1)
		db_r->m_displayname = db_r->m_fake_uri;
	
	if( (db_r->m_resource_id = dbms_set_property(db_r, resource, is_uricaseignore, false, 0, 0)) == -1 ) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_create_collection: dbms_set_property func fail [%s]", db_r->m_fake_uri );
		return solbox_dav_new_error(db_r->p, HTTP_INTERNAL_SERVER_ERROR, 0, "error occurred while writing to DBMS.");
	}

	return NULL;
}


static dav_error *dav_repos_copy_resource(const dav_resource * src, dav_resource * dst, int depth, dav_response ** response)
{ 
	dav_repos_resource *db_r_src = (dav_repos_resource *) src->info->db_r;
	dav_repos_resource *db_r_dst = (dav_repos_resource *) dst->info->db_r;

	// NEW 2012-03-23 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
	int result = 0;
	bool is_uricaseignore = false;

	// NEW 2012-03-26 huibong
	// MOVE/COPY 요청시 http header 상에 Overwrite: tag 에 대한 확인을 위한 목적으로 추가한다.
	// 이와 관련된 기능은 상위 mod_dav.c 상에서 처리하므로.. 본 함수에서 처리하지 않아도 되지만...
	// Overwrite 모드가 아닌 경우에 대한 확인을 위해 로깅 목적으로 추가한다. 
	// 아래 내역은 참고를 위한 내역이며.. 대분분 상위 모듈에서 처리된다.
	// Overwrite: 가 없는 경우 Default T 로 처리된다. ( WebDAV 규약 )
	// Overwirte: T  => Default. 대상에 대한 덮어쓰기 처리.. 이미 상위 모듈에서 동일 URI 인지 검사한 후 대상 URI 제거를 수행함.
	// Overwirte: F  => 상위 모듈에서 대상 URI가 존재할 경우 HTTP 412 Precondition Failed 를 반환한다.
	// Overwrite: 기타 => HTTP 400 Bad Request 를 반환 처리한다.
	const char * p_overwrite = apr_table_get( src->info->rec->headers_in, "Overwrite" );
	if( p_overwrite == NULL )
	{
		// HTTP Header 상에 Overwrite: tag 가 없는 경우..  WebDAV 규약 대로 default T 로 처리한다.
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_copy_resource: overwrite tag not found [%s][%s]", db_r_src->m_fake_uri, db_r_dst->m_fake_uri );
	}
	else if( strncasecmp( p_overwrite, "F", 1)==0 )  // Overwrite 모드가 아닌 경우
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_copy_resource: overwrite false mode [%s][%s][%s]", p_overwrite, db_r_src->m_fake_uri, db_r_dst->m_fake_uri );
	}

	// NEW 2012-03-23 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
	result = dav_shared_is_uri_ignore_case( src );
	if( result == 1 ) // URI 대소문자 무시 서비스인 경우
	{
		is_uricaseignore = true;
	}
	else
	{
		// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
		if( result == -1 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_copy_resource: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , src->info->user_id );
		}

		is_uricaseignore = false;
	}

	// CHG 2012-03-27
	// 상위 모듈에서 원본 및 대상 URI 에 대해 동일한지 dav_repos_is_same_resource() 함수를 호출하여 검사하므로...
	// 본 모듈에서 URI 에 대한 동일 여부를 재확인할 필요 없음.


	/* copy resources */
	if( dbms_copy_resource(db_r_src, db_r_dst, is_uricaseignore ) != 0 ) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_copy_resource: dbms_copy_resource func fail. src[%s] target[%s]", db_r_src->m_fake_uri, db_r_dst->m_fake_uri );
		return solbox_dav_new_error(db_r_src->p, HTTP_INTERNAL_SERVER_ERROR, 0, "An error occurred while copying DBMS.");
	}

	return NULL;
}


static dav_error *dav_repos_move_resource(dav_resource * src, dav_resource * dst, dav_response ** response)
{
	dav_repos_resource *db_r_src = (dav_repos_resource *) src->info->db_r;
	dav_repos_resource *db_r_dst = (dav_repos_resource *) dst->info->db_r;

	// NEW 2012-03-21 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
	int result = 0;
	bool is_uricaseignore = false;

	// NEW 2012-03-26 huibong
	// MOVE/COPY 요청시 http header 상에 Overwrite: tag 에 대한 확인을 위한 목적으로 추가한다.
	// 이와 관련된 기능은 상위 mod_dav.c 상에서 처리하므로.. 본 함수에서 처리하지 않아도 되지만...
	// Overwrite 모드가 아닌 경우에 대한 확인을 위해 로깅 목적으로 추가한다. 
	// 아래 내역은 참고를 위한 내역이며.. 대분분 상위 모듈에서 처리된다.
	// Overwrite: 가 없는 경우 Default T 로 처리된다. ( WebDAV 규약 )
	// Overwirte: T  => Default. 대상에 대한 덮어쓰기 처리.. 이미 상위 모듈에서 동일 URI 인지 검사한 후 대상 URI 제거를 수행함.
	// Overwirte: F  => 상위 모듈에서 대상 URI가 존재할 경우 HTTP 412 Precondition Failed 를 반환한다.
	// Overwrite: 기타 => HTTP 400 Bad Request 를 반환 처리한다.
	const char * p_overwrite = apr_table_get( src->info->rec->headers_in, "Overwrite" );
	if( p_overwrite == NULL )
	{
		// HTTP Header 상에 Overwrite: tag 가 없는 경우..  WebDAV 규약 대로 default T 로 처리한다.
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_move_resource: overwrite tag not found [%s][%s]", db_r_src->m_fake_uri, db_r_dst->m_fake_uri );
	}
	else if( strncasecmp( p_overwrite, "F", 1)==0 )  // Overwrite 모드가 아닌 경우
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_move_resource: overwrite false mode [%s][%s][%s]", p_overwrite, db_r_src->m_fake_uri, db_r_dst->m_fake_uri );
	}

	// NEW 2012-03-21 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
	result = dav_shared_is_uri_ignore_case( src );
	if( result == 1 ) // URI 대소문자 무시 서비스인 경우
	{
		is_uricaseignore = true;
	}
	else
	{
		// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
		if( result == -1 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_move_resource: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , src->info->user_id );
		}

		is_uricaseignore = false;
	}

	// CHG 2012-03-27
	// 상위 모듈에서 원본 및 대상 URI 에 대해 동일한지 dav_repos_is_same_resource() 함수를 호출하여 검사하므로...
	// 본 모듈에서 URI 에 대한 동일 여부를 재확인할 필요 없음.


	/* move resources */
	if( dbms_move_resource(db_r_src, db_r_dst, src, is_uricaseignore ) != 0 ) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_move_resource: dbms_move_resource func fail. src[%s] target[%s]", db_r_src->m_fake_uri, db_r_dst->m_fake_uri );
		return solbox_dav_new_error(db_r_src->p, HTTP_INTERNAL_SERVER_ERROR, 0, "An error occurred while moving DBMS.");
	}

	return NULL;
}


static dav_error *dav_repos_remove_resource(dav_resource * resource, dav_response ** response)
{
    dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;

	// NEW 2012-03-21 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
	int result = 0;
	bool is_uricaseignore = false;

	// NEW 2012-03-21 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
	result = dav_shared_is_uri_ignore_case( resource );
	if( result == 1 ) // URI 대소문자 무시 서비스인 경우
	{
		is_uricaseignore = true;
	}
	else
	{
		// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
		if( result == -1 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_remove_resource: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , resource->info->user_id );
		}

		is_uricaseignore = false;
	}

	/* Delete the resource */
	if( dbms_remove_resource(db_r, is_uricaseignore ) != 0 ) 
	{
		(*response) = NULL;

		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_remove_resource: dbms_remove_resource func fail [%s]", db_r->m_fake_uri );
		return solbox_dav_new_error(db_r->p, HTTP_INTERNAL_SERVER_ERROR, 0, "An error occurred while deleteing DBMS.");
	}

    return NULL;
}


static dav_error *dav_repos_walk(const dav_walk_params * params, int depth, dav_response ** response)
{
	dav_error *err = NULL;
	apr_pool_t *pool = params->pool;
	dav_repos_resource *tmp_r = NULL;
	dav_repos_resource *db_r = (dav_repos_resource *) params->root->info->db_r;
	dav_walker_ctx *ctx = params->walk_ctx;
	char *tmpStr = NULL;

	// NEW 2012-03-23 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
	int result = 0;
	bool is_uricaseignore = false;

	// NEW 2010-03-29 huibong
	// PROPFIND 에 따른 조회 처리시 조회 시작 시간 정보를 저장하기 위한 변수 및 파싱 기능 추가.
	// 본 기능은 연합뉴스 요구조건을 처리하기 위해 추가함.
	const char * startTime = NULL;
	startTime =  apr_table_get(db_r->r->headers_in, "StartTime");
	if( startTime != NULL )
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_respos_walk uri[%s] depth[%d] StartTime[%s]" , db_r->m_fake_uri, depth, startTime );
	}

	/* Let's start with NULL response */
	*response = NULL;

	// NEW 2012-03-23 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
	result = dav_shared_is_uri_ignore_case( params->root );
	if( result == 1 ) // URI 대소문자 무시 서비스인 경우
	{
		is_uricaseignore = true;
	}
	else
	{
		// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
		if( result == -1 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_walk: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , params->root->info->user_id );
		}

		is_uricaseignore = false;
	}

	/* 
	 ** search using postgresql 
	 ** if is not collection or depth=0, we have enough information
	 ** We support only depth 0 and 1
	 */
	// CHG 2010-03-29 huibong
	// dbms_get_collection_resource 함수상의 startTime 인자 정보 추가
	if( db_r->m_resource_type == dav_repos_COLLECTION && depth != 0 ) 
	{
		/* Will be filled children */
		if( dbms_get_collection_resource( db_r, depth, startTime, is_uricaseignore ) < 0 ) 
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_respos_walk: dbms_get_collection_resource func fail [%s]", db_r->m_fake_uri );
			return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "error occurred while reading DBMS.");
		}
	}

	// 2013.08.27 dadamin
	// SolDirve PROPFIND 시 네임스페이스 사용하기 때문에
	// 해당 파일(디렉토리 경우 소속된 모든 정보)에 대해서 
	// RCDB 네임스페이스 관련 조회 발생으로 응답 성능 저하가
	// 발생함으로  opendav 지정된(DAV:)와 기본 속성이 외에 값에
	// 대해서 오퍼레이션되지 않게함
#ifdef __USE_NAMESPACE__
	// 2013.08.12 dadamin
	// PROPPATCH 정상적인 기능을 위해서 필요함(allporp, proname) 에 대해서 추가 속성 조회
	// Revision 0150 삭제되 로직임 
	if (ctx->propfind_type == DAV_PROPFIND_IS_ALLPROP 
		|| ctx->propfind_type == DAV_PROPFIND_IS_PROPNAME ) 
	{
		dbms_fill_dead_property(db_r);
		/* Build name space id hash
		 ** It will build db_r->ns_id_hash
		 */
		dbms_build_ns_id_hash(db_r);
	}
#endif //  __USE_NAMESPACE__

	//2013.08.22 dadamin
	// hidden 속성 경우 header에 X-SB-Show-hidden 값이 yes 경우만 
	// source = 1인 항목을 리턴함
	bool is_showhidden = false;
	const char * showdidden = apr_table_get(db_r->r->headers_in, "X-SB-Show-hidden");

	if( showdidden != NULL && (strncmp(showdidden, "yes", 3)==0) )
	{			
		is_showhidden = true;
	}

	/* 
	 ** Lets walk through the results, 
	 ** assemble walk resource, and call walker
	 */
	for( tmp_r = db_r; tmp_r; tmp_r = tmp_r->next ) 
	{
		if( is_showhidden == false && tmp_r->m_source )
		{
			DBG1("Skip Hidden file [%s]", tmp_r->m_uri);
			continue;;
		}

		/* assemble walk resource */
		dav_walk_resource *wres = apr_pcalloc(pool, sizeof(*wres));
		dav_resource *resource = apr_pcalloc(pool, sizeof(*resource));
		dav_resource_private *info = apr_pcalloc(pool, sizeof(*info));

		/* Make info */
		info->pool = pool;
		info->pathname = tmp_r->m_fake_uri;
		info->db_r = tmp_r;

		/* Make resource */
		resource->exists = params->root->exists;	/* nulllock */
		resource->collection = tmp_r->m_resource_type;

		// 2014.06.12 dadamin
		// dispaly name 를 리턴 uri 사용하는 형상 지원
		char * display = tmp_r->m_uri;
		if( dav_use_display_name() == 1 )
			display = (char * ) tmp_r->m_displayname;

		//if( db_r->m_fake_uri == tmp_r->m_uri )
		// 첫번째 저장된 값은 safe 처리된 문자(') 포함되어 있으므로 원복처리한다.
		if(tmp_r == db_r)
		{
			// 단, display 컬럼 사용하는 경우 해당 값을 사용하기 때문에 원복처리할 필요 없다.
			if(display == tmp_r->m_uri)
			{
				// dav_repos_restore_unsafe_chars
				tmpStr = apr_pcalloc(pool, strlen(display) + 5);
				dav_repos_restore_unsafe_chars(display, tmpStr);
			}
			else
			{
				// 2014.11.27 dadamin
				// PROPFIND 응답 시 경로 /dav 로 리턴하도록 수정함
				char szRealURI[MaxSizeOfPathLength] = {0};
				remove_user_root_dir_from_fake_uri(display, szRealURI);
				
				if(strlen(szRealURI) == 0)
				{
					// case /dav 
					tmpStr = tmp_r->m_uri;
				}
				else 
				{
					// case /dav/????
					tmpStr = apr_pcalloc(pool, strlen(tmp_r->m_uri) + 5);
					dav_repos_restore_unsafe_chars(tmp_r->m_uri, tmpStr);
				}
			}
		}
		else
		{
			tmpStr = display;
		}


		if (strcmp(AP_SERVER_BASEVERSION, "Apache/2.2.27") <= 0 
			&&	strcmp(AP_SERVER_BASEVERSION, "Apache/2.2.26") >= 0)
		{			
			// 2014.05.10 dadamin 
			// apache 2.2.26 이상부터 해당값 대해서 상위 로직(dav_xml_escape_uri)에서 
			// ap_escape_uri() 함수 호출되지 않아서 한글포함된 경우 제대로된 결과값을 
			// 얻을 수 없기 때문에 본 로직에서 버전 체크하여 ap_escape_uri() 함수를 
			// 호출한 값을 전달 될 수 있도록 함
			resource->uri = ap_escape_uri(pool, tmpStr);
		}
		else
		{
			// 2015.07.07 dadamim
			// 최근 릴리즈된 버전(2.4.12, 2.2.29)에 해당 부분이 패치됨 
			// Changes with Apache 2.2.28 : mod_dav: Fix improper encoding in PROPFIND responses.  PR 56480.
			resource->uri = tmpStr;			
		}

		resource->info = info;
		resource->hooks = params->root->hooks;
		resource->pool = pool;

		/* Make walk resource */
		wres->pool = pool;
		wres->resource = resource;
		wres->response = *response;
		wres->walk_ctx = params->walk_ctx;

		/* need to set pool */
		tmp_r->p = pool;
		tmp_r->r = db_r->r;

		/* Need to set ns_id  */
		tmp_r->ns_id_hash = db_r->ns_id_hash;
		tmp_r->ns_hash = db_r->ns_hash;

		/* 
		 * Build dead/live props hash 
		 * It should be run even it's nulllock
		 */
		dav_repos_build_pr_hash(tmp_r);
		dav_repos_build_lpr_hash(tmp_r);

		/* Fill lock discovery for proppatch prop */
		if( ctx->propfind_type == DAV_PROPFIND_IS_PROPNAME || ctx->propfind_type == DAV_PROPFIND_IS_PROP )
			dav_repos_insert_lock_prop(params, tmp_r);

		/* Call walker */
		if( (err = (*params->func) (wres, tmp_r->m_resource_type == dav_repos_COLLECTION ?  DAV_CALLTYPE_COLLECTION : DAV_CALLTYPE_MEMBER)) != NULL) 
		{
			/* ### maybe add a higher-level description? */
			return err;
		}

		/* Save response for now */
		*response = wres->response;
	}

	/* Walk locknull files */
	if( params->walk_type & DAV_WALKTYPE_LOCKNULL && depth ) 
	{
		apr_text_header phdr = { 0 };
		apr_text *t;

		/* Let's read all locknull lists */
		err = dbms_load_locknull_list(db_r->r, db_r->m_fake_uri, &phdr, pool);
		if( err != NULL ) 
			return err;

		/* For all lists */
		for( t = phdr.first; t; t = t->next ) 
		{
			dav_lock *locks = NULL;
			
			/* assemble walk resource */
			dav_walk_resource *wres = apr_pcalloc(pool, sizeof(*wres));
			dav_resource *resource = apr_pcalloc(pool, sizeof(*resource));
			dav_resource_private *info = apr_pcalloc(pool, sizeof(*info));

			/* Make info */
			info->pool = pool;
			info->pathname = t->text;
			/* Let's make null dbr */
			info->db_r = NULL;

			/* Make resource */
			resource->exists = 0;
			resource->uri = t->text;
			resource->info = info;
			resource->hooks = params->root->hooks;
			resource->pool = pool;

			/* Make walk resource */
			wres->pool = pool;
			wres->resource = resource;
			wres->response = *response;
			wres->walk_ctx = params->walk_ctx;

			if( (err = dav_lock_query(params->lockdb, resource, &locks)) != NULL ) 
			{
				/* ### maybe add a higher-level description? */
				return err;
			}

			/* call the function for the specified dir + file */
			if (locks != NULL && (err = (*params->func) (wres, DAV_CALLTYPE_LOCKNULL)) != NULL) 
			{
				/* ### maybe add a higher-level description? */
				return err;
			}

			/* Save response for now */
			*response = wres->response;
		}
	}

	return NULL;
}


/*
**  make e tags using index and date 
*/
const char *dav_repos_getetag_dbr(const dav_repos_resource * db_r)
{
	// CHG 2011-05-16 huibong
	// pgpool 사용으로 인해 RCDB 01, 02 간에 resource_id 가 cross 되는 현상이 확인됨.
	// 이에 따라 etag 생성 방식을 기존 resource_id - length - creation_date 에서
	// filename_hash (32 자리) + length + get_lastmodified 형식으로 변경 처리함.
    
	// BUG 2011-11-29 huibong
	// 서비스 개통시 최초 생성되는 depth = 0 인 최상위 폴더에 대해 filename_hash 정보가 비어 있음.
	// 따라서 filename_hash 정보를 추출하기 위해 -32 연산을 수행할 경우 temp 포인터가 예상치 못한 구간을 나타낼 수 있는 버그 발생
	// 이를 수정하기 위해 filename_hash 의 길이가 32 이하인 경우 filename_hash 정보를 00000000000000000000000000000000 으로 처리.
	
	if( strlen(db_r->m_filename_hash) <= 32 )
	{
		return apr_psprintf( db_r->p, "\"00000000000000000000000000000000%x%x\"",
				(unsigned int)db_r->m_get_content_length,
				(unsigned int)db_r->m_get_lastmodified );
	}
	else
	{
		const char * temp = (db_r->m_filename_hash) + ( strlen(db_r->m_filename_hash) - 32) ;

		return apr_psprintf( db_r->p, "\"%s%x%x\"",
				temp,
				(unsigned int)db_r->m_get_content_length,
				(unsigned int)db_r->m_get_lastmodified );

	}

}


const char *dav_repos_getetag(const dav_resource * resource)
{
	if (!resource->exists || resource->info == NULL || resource->info->db_r == NULL) 
		return apr_pstrdup(resource->info->pool, "");

	return dav_repos_getetag_dbr(resource->info->db_r);
}


const dav_hooks_repository dav_repos_hooks_repos = {
    1,				/* special GET handling *//* 1 for GET handling, 0 for generic */
	dav_repos_get_resource,
    dav_repos_get_parent_resource,
    dav_repos_is_same_resource,
    dav_repos_is_parent_resource,
    dav_repos_open_stream,
    dav_repos_close_stream,
    dav_repos_write_stream,
    dav_repos_seek_stream,
    dav_repos_set_headers,
    dav_repos_deliver,
    dav_repos_create_collection,
    dav_repos_copy_resource,
    dav_repos_move_resource,
    dav_repos_remove_resource,
    dav_repos_walk,
    dav_repos_getetag,
};


/* Insert prop with propid */
static dav_prop_insert dav_repos_insert_prop(const dav_resource * resource,
					     int propid, dav_prop_insert what,
					     apr_text_header * phdr)
{
	int i;
	const char *value = NULL;
	const char *name = NULL;
	const char *s = NULL;
	
	apr_pool_t *pool = resource->pool;
	dav_repos_resource *dbr = (dav_repos_resource *) resource->info->db_r;

	/*
	 ** None of FS provider properties are defined if the resource does not
	 ** exist. Just bail for this case.
	 **
	 ** Even though we state that the FS properties are not defined, the
	 ** client cannot store dead values -- we deny that thru the is_writable
	 ** hook function.
	 */
	if (!resource->exists)
		return DAV_PROP_INSERT_NOTDEF;

	/* find propname using prop id */
	for( i = 0; dav_repos_props[i].name; i++ ) 
	{
		if( propid == dav_repos_props[i].propid ) 
		{
			name = dav_repos_props[i].name;
			break;
		}
	}

	/* ### what the heck was this property? */
	if( name == NULL )
		return DAV_PROP_INSERT_NOTDEF;

	/* Get value */
	value = apr_hash_get(dbr->lpr_hash, name, APR_HASH_KEY_STRING);

	/* ### Not found in the hash */
	if( value == NULL )
		return DAV_PROP_INSERT_NOTDEF;

	/* Do something according to what */
	if( what == DAV_PROP_INSERT_VALUE ) 
	{
		s = apr_psprintf(pool, "<D:%s>%s</D:%s>" DEBUG_CR, name, value, name);
	}
	else if( what == DAV_PROP_INSERT_NAME ) 
	{
		s = apr_psprintf(pool, "<D:%s/>" DEBUG_CR, name);
	}
	else
	{
		/* assert: what == DAV_PROP_INSERT_SUPPORTED */
		s = apr_psprintf(pool,
				"<D:supported-live-property D:name=\"%s\" "
				"D:namespace=\"%s\"/>" DEBUG_CR,
				name, dav_repos_namespace_uris[0]);
	}
	apr_text_append(pool, phdr, s);

	/* we inserted what was asked for */
	return what;
}


static int dav_repos_is_writable(const dav_resource * resource, int propid)
{
    int i;
	// 2013.08.29 dadamin
	// 디렉토리에 대해서 hidden, getlastmodified 수정 허용함
	// 2013.08.22 dadamin
	// 디렉토리 경우 유일한 값을 추출할 수 없어서 속성 변경 불가능 하도록함
	//if(resource && resource->info)
	//{
	//	dav_resource_private *ctx = resource->info;
	//	if(ctx->db_r)
	//	{
	//		if(ctx->db_r->m_resource_type) 
	//			return 0;
	//	}
	//}

	/* Try to find is_writable */
	for( i = 0; dav_repos_props[i].name; i++ ) 
	{
		if( propid == dav_repos_props[i].propid ) 
		{
			return dav_repos_props[i].is_writable;
		}
	}

	/* It is writable */
	return 1;
}

// 2013.08.21 dadamin
// PROPPATCH hidden 유효성 체크 
static dav_error *dav_repos_patch_validate_hidden(const dav_resource * resource, const apr_xml_elem * elem, int operation, void **context)
{
	const apr_text *cdata;
	const apr_text *f_cdata;
	char value;	

	if (operation == DAV_PROP_OP_DELETE) 
	{
		return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
			"The 'hidden' property cannot be removed.");
	}

	cdata = elem->first_cdata.first;

	/* ### hmm. this isn't actually looking at all the possible text items */
	f_cdata = elem->first_child == NULL
		? NULL
		: elem->first_child->following_cdata.first;


	if (cdata == NULL) {
		if (f_cdata == NULL) {
			return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
				"The 'hidden' property expects a single "
				"character, valued 'T' or 'F'. There was no "
				"value submitted.");
		}
		cdata = f_cdata;
	}
	else if (f_cdata != NULL)
		goto too_long;

	if (cdata->next != NULL || strlen(cdata->text) != 1)
		goto too_long;

	value = cdata->text[0];
	if (value != 'T' && value != 'F') {
		return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
			"The 'hidden' property expects a single "
			"character, valued 'T' or 'F'. The value "
			"submitted is invalid.");
	}

	*context = (void *)((long)(value == 'T'));

	return NULL;
too_long:
	return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
		"The 'hidden' property expects a single "
		"character, valued 'T' or 'F'. The value submitted "
		"has too many characters.");
}
// PROPPATCH getlastmodified 유효성 체크 
static dav_error *dav_repos_patch_validate_getlastmodified(const dav_resource * resource, const apr_xml_elem * elem, int operation, void **context)
{
	const apr_text *cdata;
	const apr_text *f_cdata;

	if (operation == DAV_PROP_OP_DELETE) 
	{
		return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
			"The 'getlastmodified' property cannot be removed.");
	}

	cdata = elem->first_cdata.first;

	/* ### hmm. this isn't actually looking at all the possible text items */
	f_cdata = elem->first_child == NULL
		? NULL
		: elem->first_child->following_cdata.first;


	if (cdata == NULL) {
		if (f_cdata == NULL) {
			return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
				"The 'getlastmodified' property expects a UTC "
				"timestamp. There was no value submitted.");
		}
		cdata = f_cdata;
	}
	else if (f_cdata != NULL)
		goto empty_data;

	if (cdata->next != NULL || strlen(cdata->text) <= 0)
		goto empty_data;

	//value = cdata->text[0];
	if (atoll(cdata->text) <= 0) {
		return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
			"The 'getlastmodified' property expects a UTC timestamp."
			"The value submitted is invalid.");
	}

	*context = (void *)(cdata->text);

	return NULL;

empty_data :
	return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
		"The 'getlastmodified' property expects a UTC "
		"timestamp. There is no data.");
}

static dav_error *dav_repos_patch_validate( const dav_resource * resource, const apr_xml_elem * elem, int operation, void **context, int *defer_to_dead ) 
{
	// 2013.08.21 dadamin
	// PROPPATCH 오퍼레이션 및 설정값 유효성 체크
	dav_elem_private *priv = elem->priv;
	
	DBG2("dav_repos_patch_validate: propid[%d], operation [%d]", priv->propid, operation);
	
	switch(priv->propid)
	{
	case DAV_PROPID_hidden:
		return dav_repos_patch_validate_hidden(resource, elem, operation, context);
		break;
	case DAV_PROPID_getlastmodified:
		return dav_repos_patch_validate_getlastmodified(resource, elem, operation, context);
		break;
	default:
		*defer_to_dead = 1;
		break;
	}

	return NULL;
}

static dav_error *dav_repos_patch_exec( const dav_resource * resource, const apr_xml_elem * elem, int operation, void *context, dav_liveprop_rollback ** rollback_ctx )
{
	// 2013.08.21 dadamin
	// PROPPATCH 에 해당 되는 오퍼레이션 동작

	dav_resource_private *ctx = resource->info;
	dav_elem_private *priv = elem->priv;
	bool is_uricaseignore = false;
	DBG2("dav_repos_patch_exec: propid[%d], operation [%d]", priv->propid, operation);

	int res = dav_shared_is_uri_ignore_case( resource );
	if( res == 1 ) // URI 대소문자 무시 서비스인 경우
	{
		is_uricaseignore = true;
	}
	else // 양방향 기본 ( URI 대소문자 구분 )
	{
		// 만약 dav_shared_is_uri_ignore_case_by_tranid 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
		if( res == -1 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, 
				"dav_repos_patch_exec: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]"
				, resource->info->user_id);
		}

		is_uricaseignore = false;
	}

	switch(priv->propid)
	{
	case DAV_PROPID_hidden:
		{
			long value = context != NULL;
			
			// don't do anything if there is no change. no rollback info either.
			if( ctx->db_r->m_source == value )
				break;

			if( dbms_update_hidden(ctx->db_r, value, is_uricaseignore) == -1)
			{
				return solbox_dav_new_error(resource->info->pool,
									HTTP_INTERNAL_SERVER_ERROR, 0,
									"Could not set the hidden flag of the "
									"target resource.");
			}
		}
		break;
	case DAV_PROPID_getlastmodified:
		{
			apr_int64_t  value = atoll((char * )context);

			// don't do anything if there is no change. no rollback info either.
			if(value == ctx->db_r->m_get_lastmodified)
				break;

			if( dbms_update_file_lastmodified(ctx->db_r, value, is_uricaseignore) == -1)
			{
				return solbox_dav_new_error(resource->info->pool,
									HTTP_INTERNAL_SERVER_ERROR, 0,
									"Could not set the getlastmodified value of the "
									"target resource.");
			}
		}
		break;
	default:
		break;
	}

    return NULL;
}


static void dav_repos_patch_commit( const dav_resource * resource, int operation, void *context, dav_liveprop_rollback * rollback_ctx )
{
    /* nothing to do */
	// 2013.08.21 dadamin
	// dav_repos_patch_exec() 함수에서 모두 처리되기 때문에 후 처리 필요 없음
}


static dav_error *dav_repos_patch_rollback( const dav_resource * resource, int operation, void *context, dav_liveprop_rollback * rollback_ctx )
{
	/* nothing to do */
	// 2013.08.21 dadamin
	// dav_repos_patch_exec() 함수에서 모두 처리되기 때문에 후 처리 필요 없음

    return NULL;
}


static const dav_hooks_liveprop dav_repos_hooks_liveprop = {
    dav_repos_insert_prop,
    dav_repos_is_writable,
    dav_repos_namespace_uris,
    dav_repos_patch_validate,
    dav_repos_patch_exec,
    dav_repos_patch_commit,
    dav_repos_patch_rollback
};
