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

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

   $Id: util.c,v 1.5 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/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <iconv.h>

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


#include <apr.h>
#include <apr_md5.h>
#include <apr_strings.h>
#include <apr_hash.h>
#include <apr_tables.h>
#include <apr_file_io.h>
#include <apu_version.h>
#include <apr_dbd.h>
#include <mod_dbd.h>



#include "dav_repos.h"
#include "dbms.h"
#include "util.h"


/* Note: the "dav_repos" prefix is mandatory */
extern module AP_MODULE_DECLARE_DATA dav_repos_module;

/*****************************/
/* common functions */

// FreeBSD 6.2 설치된 iconv 경우 iconv에 srcbuf를 const char* 로 받음
#ifdef __FreeBSD__
static char *iconv_string(apr_pool_t *p, iconv_t cd, const char *srcbuf, size_t srclen)
#else // !__FreeBSD__
static char *iconv_string(apr_pool_t *p, iconv_t cd, char *srcbuf, size_t srclen)
#endif // __FreeBSD__
{
    char *outbuf, *marker;
    size_t outlen;

    if (srclen == 0) {
#ifdef __FreeBSD__
		return (char *)srcbuf;
#else // !__FreeBSD__
        return srcbuf;
#endif // __FreeBSD__
    }

    /* Allocate space for conversion. Note max bloat factor is 4 of UCS-4 */
    marker = outbuf = (char *)apr_palloc(p, outlen = srclen * 4 + 1);

    if (outbuf == NULL) {
        return NULL;
    }

    /* Convert every character within input string. */
    while (srclen > 0) {
#ifdef __FreeBSD__
        if (iconv(cd, (const char **)&srcbuf, &srclen, &outbuf, &outlen) == (size_t)(-1)) 
#else // !__FreeBSD__
		if (iconv(cd, &srcbuf, &srclen, &outbuf, &outlen) == (size_t)(-1)) 
#endif // __FreeBSD__
		{

            return NULL;
        }
    }

    *outbuf = '\0';

    return marker;
}

// NEW 2019-09-18 huibong 인코딩 처리 관련 신규 추가 (#32584)
// - URL 인코딩에 대한 판단 오류 상황을 보완하기 위해 인터넷에 유통 중인 소스 중 신뢰성 있는 소스의 기능을 추가
// - 본 소스는 전달받은 문자열이 UTF-8 인코딩인지를 판단하는 함수로서
// - android base 소스에 등록된 함수임
// - 참고자료
//   -- https://android.googlesource.com/platform/frameworks/base/+/master/media/jni/android_media_MediaScanner.cpp
static bool isValidUtf8( const char * bytes ) 
{
	while( *bytes != '\0' ) 
	{
		unsigned char utf8 = *( bytes++ );
		//ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "isValidUtf8: [%c][%02X][%02X]", utf8, utf8, utf8>>4);

		// Switch on the high four bits.
		switch( utf8 >> 4 )
		{
			case 0x00:
			case 0x01:
			case 0x02:
			case 0x03:
			case 0x04:
			case 0x05:
			case 0x06:
			case 0x07:
				// Bit pattern 0xxx. No need for any extra bytes.
				break;

			case 0x08:
			case 0x09:
			case 0x0a:
			case 0x0b:
			case 0x0f:
				/*
				 * Bit pattern 10xx or 1111, which are illegal start bytes.
				 * Note: 1111 is valid for normal UTF-8, but not the
				 * modified UTF-8 used here.
				 */
				return false;

			case 0x0e:
				// Bit pattern 1110, so there are two additional bytes.
				utf8 = *( bytes++ );
				if( ( utf8 & 0xc0 ) != 0x80 ) {
					return false;
				}
				// Fall through to take care of the final byte.
				//FALLTHROUGH_INTENDED;
			case 0x0c:
			case 0x0d:
				// Bit pattern 110x, so there is one additional byte.
				utf8 = *( bytes++ );
				//ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "isValidUtf8:   [%c][%02X][%02X]", utf8, utf8, utf8 & 0xc0 );

				if( ( utf8 & 0xc0 ) != 0x80 ) {
					return false;
				}

				// ADD 2019-09-18 huibong 연관일감 #21096 관련 회피 기능 추가 ((#32584)
				// - "책" 문자열이 utf-8 로 인식되는 거를 막기 위해 회피하도록 조건 추가 
				if( utf8 == 0xa5 ) {
					return false;
				}

				break;
		}
	}
	return true;
}

// CHG 2019-09-18 huibong URL의 정확한 인코딩 탐지 실패 관련 기능 보완 (#32584)
// - #32584 에서 UTF-8 기반 url 을 UHC 로 인식하는 문제 발생
// - UTF-8, UHC 혼합사용으로 인해 정확한 탐지를 실패할 가능성이 존재하지만.....
// - 이를 조금이라도 보완하기 위해 인코딩 check 로직을 보완함.
static int is_valid_uhc( char * buf )
{
	// CHG 2019-09-18 huibong UTF-8 enconding 인지 check 기능 추가 (#32584)
	// - 우선 기본으로 UTF-8 인 요청을 거른다.
	// - 인코딩 특성상 UHC 인데.. UTF-8 로 인식될 가능성이 있다.
	// - 이런 경우에는 isValidUtf8() 함수에 해당 요청에 대한 예외를 추가하는 방법 밖에 없다.
	if( isValidUtf8( buf ) == true )
	{
		//ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "is_valid_uhc: isValidUtf8() return UTF-8 [%s]", buf );
		return 0;
	}

	// 일단 UTF-8 인코딩이 아니라고 판단된 경우....
	// - 대부분 UHC 이겠지만.... 
	// - UTF-8 인데.. 예외 처리 등으로 UHC 로 잘못 판정될 가능성 존재하므로.....
	// - iconv 를 이용하여 다시 UHC 인지 다시 확인한다. 
	char outBuf[4096] = { 0 };

	iconv_t cd = iconv_open( "UTF-8", "UHC" );
	if( cd == (iconv_t)( -1 ) )
	{
		ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "is_valid_uhc: iconv open failed [%s]", buf );
		return 0;
	}

	size_t readBytes = strlen( buf );
	size_t writeBytes = sizeof( outBuf );

	char * in = buf;
	char * out = outBuf;
	if( iconv( cd, (const char **)& in, &readBytes, &out, &writeBytes ) == (size_t)-1 )
	{
		int errorNum = errno;
		iconv_close( cd );

		// UHC -> UTF-8 로 변환 실패시... 
		// - 변환 성공시 UHC 이지만.. 변환 실패시에는 UTF-8 로 판정한다.
		ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "is_valid_uhc: iconv UHC->UTF-8 error. reuturn UTF-8 [%d][%s]", errorNum, buf );
		return 0;
	}
	
	iconv_close( cd );

	// 최종 UHC 로 판정한다.
	//ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "is_valid_uhc: return UHC [%s]", buf );
	return 1;
}


// UTF-8 -> Database Character Set
char* transformDBCharacter(request_rec *r, char* srcbuf, char *prefixmsg )
{
	char *ret = NULL;
	char *srccharset = NULL, *destcharset = NULL;
	dav_repos_server_conf *conf = NULL;
	conf = dav_repos_get_server_conf(r->server);
	iconv_t cd;

	if( conf == NULL )
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"%s: dav_repos_get_server_conf NULL. return srcbuf", prefixmsg);
		return srcbuf;
	}

	if( conf->db_charset == NULL )
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
			"%s: config DavProviderDBCharset set NULL. return srcbuf", prefixmsg);
		return srcbuf;
	}

	destcharset = (char *) conf->db_charset;

	// Database Character UTF-8 지원 시 해당 로직은 수행하지 않음
	if (strcmp( destcharset, "UTF-8") == 0)
	{
		return srcbuf;
	}

	// check UHC Character
	if ( is_valid_uhc(srcbuf) == 1) 
	{
		ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "transformDBCharacter: url encoding is UHC [%s][%s]", prefixmsg, srcbuf);
		srccharset = "UHC";
	}
	else
	{
		//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "transformDBCharacter: url encoding is UTF-8 [%s][%s]", prefixmsg, srcbuf);
		srccharset = "UTF-8";
	}
	
	// unknown Character set and return srcbuf
	if( srccharset == NULL )
	{
		return srcbuf;
	}

	// compare character set
	if( strcmp(srccharset, destcharset) == 0 )
	{
		//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s: same Character [%s]", prefixmsg, srcbuf);
		return srcbuf;
	}

	// transform src character set -> DB character set
	cd = iconv_open(destcharset, srccharset);
	if( cd == (iconv_t)(-1)) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s: iconv open failed [%s]", prefixmsg, srcbuf);
		ret = apr_pstrdup(r->pool, srcbuf);
	} 
	else 
	{
		ret = iconv_string(r->pool, cd, srcbuf, strlen(srcbuf));
		if (ret == NULL) 
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s: iconv error [%s]",prefixmsg, srcbuf);
			ret = apr_pstrdup(r->pool, srcbuf);
		}

		iconv_close(cd);
	}

	return ret;
}

// Database Character Set -> UTF-8
char* transformUTF8(request_rec *r, char* srcbuf, char *prefixmsg )
{
	char *ret = NULL;
	char *srccharset = NULL, *destcharset = NULL;
	dav_repos_server_conf *conf = NULL;
	conf = dav_repos_get_server_conf(r->server);
	iconv_t cd;

	destcharset = "UTF-8";

	if( conf == NULL )
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
			"%s: dav_repos_get_server_conf NULL. return srcbuf", prefixmsg);
		return srcbuf;
	}

	if( conf->db_charset == NULL )
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
			"%s: config DavProviderDBCharset set NULL. return srcbuf", prefixmsg);
		return srcbuf;
	}
	
	// check UHC Character
	if (is_valid_uhc(srcbuf) == 1)
	{
		srccharset = "UHC";
	}
	else 
	{
		srccharset = "UTF-8";
	}

	// compare character set
	if( strcmp(srccharset, destcharset) == 0 )
	{	
		return srcbuf;
	}

	// transform DB character set -> UTF-8
	cd = iconv_open(destcharset, srccharset);
	if( cd == (iconv_t)(-1)) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s: iconv open failed [%s]", prefixmsg, srcbuf);
		ret = apr_pstrdup(r->pool, srcbuf);
	} 
	else
	{
		ret = iconv_string(r->pool, cd, srcbuf, strlen(srcbuf));
		if (ret == NULL) 
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s: iconv error[%s]", prefixmsg, srcbuf);
			ret = apr_pstrdup(r->pool, srcbuf);
		}

		iconv_close(cd);
	}

	return ret;	
}


int dav_repos_remove_base_uri(const char *base_uri, const char *req_uri, char *result)
{
	int nCount = 0;
	int req_uri_length = 0;

	if((base_uri == NULL) || (req_uri == NULL) || (result == NULL)) 
	{
		return -1;
	}

	req_uri_length = strlen(req_uri);
	if(req_uri_length == 1) 
	{
		if((*req_uri == '/') || (*req_uri == '\0') || (*req_uri == ' ')) 
		{
			result[0] = '/';
			result[1] = '\0';
		} 
		else 
		{
			return -2;
		}
		
		return 1;
	} 
	else if(req_uri_length == 0) 
	{
		return -3;
	}

	while((*req_uri != '\0') && (*base_uri != '\0')) 
	{
		if(*base_uri == *req_uri) 
		{
			base_uri++;
			req_uri++;
		} 
		else 
		{
			break;
		}
	}

	while(*req_uri != '\0') 
	{
		result[nCount++] = *req_uri++;
	}

	result[nCount] = '\0';
	return 1;
}


int remove_user_root_dir_from_fake_uri(const char *pcszFakeURI, char *pURI)
{
	int nCount = 0;
	int sCount = 0;
	char sztmpBuffer[MaxSizeOfPathLength];

	if(pcszFakeURI == NULL) 
	{
		return -1;
	}
	if(strlen(pcszFakeURI) > MaxSizeOfPathLength) 
	{
		return -2;
	}

	while(*pcszFakeURI != '\0') 
	{
		if(*pcszFakeURI == '/') 
		{
			sCount++;
			if(sCount == 2) 
			{
				nCount = 0;
				memset(sztmpBuffer, 0, MaxSizeOfPathLength);
				sztmpBuffer[nCount++] = *pcszFakeURI++;
			}
		}
		sztmpBuffer[nCount++] = *pcszFakeURI++;
	}

	sztmpBuffer[nCount] = '\0';
	snprintf(pURI, MaxSizeOfPathLength, "%s", sztmpBuffer);
	
	return 1;
}


int get_filename_in_path(const char *pcszPath, char *pszPathName, char *pszFileName)
{
	int nCount = 0;
	char sztmpBuffer[MaxSizeOfPathLength];

	if((pcszPath == NULL) || (pszFileName == NULL) || (pszPathName == NULL)) 
	{
		return -1;
	}
	if(strlen(pcszPath) > MaxSizeOfPathLength) 
	{
		return -2;
	}

	memset(sztmpBuffer, 0, MaxSizeOfPathLength);
	while(*pcszPath != '\0') 
	{
		if(*pcszPath == '/') 
		{
			sztmpBuffer[nCount++] = *pcszPath++;
			if(*pcszPath == '\0') 
			{
				break;
			}
			
			sztmpBuffer[nCount] = '\0';
			//DBG1("get_filename_in_path::sztmpBuffer = %s", sztmpBuffer);
			strncat(pszPathName, sztmpBuffer, MaxSizeOfPathLength - strlen(pszPathName));
			nCount = 0;
			memset(sztmpBuffer, 0, MaxSizeOfPathLength);
		}
		sztmpBuffer[nCount++] = *pcszPath++;
	}

	sztmpBuffer[nCount] = '\0';
	snprintf(pszFileName, MaxSizeOfPathLength, "%s", sztmpBuffer);
	
	return 1;
}


// CHG 2010-08-10 huibong
// Hash 정보 생성 처리시 생성 결과를 static 변수에 저장하고
// 해당 변수를 반환처리 하는 경우 ..
// 동시간 접근시 Lock 을 사용하지 않게 되면 문제가 발생하게 됨. ( File Name에 대한 Hash 정보가 동일하게 생성)
// 하지만 Lock 을 처리속도에 영향을 미칠수 있으므로 
// 결과 값을 저장할 출력변수를 매개변수로 전달받도록 수정처리 한다.
//char *get_hashed_filepath(const char *plain_filepath)
bool get_hashed_filepath( const char *plain_filepath, char * result, int result_length)
{
	// 매개변수에 대한 검증 처리
	if( plain_filepath == NULL || result == NULL || result_length < (APR_MD5_DIGESTSIZE * 2 + 1))
		return false;
	else
	{
		// 출력값을 저장할 변수가 적합한 경우.
		int i;
		unsigned char digest[APR_MD5_DIGESTSIZE];
		apr_md5(digest, (const unsigned char *) plain_filepath, strlen(plain_filepath));

		// 화면에 표시 가능하도록 Hex 처리.
		for (i = 0; i < APR_MD5_DIGESTSIZE; i++)
			apr_snprintf((char *) &result[i + i], 3, "%02x", digest[i]);

		result[APR_MD5_DIGESTSIZE * 2] = '\0';

		return true;
	}
}


/* split user_login_id@partner_login_id into  */ 
/* user_login_id, partner_login_id */ 

/* 2007. 07. 30: Elenoa Lazyfake
 * r->user format changed: for classified traffic manage
 * (in Makefile, __CLASSIFIED_TRAFFIC_MANAGE__)
 * from: tranid@userid@sessionkey
 * to: tranid.classid@userid@sessionkey
 */
int at_split(const char *pszAcctInfo, acct_info *pAcctInfo)
{
	int fFound = 0;
	int chCount = 0;
	char sztmpBuffer[MaxSizeOfAcctSession + 1];

	if((pszAcctInfo == NULL) || (pAcctInfo == NULL)) 
	{
		return -1;
	}

	/* 0nd step : clear acct_info buffer */
	memset((char *)pAcctInfo, 0, sizeof(acct_info));

	/* 1st step : parse user login id */
	fFound = 0;
	chCount = 0;
	memset(sztmpBuffer, 0, MaxSizeOfAcctVolume);
	while(*pszAcctInfo != '\0') 
	{
		if(*pszAcctInfo == '@') 
		{
			sztmpBuffer[chCount] = '\0';
			snprintf(pAcctInfo->szVolumeID, MaxSizeOfAcctVolume, "%s", sztmpBuffer);
			DBG1("at_split::sztmpBuffer #1(end) = %s", sztmpBuffer);
			fFound = -1;
			break;
		}
#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
		if(*pszAcctInfo == '.') 
		{
			sztmpBuffer[chCount] = '\0';
			snprintf(pAcctInfo->szVolumeID, MaxSizeOfAcctVolume, "%s", sztmpBuffer);
			DBG1("at_split::sztmpBuffer #1(cont') = %s", sztmpBuffer);
			fFound = 1;
			break;
		}
#endif
		sztmpBuffer[chCount++] = *pszAcctInfo++;
	}

#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
	if (fFound == -1) 
	{
		sprintf(pAcctInfo->szTCClassID, "0");
		goto second_step;
	}
#endif

	if (fFound == 0) 
	{
#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
		pAcctInfo->szTCClassID[0] = '\0';
#endif
		return -2;
	} 

#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
	fFound = 0;
	chCount = 0;
	pszAcctInfo++;
	memset(sztmpBuffer, 0, MaxSizeOfAcctVolume);

	while(*pszAcctInfo != '\0') 
	{
		if(*pszAcctInfo == '@') 
		{
			sztmpBuffer[chCount] = '\0';
			snprintf(pAcctInfo->szTCClassID, MaxSizeOfAcctVolume, "%s", sztmpBuffer);
			DBG1("at_split::sztmpBuffer #1(cid) = %s", sztmpBuffer);
			fFound = 1;
			break;
		}
		sztmpBuffer[chCount++] = *pszAcctInfo++;
	}
	if(fFound != 1) 
	{
		return -3;
	} 

second_step:
#endif
	/* 2nd step : parse partner login id */
	fFound = 0;
	chCount = 0;
	pszAcctInfo++;
	memset(sztmpBuffer, 0, MaxSizeOfAcctUserID);
	
	while(*pszAcctInfo != '\0') 
	{
		if(*pszAcctInfo == '@') 
		{
			sztmpBuffer[chCount] = '\0';
			snprintf(pAcctInfo->szUserID, MaxSizeOfAcctUserID, "%s", sztmpBuffer);
			DBG1("at_split::sztmpBuffer #2 = %s", sztmpBuffer);
			fFound = 1;
			break;
		}
		sztmpBuffer[chCount++] = *pszAcctInfo++;
	}

	if(fFound != 1) 
	{
		sztmpBuffer[chCount] = '\0';
		snprintf(pAcctInfo->szUserID, MaxSizeOfAcctUserID, "%s", sztmpBuffer);
		DBG1("at_split::sztmpBuffer #2 = %s, #3 none", sztmpBuffer);
		pAcctInfo->szSessionID[0] = '\0';
	} 
	else 
	{
		/* 3rd step : parse session id */
		chCount = 0;
		memset(sztmpBuffer, 0, MaxSizeOfAcctSession);

		pszAcctInfo++;
		while(*pszAcctInfo != '\0') 
		{
			sztmpBuffer[chCount++] = *pszAcctInfo++;
		}
		sztmpBuffer[chCount] = '\0';
		snprintf(pAcctInfo->szSessionID, MaxSizeOfAcctSession, "%s", sztmpBuffer);
		DBG1("at_split::sztmpBuffer #3 = %s", sztmpBuffer);
	}

	return 1;
}


int check_string(char *to, const char *from, size_t length)
{
	int nCount = 0;
	char tmpBuff[length];
	
	if((to == NULL) || (from == NULL)) 
	{
		return -1;
	}
	
	while((*from != '\0') && (length > 0)) 
	{
		if(*from != '\'') 
		{
			tmpBuff[nCount++] = *from;
		}
		from++;
		length--;
	}

	tmpBuff[nCount] = '\0';
	snprintf(to, nCount + 1, "%s", tmpBuff);
	
	return 1;
}


/*************************/
/* configuration support */

dav_repos_server_conf *dav_repos_get_server_conf( server_rec *s )
{
	if( s == NULL ) 
		return NULL;
	else
		return ap_get_module_config(s->module_config, &dav_repos_module);
}

inline void dav_spin_wait(int vsleep)
{
	struct timeval tv;
	tv.tv_sec = 0;
	tv.tv_usec = vsleep;
	select  (0, NULL, NULL, NULL, &tv);
}

ap_dbd_t *dav_repos_get_db(request_rec *r)
{
	ap_dbd_t *db;

	/* loop until get acquire db session */
	if( (db = ap_dbd_open(r->server->process->pool, r->server)) != NULL )
	{
		return db;
	}

	/* can reach here? */
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "OPENDAV:dav_repos_get_db database connection pool error.");
	return NULL;
}

void dav_repos_release_db(request_rec *r, ap_dbd_t *db, int o_rv)
{
	int retval;

	{
		ap_dbd_close(r->server, db);
		retval = OK;
	}

	if (retval == !OK) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "OPENDAV: database not properly released");
	}
}


int dav_repos_begin_trans(const char *func, request_rec *r, ap_dbd_t *db, apr_dbd_transaction_t **trans)
{
	int rv;

	rv = apr_dbd_transaction_start(db->driver, r->pool, db->handle, trans);
	if (rv) 
	{
		char errbuf[1024];

		sprintf(errbuf, "%s: begin transaction", func);
		db_error_message(db, errbuf, rv);
		dav_repos_release_db(r, db, rv);
		//apr_thread_rwlock_unlock(db_lock);
		return -20;
	}

	return 0;
}


int dav_repos_end_trans(const char *func, request_rec *r, ap_dbd_t *db, apr_dbd_transaction_t *trans)
{
	int rv;

	rv = apr_dbd_transaction_end(db->driver, r->pool, trans);
	if (rv) 
	{
		char errbuf[1024];

		sprintf(errbuf, "%s: end transaction", func);
		db_error_message(db, errbuf, rv);
		dav_repos_release_db(r, db, rv);
		//apr_thread_rwlock_unlock(db_lock);
		return -21;
	}

	return 0;
}

/* Note: picked up from ap_gm_timestr_822() */
/* NOTE: buf must be at least DAV_TIMEBUF_SIZE chars in size */
void dav_repos_format_time(int style, apr_time_t sec, char *buf)
{
	apr_time_exp_t tms;

	DBG1("dav_repos_format_time:sec = %" APR_TIME_T_FMT "", sec);

	(void) apr_time_exp_gmt(&tms, sec);

	TRACE();

	if (style == DAV_STYLE_ISO8601) 
	{
		/* ### should we use "-00:00" instead of "Z" ?? */

		/* 20 chars plus null term */
		sprintf(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2dZ",
				tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday,
				tms.tm_hour, tms.tm_min, tms.tm_sec);
	} 
	else 
	{
		/* RFC 822 date format; as strftime '%a, %d %b %Y %T GMT' */
		/* 29 chars plus null term */
		sprintf(buf,
				"%s, %.2d %s %d %.2d:%.2d:%.2d GMT",
				apr_day_snames[tms.tm_wday],
				tms.tm_mday, apr_month_snames[tms.tm_mon],
				tms.tm_year + 1900, tms.tm_hour, tms.tm_min, tms.tm_sec);
	}

	DBG1("dav_repos_format_time:buf = %s", buf); 
}


const char *dav_repos_build_ns_name_key(const char *ns, const char *name, apr_pool_t * pool)
{
	TRACE();

	/* Woops */
	if (ns == NULL || name == NULL || pool == NULL)
		return NULL;

	if (strlen(ns)==0) 
		ns=" "; /* No namespace */

	return apr_psprintf(pool, "%s\t%s", ns, name);
}


void dav_repos_get_ns_name_key(const char *key, dav_prop_name * pname, apr_pool_t * pool)
{
    char *str;
    pname->ns = pname->name = NULL;

    /* Woops */
	if (key == NULL || pool == NULL)
		return;

    str = apr_pstrdup(pool, key);
    pname->ns = strtok(str, "\t");
    pname->name = strtok(NULL, "\t");
}


/* Make XML response */
dav_response *dav_repos_mkresponse(dav_repos_resource * db_r)
{
    int i;
    char *s = NULL;
    const char *ns;
    apr_text_header hdr = { 0 };
    apr_text_header hdr_ns = { 0 };
    dav_response *res = apr_pcalloc(db_r->p, sizeof(*res));
    dav_repos_property *dead_prop;
    char *ns_use = apr_pcalloc(db_r->p, DAV_REPOS_MAX_NAMESPACE);	/* Maximul namespaces */

    /* Resopnse Example
       <?xml version="1.0" encoding="utf-8"?>
       <D:multistatus xmlns:D="DAV:">
       <D:response xmlns:lp1="DAV:" xmlns:lp2="http://localhost/repos/">
       <D:href>/</D:href>
       <D:propstat>
       <D:prop>
       <lp1:resourcetype><D:collection/></lp1:resourcetype>
       <lp1:creationdate>2001-10-22T02:46:33Z</lp1:creationdate>
       <lp1:getlastmodified>Mon, 22 Oct 2001 02:46:33 GMT</lp1:getlastmodified>
       <lp1:getetag>"59272-1000-7e1c7440"</lp1:getetag>
       <D:supportedlock>
       <D:lockentry>
       <D:lockscope><D:exclusive/></D:lockscope>
       <D:locktype><D:write/></D:locktype>
       </D:lockentry>
       <D:lockentry>
       <D:lockscope><D:shared/></D:lockscope>
       <D:locktype><D:write/></D:locktype>
       </D:lockentry>
       </D:supportedlock>
       <D:lockdiscovery/>
       <D:getcontenttype>httpd/unix-directory</D:getcontenttype>
       </D:prop>
       <D:status>HTTP/1.1 200 OK</D:status>
       </D:propstat>
       </D:response>
       </D:multistatus>
     */

    /* Make fake data for test */
    res->href = db_r->m_uri;
    res->status = 200;

    TRACE();

    DBG0("dav_repos_mkresponse:");

	/* Generate namespace first */
	/* Dead properties */
	for (dead_prop = db_r->pr; dead_prop; dead_prop = dead_prop->next) 
	{
		ns_use[(int) dead_prop->m_ns_id] = 1;
	}

	/* Add namespace for dead properties */
	for (i = 0; i < DAV_REPOS_MAX_NAMESPACE; i++) 
	{
		if (ns_use[i] == 1) 
		{
			ns = dbms_get_ns(db_r, i);
			if (ns == NULL || strlen(ns) == 0) 
			{
				ns_use[i] = -1;	/* nullns */
			}
			else 
			{
				s = apr_psprintf(db_r->p, " xmlns:%s%d=\"%s\"", "ns", i, ns);
				apr_text_append(db_r->p, &hdr_ns, s);
			}
		}
	}

    /* Make XML here */

    apr_text_append(db_r->p, &hdr, "<D:propstat>" DEBUG_CR "<D:prop>" DEBUG_CR);

    /* Start prop */

	/* Dead properties */
	for (dead_prop = db_r->pr; dead_prop; dead_prop = dead_prop->next) 
	{
		/* Null ns need xmlns="" */
		if (ns_use[(int) dead_prop->m_ns_id] == -1)
		{
			s = apr_psprintf(db_r->p, "<%s xmlns=\"\">%s</%s>" DEBUG_CR,
					dead_prop->m_propname, dead_prop->m_propvalue,
					dead_prop->m_propname);
		}
		else
		{
			s = apr_psprintf(db_r->p, "<ns%ld:%s>%s</ns%ld:%s>" DEBUG_CR,
					dead_prop->m_ns_id, dead_prop->m_propname,
					dead_prop->m_propvalue,
					dead_prop->m_ns_id, dead_prop->m_propname);
		}

		apr_text_append(db_r->p, &hdr, s);
		ns_use[(int) dead_prop->m_ns_id] = 1;
	}

	/* Live properties */
	if (db_r->lpr_hash) 	/* build it before mkresponse */
	{
		long klen;
		const char *stmp;
		const char *key, *val;
		apr_hash_index_t *hindex;

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

	/* Lock discovery print, if we have */
    /* FIXME : Do we really need lock info for search */
#if 0
    apr_text_append(db_r->p, &hdr, "<lp1:lockdiscovery>" DEBUG_CR);
    if (db_r->lockdiscovery)
	apr_text_append(db_r->p, &hdr, db_r->lockdiscovery);
    apr_text_append(db_r->p, &hdr, "</lp1:lockdiscovery>" DEBUG_CR);

    /* Supported lock */
    if (db_r->supportedlock)
	apr_text_append(db_r->p, &hdr, db_r->supportedlock);
#endif

	/* Closing */
	apr_text_append(db_r->p, &hdr,
			"</D:prop>" DEBUG_CR
			"<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
			"</D:propstat>" DEBUG_CR);

	/* 
	 ** name space set up
	 ** Better way??
	 */
	s = apr_psprintf(db_r->p, " xmlns:%s%d=\"%s\"", "lp", 0, "DAV:");
	apr_text_append(db_r->p, &hdr_ns, s);

	s = apr_psprintf(db_r->p, " xmlns:%s%d=\"%s\"", "lp", 1, "DAV:");
	apr_text_append(db_r->p, &hdr_ns, s);

	res->propresult.propstats = hdr.first;
	res->propresult.xmlns = hdr_ns.first;

	return res;
}

/* Build dead property hash
 * Build the hash before makeresponse
 */
void dav_repos_build_pr_hash(dav_repos_resource * db_r)
{
	dav_repos_property *dead_prop;

	TRACE();

	/* Let's build hash */
	db_r->pr_hash = apr_hash_make(db_r->p);

	/* Dead properties */
	for (dead_prop = db_r->pr; dead_prop; dead_prop = dead_prop->next) 
	{
		const char *ns = dbms_get_ns(db_r, dead_prop->m_ns_id);
		const char *key = dav_repos_build_ns_name_key(ns, dead_prop->m_propname, db_r->p);
		apr_hash_set(db_r->pr_hash, key, APR_HASH_KEY_STRING, dead_prop);
		//DBG2("KEY: [%s]%s", key, dead_prop->m_propname);
	}

}

/* Build live property */
void dav_repos_build_lpr_hash(dav_repos_resource *db_r)
{
    const char *s;
	dav_repos_server_conf *dsc = NULL;

    /* an HTTP-date can be 29 chars plus a null term */
    /* a 64-bit size can be 20 chars plus a null term */
    char date[DAV_TIMEBUF_SIZE];;

	TRACE();

	dsc = dav_repos_get_server_conf(db_r->r->server);
	if (!dsc) 
		return;

	/* Let's build hash */
	db_r->lpr_hash = apr_hash_make(db_r->p);

	/* is directory */
	if (db_r->m_resource_type == dav_repos_COLLECTION) 
	{
		apr_hash_set(db_r->lpr_hash, "resourcetype", APR_HASH_KEY_STRING, "<D:collection/>");
	} 
	else 
	{
		apr_hash_set(db_r->lpr_hash, "resourcetype", APR_HASH_KEY_STRING, "");
	}

	s = apr_psprintf(db_r->p, "%" APR_INT64_T_FMT "", db_r->m_get_content_length);
	apr_hash_set(db_r->lpr_hash, "getcontentlength", APR_HASH_KEY_STRING, s);

	DBG1("dav_repos_build_lpr_hash:db_r->m_creation_date= %" APR_INT64_T_FMT "", db_r->m_creation_date);
	/* set live properties */
	dav_repos_format_time(DAV_STYLE_ISO8601, db_r->m_creation_date, date);
	apr_hash_set(db_r->lpr_hash, "creationdate", APR_HASH_KEY_STRING, apr_pstrdup(db_r->p, date));

	DBG1("dav_repos_build_lpr_hash:db_r->m_get_lastmodified = %" APR_INT64_T_FMT "", db_r->m_get_lastmodified);
	dav_repos_format_time(DAV_STYLE_RFC822, db_r->m_get_lastmodified, date);
	apr_hash_set(db_r->lpr_hash, "getlastmodified", APR_HASH_KEY_STRING, apr_pstrdup(db_r->p, date));

	apr_hash_set(db_r->lpr_hash, "getetag", APR_HASH_KEY_STRING, dav_repos_getetag_dbr(db_r));

	apr_hash_set(db_r->lpr_hash, "getcontenttype", APR_HASH_KEY_STRING, db_r->m_get_content_type ? db_r->m_get_content_type : "");

	DBG2("dav_repos_build_lpr_hash:db_r->m_host_name=%s dsc->host_name=%s", db_r->m_host_name, dsc->host_name);

	/* CHG 2010-07-02 huibong
	 * NetCache 상에서 PROFIND 에 대한 결과 요청시 원본 host_name 정보를 무조건 입력해서 보낼도록 수정
	 * 기존 양방향과 호환을 위해 User-Agent String 상에 NetCache 가 존재하는 경우에만 처리하도록 함.
	 * 양방향에서 현 장비에 해당 파일이 존재하는 경우 Connection 재사용을 위해 Host Name 정보를 보내지 않음.
	 */
	/*
	apr_hash_set(db_r->lpr_hash, "source", APR_HASH_KEY_STRING,
		(db_r->m_host_name && strcmp(db_r->m_host_name, dsc->host_name) ?
		apr_pstrdup(db_r->p, db_r->m_host_name) : ""));
	*/
	if( db_r->m_host_name != NULL )	// 원본 파일에 대한 Host Name 정보가 존재하는 경우
	{
		// 원본 Host Name 과 현재 장비의 Host Name 이 틀린 경우 => Host Name 정보 전달.
		if( strcmp(db_r->m_host_name, dsc->host_name) != 0 )
		{
			apr_hash_set(db_r->lpr_hash, "source", APR_HASH_KEY_STRING, apr_pstrdup(db_r->p, db_r->m_host_name));
		}
		else
		{
			// NetCache Agent 인지를 확인하기 위해 PROFPIND Header 의 User-Agent 정보 검사.
			const char * userAgent = NULL;
			userAgent = apr_table_get( db_r->r->headers_in, "User-Agent");

			if( userAgent != NULL && (strncmp(userAgent, "NetCache", 8)==0) )
			{
				// User-Agent 정보가 NetCache 로 시작하는 경우 -> 무조건 Host Name 정보를 입력하여 전달.
				//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,"dav_repos_build_lpr_hash: User-Agent[%s] source[%s]", userAgent, db_r->m_host_name);
				apr_hash_set(db_r->lpr_hash, "source", APR_HASH_KEY_STRING, apr_pstrdup(db_r->p, db_r->m_host_name));
			}
			else
			{
				// 기존 양방향 Client 인 경우 Host Name 정보와 현재 장비가 동일한 하면 => 빈 문자열 전달.
				apr_hash_set(db_r->lpr_hash, "source", APR_HASH_KEY_STRING, "");
			}
		}
	}
	else
	{
		// 원본 파일에 대한 Host Name 정보가 존재하지 않는 경우 빈값을 전달처리
		apr_hash_set(db_r->lpr_hash, "source", APR_HASH_KEY_STRING, "");
	}

	// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
	apr_hash_set(db_r->lpr_hash, "getcontentlanguage", APR_HASH_KEY_STRING, "");

}

/*# FIXME : This is a insert lock info for search and prop.
** Do we really need a lock info when we search ?
** Then we should make params in search semewhere.
** 
** Need to move find_live and live props
*/
dav_error *dav_repos_insert_lock_prop(const dav_walk_params * params, dav_repos_resource * db_r)
{
	dav_error *err;
	dav_resource *resource = NULL;

	//dav_walker_ctx *ctx = params->walk_ctx;

	TRACE();

	/* Initilize */
	// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)

	if (params->lockdb != NULL) 
	{
		dav_lock *locks = NULL;

		/*
		 * FIXME : We don't have a resource here
		 * Our lock hook will use only uri and exist filed in resource
		 */
		resource = apr_pcalloc(db_r->p, sizeof(*resource));
		resource->exists = 1;
		resource->uri = db_r->m_fake_uri;

		if ((err = dav_lock_query(params->lockdb, resource, &locks)) != NULL) 
		{
			return dav_push_error(db_r->p, err->status, 0,
					"DAV:lockdiscovery could not be "
					"determined due to a problem fetching "
					"the locks for this resource.", err);
		}

		/* fast-path the no-locks case */
		if (locks) 
		{
			/*
			 ** This may modify the buffer. value may point to
			 ** wb_lock.pbuf or a string constant.
			 */
			// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
			//db_r->m_lock_discovery = dav_lock_get_activelock(ctx->r, locks, NULL);

			/* Do we need strdup here */
			/* db_r->lockdiscovery = apr_pstrdup(db_r->p, wb_lock.buf); */
		}

		/* 
		 * Get supported lock info 
		 * Our lock hook will not use resource though.
		 */
		// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
	}

	return NULL;
}

/* make sure the pathname does not have a trailing "/" */
void dav_repos_no_trail(char *str)
{
	int len = strlen(str);
	while (len > 1 && str[len - 1] == '/') 
	{
		str[len - 1] = '\0';
		len = strlen(str);
	}

	TRACE();
}


int dav_repos_remove_unsafe_chars(const char *pOld, char *pNew)
{
	int nCount = 0;

	if((pOld == NULL) || (pNew == NULL)) 
	{
		return -1;
	}

	while(*pOld != '\0') 
	{
		if((*pOld == '\'') || (*pOld == '\\')) 
		{
			pOld++;
		} 
		else 
		{
			pNew[nCount++] = *pOld++;
		}
	}

	pNew[nCount] = '\0';
	return 1;
}


int dav_repos_replace_unsafe_chars(const char *pOld, char *pNew)
{
	if ((pOld == NULL) || (pNew == NULL)) 
	{
		return -1;
	}

	char const * const pattern = "'";
	char const * const replacement = "''";

	size_t const replen = strlen(replacement);
	size_t const patlen = strlen(pattern);

	const char * oriptr;
	const char * patloc;

	memset(pNew, 0x00, sizeof(*pNew));
	for (oriptr = pOld; (patloc = strstr(oriptr, pattern)); oriptr = patloc + patlen)
	{
		size_t const skplen = patloc - oriptr;
		// copy the section until the occurence of the pattern
		strncpy(pNew, oriptr, skplen);
		pNew += skplen;
		// copy the replacement 
		strncpy(pNew, replacement, replen);
		pNew += replen;
	}

	// copy the rest of the string.
	strcpy(pNew, oriptr);

	return 1;
}


int dav_repos_restore_unsafe_chars(const char *pOld, char *pNew)
{

	if ((pOld == NULL) || (pNew == NULL)) 
	{
		return -1;
	}

	char const * const pattern = "''";
	char const * const replacement = "'";

	size_t const replen = strlen(replacement);
	size_t const patlen = strlen(pattern);

	const char * oriptr;
	const char * patloc;

	memset(pNew, 0x00, sizeof(*pNew));
	for (oriptr = pOld; (patloc = strstr(oriptr, pattern)); oriptr = patloc + patlen)
	{
		size_t const skplen = patloc - oriptr;
		// copy the section until the occurence of the pattern
		strncpy(pNew, oriptr, skplen);
		pNew += skplen;
		// copy the replacement 
		strncpy(pNew, replacement, replen);
		pNew += replen;
	}

	// copy the rest of the string.
	strcpy(pNew, oriptr);

	return 1;
}


int dav_repos_replace_multi_slash_chars(const char *pOld, char *pNew)
{
	int nCount = 0;
	int slash_detect = 0;

	if ((pOld == NULL) || (pNew == NULL)) 
	{
		return -1;
	}

	while (*pOld != '\0') 
	{
		if (*pOld == '/') 
		{
			if (slash_detect == 0)
			{
				slash_detect = 1;
			}
			else 
			{
				pOld++;
				continue;
			}
		} 
		else
		{
			slash_detect = 0;
		}

		pNew[nCount++] = *pOld++;
	}
	
	pNew[nCount] = '\0';
	return 1;
}


#ifdef __OPENDISK_SOLUTION__
int dav_repos_change_resource_type(int resource_type)
{
	if (resource_type >= dav_repos_RECYCLE) 
	{
		return (resource_type - dav_repos_RECYCLE);
	} 
	else if (resource_type >= dav_repos_SHARE) 
	{
		return (resource_type - dav_repos_SHARE);
	}

	return resource_type;
}
#endif


#define PRIVATE_KEY		"gE\x8bk\xc6\x23{2i\x98<dsH3f"


//static void __tea_encrypt(unsigned int *v, unsigned int *k)
//{
//	unsigned int v0 = v[0], v1 = v[1], sum = 0, i;   /* set up */
//	unsigned int delta = 0x9e3779b9;              /* a key schedule constant */
//	unsigned int k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];   /* cache key */

//	for (i = 0; i < 32; i++) {                        /* basic cycle start */
//		sum += delta;
//		v0 += (v1 << 4) + (k0 ^ v1) + (sum ^ (v1 >> 5)) + k1;
//		v1 += (v0 << 4) + (k2 ^ v0) + (sum ^ (v0 >> 5)) + k3;  /* end cycle */
//	}

//	v[0] = v0; v[1] = v1;
//}

static void __tea_decrypt(unsigned int *v, unsigned int *k)
{
	unsigned int v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i;  /* set up */
	unsigned int delta = 0x9e3779b9;              /* a key schedule constant */
	unsigned int k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];   /* cache key */

	for (i = 0; i < 32; i++) {                        /* basic cycle start */
		v1 -= (v0 << 4) + (k2 ^ v0) + (sum ^ (v0 >> 5)) + k3;
		v0 -= (v1 << 4) + (k0 ^ v1) + (sum ^ (v1 >> 5)) + k1;
		sum -= delta;                                 /* end cycle */
	}

	v[0] = v0; v[1] = v1;
}

/*
static void tea_encrypt(char *buf, int len)
{
	int __len = (len + 7) >> 3;
	int i;
	//char *key = PRIVATE_KEY;

	for (i = 0; i < __len; i++) {
		__tea_encrypt((((unsigned int *)buf) + (i << 1)), (unsigned int *)PRIVATE_KEY);
	}
}
*/

void tea_decrypt(char *buf, int len)
{
	int __len = (len + 7) >> 3;
	int i;

	for (i = 0; i < __len; i++) {
		__tea_decrypt((((unsigned int *)buf) + (i << 1)), (unsigned int *)PRIVATE_KEY);
	}
}



int file_trylock(int fd)
{
	struct flock _lock;

	memset( &_lock, 0, sizeof(struct flock) );
	
	_lock.l_type = F_WRLCK;
	_lock.l_start = 0;
	_lock.l_whence = SEEK_SET;
	_lock.l_len = 0;

	return fcntl(fd, F_SETLK, &_lock);
}


int file_unlock(int fd)
{
	struct flock _lock;
	
	memset( &_lock, 0, sizeof(struct flock) );
	
	_lock.l_type = F_UNLCK;
	_lock.l_start = 0;
	_lock.l_whence = SEEK_SET;
	_lock.l_len = 0;

	return fcntl(fd, F_SETLK, &_lock);
}

/* apache version 업에 따른 apr_dbd_get_row 처리 방법 변경 */
int solbox_apr_dbd_get_row(const apr_dbd_driver_t *driver, apr_pool_t *pool,
	apr_dbd_results_t *res, apr_dbd_row_t **row, int rownum)
{
	DBG1("solbox_apr_dbd_get_row #1 = %s", APU_VERSION_STRING);
	/* apu vsrsion 1.3.0 부터 rowunm counting 1부터 시작*/
	if(rownum >= 0 && strcmp(APU_VERSION_STRING, "1.3.0") >= 0)
	{
		return apr_dbd_get_row(driver, pool, res, row, rownum+1);
	}
	else
	{
		return apr_dbd_get_row(driver, pool, res, row, rownum);
	}

	return -1;
}

dav_error* solbox_dav_new_error(apr_pool_t *p, int status,
	int error_id, const char *desc)
{
	

#if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER >= 3
	return dav_new_error(p, status, error_id, 0, desc);
#else // 2.2.x
	return dav_new_error(p, status, error_id, desc);
#endif // 2.3 over

}

const char *dav_http_scheme(const request_rec *r)
{
	/*
	* The http module shouldn't return anything other than
	* "http" (the default) or "https".
	*/
	if (r->server->server_scheme &&
		(strcmp(r->server->server_scheme, "https") == 0))
		return "https";

	return "http";
}
