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

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

   $Id: auth.c,v 1.4 2007/03/19 08:43:11 elenoa Exp $

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

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

#include <ctype.h>
#include <mcrypt.h>

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

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

#include "dav_repos.h"
#include "dbms.h"
#include "util.h"
#include "share_common.h"
#include "share_account.h"
#include "share_anonymous.h"


// 2018-01-25 huibong 토토지원 과금 IP 확인용 임시 코드 , inet_ntop()
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>



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

int dav_auth_user(request_rec *r, apr_off_t content, apr_off_t *permitted);


/* Elenoa 2005.12.28 : SP-Authrization START */
/* Copy and Modify : ap_get_basic_auth_pw from server/protocol.c */

/* Cannot use ap_pbase64decode. remake ap_get_basic_auth_pw */
static int dav_auth_get_pw_str(request_rec *r, const char **pw)
{
	const char *auth_line = apr_table_get(r->headers_in,
										  (PROXYREQ_PROXY == r->proxyreq)
											  ? "Proxy-Authorization"
											  : "Authorization");
	const char *t;

#ifdef __OPENDAV_DEBUG_AUTH__
	DBG1("dav_auth_get_pw_str: authline: %s", auth_line);
#endif

	if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic"))
		return DECLINED;

	if (!ap_auth_name(r)) {
		ap_log_error(APLOG_MARK, APLOG_ERR,
					  0, NULL, "need AuthName: %s", r->uri);
		return HTTP_INTERNAL_SERVER_ERROR;
	}

	if (!auth_line) {
		ap_note_basic_auth_failure(r);
		return HTTP_UNAUTHORIZED;
	}

	if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
		/* Client tried to authenticate using wrong auth scheme */
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
					  "client used wrong authentication scheme: %s", r->uri);
		ap_note_basic_auth_failure(r);
		return HTTP_UNAUTHORIZED;
	}

	while (*auth_line == ' ' || *auth_line == '\t') {
		auth_line++;
	}

	*pw = auth_line;

	return OK;
}

#ifdef __OPENDAV_DEBUG__
void binary_dump(char *szBin, int len)
{
	int i;
	char outbuf[1024];
	outbuf[0] = '\0';

	for (i = 0; i < len; i++) {
		sprintf(outbuf, "%s %2X", outbuf, (unsigned char)(*((unsigned char *)szBin + i)));

		if (((i + 1) % 16) == 0) {
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s", outbuf);
			outbuf[0] = '\0';
		}
	}
	if (strlen(outbuf) > 0) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s", outbuf);
	}
}
#endif

/* OLD Authorization */
static int dav_auth_sp_old(request_rec *r, char *pszPW, int len, struct account_cache *pac)
{
	char *pszResult = NULL;
	char *pszCUser = NULL;
	char *pszCExpire = NULL;
	char *pszCPW = NULL;
	char *p1, *p2, *p3;
	MCRYPT mcrypt = NULL;
	time_t now = time(0);
	char *pszKey;
	char *pszIV;
	char *pszDBPW;

	if (!pszPW || !pac)
		return !OK;

	pszKey = pac->pk;
	pszIV = pac->iv;
	pszDBPW = pac->pass;

#ifdef __OPENDAV_DEBUG_AUTH__
	DBG3("dav_auth_sp_old::pszPW=%s (%d) pszKey=%s",
			pszPW, len, pszKey);
	DBG1("dav_auth_sp_old::pszIV=%s", pszIV);
#endif
	//apr_global_mutex_lock(db_global_lock);
	mcrypt = mcrypt_module_open(MCRYPT_BLOWFISH, NULL, MCRYPT_CBC, NULL);
	if (mcrypt == MCRYPT_FAILED) {
#ifdef __OPENDAV_OLDAUTH_LOG__
		ap_log_error(APLOG_MARK, APLOG_ERR,
				0, NULL, "dav_auth_sp_old: mcrypt open failed");
#endif
		//apr_global_mutex_unlock(db_global_lock);
		return !OK;
	}
	if (mcrypt_generic_init(mcrypt, pszKey, KEY_LENGTH, pszIV) < 0) {
#ifdef __OPENDAV_OLDAUTH_LOG__
		ap_log_error(APLOG_MARK, APLOG_ERR,
				0, NULL, "dav_auth_sp_old: mcrypt init failed");
#endif
		//apr_global_mutex_unlock(db_global_lock);
		return !OK;
	}
	if (mdecrypt_generic(mcrypt, pszPW, len) < 0) {
#ifdef __OPENDAV_OLDAUTH_LOG__
		ap_log_error(APLOG_MARK, APLOG_ERR,
				0, NULL, "dav_auth_sp_old: mdecrypt failed");
#endif
		//apr_global_mutex_unlock(db_global_lock);
		return !OK;
	}
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG1("dav_auth_sp_old::decrypted pszPW=%s", pszPW);
	//DBG1("dav_auth_sp_old::TEST %s", get_hashed_filepath("sp"));
#endif

	mcrypt_generic_deinit(mcrypt);
	//apr_global_mutex_unlock(db_global_lock);

	/* Elenoa : 2006. 4. 6 :
	 * fix problem '-' char in userid and product name */

	/*
	   pszCUser = ap_getword_nulls_nc (r->pool, &pszPW, '-');
	   pszCPW = ap_getword_nulls_nc (r->pool, &pszPW, '-');
	   pszCExpire = pszPW;
	*/

	p1 = strchr(pszPW, '@');
	p2 = strrchr(pszPW, '-');

	if (!p1 || !p2) {
		return !OK;
	}
	p1++;
	p3 = p2 - 1;
	while (p3 > p1 && *p3 != '-') p3--;

	if (p3 == p1) {
#ifdef __OPENDAV_OLDAUTH_LOG__
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_sp_old: password format not matched: case 2");
#endif
		return !OK;
	}

	pszCUser = apr_pstrndup(r->pool, pszPW, (p3 - pszPW));
	p3++;
	pszCPW = apr_pstrndup(r->pool, p3, (p2 - p3));
	pszCExpire = p2 + 1;

#ifdef __OPENDAV_DEBUG_AUTH__
	DBG2("dav_auth_sp_old::now=%d expire=%s", now, pszCExpire);
#endif
	if (atoi(pszCExpire) < now) {
#ifdef __OPENDAV_OLDAUTH_LOG__
		if (atoi(pszCExpire) != 0) {
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
					"dav_auth_sp_old: timeout: server %d client %s",
					now, pszCExpire);
		}
#endif

		return !OK;
	}
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG1("dav_auth_sp_old::pszPW %s", pszPW);
	DBG3("dav_auth_sp_old::pszCUser %s pszCPW %s pszCExpire %s",
			pszCUser, pszCPW, pszCExpire);
#endif

	pszResult = apr_pstrcat(r->pool, pszCUser, pszDBPW, pszCExpire, NULL);

	// CHG 2010-08-10 huibong
	// get_hashed_filepath 함수정의 변경에 따른 코드 수정
	char szHashResult[APR_MD5_DIGESTSIZE * 2 + 1];
	if( get_hashed_filepath( pszResult, szHashResult, sizeof(szHashResult) ) == false )
	{
		// Clear array
		memset( szHashResult, 0x00, sizeof(szHashResult));

		// Write error log
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_auth_sp_old:get_hashed_filepath return false.[%s][%s][%zu]"
				, pszResult, szHashResult, sizeof(szHashResult));
	}

#ifdef __OPENDAV_DEBUG_AUTH__
	//DBG3("dav_auth_sp_old::pszResult=%s,hash=%s,pszCPW=%s", pszResult, get_hashed_filepath(pszResult), pszCPW);
	DBG3("dav_auth_sp_old::pszResult=%s,hash=%s,pszCPW=%s", pszResult, szHashResult, pszCPW);
#endif

	//if (strcmp(get_hashed_filepath(pszResult), pszCPW)) 
	if (strcmp(szHashResult, pszCPW)) 
	{

#ifdef __OPENDAV_OLDAUTH_LOG__
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_auth_sp_old: auth string failed: server %s client %s", szHashResult, pszCPW);
#endif

		return !OK;
	}

	return OK;
}


//pac->pk, pac->iv, pac->pass
/* NEW Authorization */
static int dav_auth_sp_new(request_rec *r, char *pszPW, int len, struct account_cache *pac)
{
	char *pszResult = NULL;
	char *pszCUser = NULL;
	char *pszCExpire = NULL;
	char *pszCPW = NULL;
	char *p1, *p2, *p3;
	MCRYPT mcrypt = NULL;
	time_t now = time(0);
	time_t csdk_time = 0;
	char hashed_uri[16];
	char hashed_session_dump[33];

	// CHG 2011-12-01 huibong
	// apr_md5() 함수 형식에 맞도록 변수 타입 수정 처리
	//char hashed_current_uri[16], *escaped_request;
	unsigned char hashed_current_uri[16];
	char *escaped_request;
	
	int i;
	char *pszKey;
	char *pszIV;
	char *pszDBPW;

	//if (!pszPW || !pszKey || !pszIV || !pszDBPW)
	if (!pszPW || !pac)
		return !OK;

	pszKey = pac->pk;
	pszIV = pac->iv;
	pszDBPW = pac->pass;

#ifdef __OPENDAV_DEBUG_AUTH__
	DBG3("dav_auth_sp_new::pszPW=%s (%d) pszKey=%s",
		 pszPW, len, pszKey);
	DBG1("dav_auth_sp_new::pszIV=%s", pszIV);
#endif

	/* compare length. (timestamp, session, uri) */
	if (len <= (4 + 16 + 16)) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_sp_new: length too small (< 36)");
		return !OK;
	}

	/* Elenoa: 2006. 7. 11: Add New Authrization START */
	/* decrypt tea */
	tea_decrypt(pszPW, len);
#ifdef __OPENDAV_DEBUG__
	binary_dump(pszPW, len);
#endif
	/* get time */
	csdk_time = (time_t)*((int *)pszPW);
	pszPW += 4;

	/* compare timestamp (from CSDK) */
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG2("dav_auth_sp_new::now=%d expire(CSDK)=%d", now, csdk_time);
#endif

#define CSDK_TIMEOUT	3600

	if ((csdk_time + CSDK_TIMEOUT) < now ||
		(csdk_time - CSDK_TIMEOUT) > now) {

		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_sp_new: timeout (csdk): server %d client %d",
				(int)now, (int)csdk_time);
		return !OK;
	}

	/* get session (hashed) */
	for (i = 0; i < 16; i++) {
		sprintf((hashed_session_dump + (i << 1)), "%02x", *((unsigned char *)(pszPW + i)));
	}
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG1("dav_auth_sp_new::session %s", hashed_session_dump);
#endif
	pszPW += 16;

	/* get uri (hashed) */
	memcpy(hashed_uri, pszPW, 16);
	pszPW += 16;

	/* compare md5'd file path */
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG2("dav_auth_sp_new::the_request=%s uri=%s", r->the_request, r->uri);
#endif
	{
		const char *ll = r->the_request;

		// CHG 2019-08-28 huibong gcc compile warning 수정 (#32780)
		ap_getword_white(r->pool, &ll); // method

		char *uri = ap_getword_white(r->pool, &ll); // uri
		escaped_request = apr_pstrdup(r->pool, uri);
	}
	ap_unescape_url(escaped_request);
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG1("dav_auth_sp_new::escaped_request=%s", escaped_request);
#endif
	apr_md5(hashed_current_uri, (const unsigned char *)escaped_request, strlen(escaped_request));
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG3("dav_auth_sp_new::hashed_uri=%x inputed hash=%x uri=%s",
			*((int *)hashed_current_uri), *((int *)hashed_uri), escaped_request);
#endif

	if (memcmp(hashed_current_uri, hashed_uri, 16)) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_sp_new: uri not match: server %s client %s",
				hashed_current_uri, hashed_uri);

		return !OK;
	}

	/* before decrypt mcrypt-blowfish, decrypt base64 first. */
	{
		char *decoded;
		int dec_len;
		char *p;

#ifdef __OPENDAV_DEBUG_AUTH__
		DBG1("dav_auth_sp_new::pszPW=%s", pszPW);
#ifdef __OPENDAV_DEBUG__
		binary_dump(pszPW, strlen(pszPW));
#endif
#endif

		decoded = (char *) apr_palloc(r->pool, 8 + (dec_len = apr_base64_decode_len(pszPW)));
		memset(decoded, 0, dec_len + 7);
		dec_len = apr_base64_decode(decoded, pszPW);

		pszPW = decoded;
		len = dec_len;

		/* skip til ':' char */
#ifdef __OPENDAV_DEBUG_AUTH__
		DBG2("dav_auth_sp_new::before skip pszPW=%s(%d)", pszPW, len);
#endif
		if ((p = strchr(pszPW, ':'))) {
			p++;
			len -= (p - pszPW); pszPW = p;
		}
#ifdef __OPENDAV_DEBUG_AUTH__
		DBG2("dav_auth_sp_new::before decrypt pszPW=%s(%d)", pszPW, len);
#endif
	}
	/* Elenoa: 2006. 7. 11: Add New Authrization END */

	/* decrypt mcrypt-blowfish */
	//apr_global_mutex_lock(db_global_lock);
	mcrypt = mcrypt_module_open(MCRYPT_BLOWFISH, NULL, MCRYPT_CBC, NULL);
	if (mcrypt == MCRYPT_FAILED) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_sp_new: mcrypt open failed");
		//apr_global_mutex_unlock(db_global_lock);
		return !OK;
	}
	if (mcrypt_generic_init(mcrypt, pszKey, KEY_LENGTH, pszIV) < 0) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_sp_new: mcrypt init failed");
		//apr_global_mutex_unlock(db_global_lock);
		return !OK;
	}
	if (mdecrypt_generic(mcrypt, pszPW, len) < 0) {
		ap_log_error(APLOG_MARK, APLOG_ERR,
				0, NULL, "dav_auth_sp_new: mdecrypt failed");
		//apr_global_mutex_unlock(db_global_lock);
		return !OK;
	}

#ifdef __OPENDAV_DEBUG_AUTH__
	DBG1("dav_auth_sp_new::decrypted pszPW=%s", pszPW);
	//DBG1("dav_auth_sp_new::TEST %s", get_hashed_filepath("sp"));
#endif

	mcrypt_generic_deinit(mcrypt);
	//apr_global_mutex_unlock(db_global_lock);

	/* Elenoa : 2006. 4. 6 :
	 * fix problem '-' char in userid and product name */

	/* parse user id, user pass, timestamp */
	p1 = strchr(pszPW, '@');
	p2 = strrchr(pszPW, '-');

	if (!p1 || !p2) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_sp_new: password format not matched: case 1");
		return !OK;
	}

	p1++;
	p3 = p2 - 1;
	while (p3 > p1 && *p3 != '-') p3--;

	if (p3 == p1) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_sp_new: password format not matched: case 2");
		return !OK;
	}

	pszCUser = apr_pstrndup(r->pool, pszPW, (p3 - pszPW));
	p3++;
	pszCPW = apr_pstrndup(r->pool, p3, (p2 - p3));
	pszCExpire = p2 + 1;
	
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG2("dav_auth_sp_new::now=%d expire=%s", now, pszCExpire);
#endif

	/* compare expire date (from SSDK) */
	if (atoi(pszCExpire) < now) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_sp_new: timeout: server %d client %s",
				(int)now, pszCExpire);

		return !OK;
	}

#ifdef __OPENDAV_DEBUG_AUTH__
	DBG1("dav_auth_sp_new::pszPW %s", pszPW);
	DBG3("dav_auth_sp_new::pszCUser %s pszCPW %s pszCExpire %s",
		pszCUser, pszCPW, pszCExpire);
#endif

	pszResult = apr_pstrcat(r->pool, pszCUser, pszDBPW, pszCExpire, NULL);

	// CHG 2010-08-10 huibong
	// get_hashed_filepath 함수 정의 변경에 따른 코드 수정
	char szHashResult[APR_MD5_DIGESTSIZE * 2 + 1];
	if( get_hashed_filepath( pszResult, szHashResult, sizeof(szHashResult) ) == false )
	{
		// Clear array
		memset( szHashResult, 0x00, sizeof(szHashResult));

		// Write error log
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_auth_sp_new:get_hashed_filepath return false.[%s][%s][%zu]"
				, pszResult, szHashResult, sizeof(szHashResult));
	}

#ifdef __OPENDAV_DEBUG_AUTH__
	DBG3("dav_auth_sp_new::pszResult=%s,hash=%s,pszCPW=%s", pszResult, szHashResult, pszCPW);
#endif

	/* compare hashed password */
	if (strcmp(szHashResult, pszCPW)) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_auth_sp_new: auth string failed: server %s client %s"
				, szHashResult, pszCPW);

		return !OK;
	}

	/* New Format */
	r->user = apr_pstrcat(r->pool, r->user, "@", hashed_session_dump, NULL);

	return OK;
}
/* Elenoa 2005.12.28 : SP-Authrization END */


static int dav_auth_check_basic_user(request_rec * r, char *pRcvdPasswd, int len, char *pszSafePasswd, struct account_cache *pac)
{
	/* make user data */
	r->user = apr_pstrcat(r->pool, pac->tran_id, "@", pac->seq, NULL);

#ifdef __OPENDAV_DEBUG_AUTH__
	DBG1("dav_auth_check_basic_user::DB pass=%s", pac->pass);
#endif

	/* DAV-Authrization */
	if (strcmp(pszSafePasswd, pac->pass)) {
		/* Elenoa 2005.12.28 : SP-Authrization START */
		/* login failed */
		char *pRcvdPasswdNew = apr_pcalloc(r->pool, len + 5);

#ifdef __OPENDAV_DEBUG_AUTH__
		DBG2("dav_auth_check_basic_user:: padd IV:%s Key:%s", pac->iv, pac->pk);
#endif

		/* copy a new set of inputed password string */
		memcpy(pRcvdPasswdNew, pRcvdPasswd, len);
		*(pRcvdPasswdNew + len) = '\0';

		if (
#ifdef __USE_OLD_AUTH__
			dav_auth_sp_old(r, pRcvdPasswdNew, len, pac) != OK
#else
			(r->method_number == M_PUT &&
			dav_auth_sp_old(r, pRcvdPasswdNew, len, pac) != OK) ||
			r->method_number != M_PUT
#endif
			) {

			/* copy a new set of inputed password string */
			memcpy(pRcvdPasswdNew, pRcvdPasswd, len);
			*(pRcvdPasswdNew + len) = '\0';

			if (dav_auth_sp_new(r, pRcvdPasswdNew, len, pac) != OK) {
				//ap_note_basic_auth_failure(r);
				return HTTP_UNAUTHORIZED;
			}

			/* Elenoa: 2007. 2. 1: not here */
#if 0
			/* Elenoa: 2007. 1. 25: Add 'User Authorization' */
			/* Here we check, if register SP's authorization server */
#ifdef __OPENDAV_DEBUG_USER_AUTH__
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER C (%d) %s", getpid(), pac->uri);
#endif
			if (pac->uri[0] != '\0' && (retval = dav_auth_user(r, pac)) != OK) {
				if (retval == HTTP_UNAUTHORIZED) {
					ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
							"OPENDAV: user auth failed: (%s:%s)",
							pac->seq, pac->tran_id);
					return HTTP_PRECONDITION_FAILED;
				}
			}
#endif
		}
		/* Elenoa 2005.12.28 : SP-Authrization END */
	}

	return OK;
}


static int dav_auth_get_account_cache(request_rec *r, char *szVolumeID, struct account_cache *ac)
{

	struct account_cache *pac;
	memset((char *)ac, 0, sizeof(struct account_cache));

	if ((pac = dav_shared_ac_get_by_vol(szVolumeID))) {
		memcpy(ac, pac, sizeof(struct account_cache));
		return 0;
	}

	// 201.07.15 dadamin
	// 서비스 정보 없을 시 수행되던 cache 갱신 로직 삭제처리함
	// 공유 메모리 쓰기 단일화(fimngd 수행) 위함
	
	return -1;
}

#ifdef __CLOUD_STREAMING__
// 2012.12.18 dadamin
// Axissoft StarPlayer 인증 함수
bool check_starplayer_auth( request_rec * r )
{
	// header Accept check
	// 2013-08-05 : 해당 헤더 체크 기능 null 문자(값없음)까지 허용, "Accept:" 와 같은 형식
	//              모마일(아이폰, 안드로이폰) 지원 시 해당 헤더 제거 할 수 없으므로 
	//              관련 헤더 null 문자(값없음)까지만 허용하기함
	//              이는 Axissoft 측과 협의된 상태임
	// 스타플레이어에서는 ACCEPT정보를 보내지 않으므로 서버변수 accept가 있으면 
	// 스타플레이어 외 다른 방식으로 접근한 케이스이므로 차단
	char *acceptstr = NULL;
	acceptstr = (char *)apr_table_get(r->headers_in, "Accept");
	if(acceptstr && strlen(acceptstr) != 0)
	{
		//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, 
		//	"OPENDAV::check_starplayer_auth fail(Accept header field exist).[%s]",r->unparsed_uri);
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, 
			"OPENDAV::check_starplayer_auth fail(Accept header field(%s)).[%s]"
			, acceptstr, r->unparsed_uri);
		return false;
	}

	// HTTP head 요청 경우 : Range, X-Streaming-Checksum 존재하지 않으므로 인증 통과
	if(r->method_number == M_GET && r->header_only)
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, 
			"OPENDAV::check_starplayer_auth HEAD request successed.[%s]",r->unparsed_uri);
		return true;
	}

	// checksum  
	//a = range 문자열 예) "bytes=0-1023"
	//b = checksum     예) 387
	unsigned int checksum = 0;
	const char* p = NULL;

	p = apr_table_get(r->headers_in, "Range");
	if(p == NULL)
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, 
			"OPENDAV::check_starplayer_auth fail(Range not found).[%s]"
			, r->unparsed_uri);
		return false;
	}
	
	while (*p != 0) 
	{
		checksum += *p++;
	}

	char buf[16];
	int i = 0;

	while (checksum && i < sizeof(buf)) 
	{
		char n = checksum & 0xf;
		if (n < 10)
			buf[i++] = n + '0';
		else
			buf[i++] = (n - 10) + 'a';
		checksum >>= 4;
	}
	
	p = apr_table_get(r->headers_in, "X-Streaming-Checksum");
	if(p == NULL)
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, 
			"OPENDAV::check_starplayer_auth fail(X-Streaming-Checksum not found).[%s]"
			, r->unparsed_uri);
		return false;
	}
	while (i && p) 
	{
		if (buf[--i] != *p++)
			return false;
	}

	return (*p == 0 && i == 0);
}

int check_cloud_streaming_auth(request_rec * r)
{
	const char * userAgent = NULL;

	// 2012.12.18 dadamin
	// Axissoft StarPlayer 인증을 처리 추가(양방향은 무인증 처리함)
	userAgent = apr_table_get( r->headers_in, "User-Agent");
	if(userAgent != NULL && strcmp(userAgent, "StarPlayer/1.0") == 0)
	{
		// Axissoft StarPlayer 인증
		if( check_starplayer_auth(r) == false)
		{
			// Axissoft StarPlayer 인증 실패
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, 
				"OPENDAV::check_starplayer_auth fail.[%s]",r->unparsed_uri);

			return HTTP_UNAUTHORIZED;
		}
		else
		{			
			// 무인증 처리를 위하여 서비스 name 설정
			const char * xstreamdomain = NULL;
			xstreamdomain = apr_table_get( r->headers_in, "X-Streaming-Domain");

			if( xstreamdomain != NULL )
			{
				const char * p1 = NULL;
				char * svc_name = NULL;
				int svc_name_len = 0;

				p1 = strchr( xstreamdomain, '.' );

				if( p1 != NULL )
				{
					// 서비스 name 정보로 사용한다.
					svc_name_len = (p1 - xstreamdomain);

					svc_name = apr_pstrdup(r->pool, xstreamdomain);
					*( svc_name + svc_name_len )  = '\0';

					if(svc_name == NULL || strlen(svc_name) == 0)
					{
						ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, 
							"OPENDAV::check_cloud_streaming_auth: svc_name of X-Streaming-Domain is NULL.[%s][%s]",
							xstreamdomain, r->unparsed_uri);
					}
					else 
					{
						apr_table_setn(r->headers_in, "RedHost",  svc_name );

						// 2013.01.23 dadamin
						// redirect된 경우에 대한 처리 
						// uri 규칙 : /dav/서비스명/파일path
						// 이후 로직에서 정상 처리를 위해서 서비스명이 제거되어야함
						ap_unescape_url( r->unparsed_uri );
						if (strstr(r->unparsed_uri , "/dav/") == r->unparsed_uri)
						{
							if (strstr((r->unparsed_uri+5) , svc_name) == (r->unparsed_uri +5))
							{
								char * new_uri = NULL;
								new_uri =  r->unparsed_uri+ 5 + strlen(svc_name);
								new_uri = apr_pstrcat(r->pool, "/dav", new_uri, NULL);
								ap_parse_uri( r, new_uri );
								ap_getparents( r->uri );
							}
						}
						//is_anonymous = true;
						return HTTP_NON_AUTHORITATIVE;
					}
				}
				else
				{
					//is_anonymous = false;
					ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, 
						"OPENDAV::check_cloud_streaming_auth: X-Streaming-Domain format is wrong [%s] [%s]",
						xstreamdomain, r->unparsed_uri );
				}
			}
			else
			{
				//is_anonymous = false;
				ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, 
					"OPENDAV::check_cloud_streaming_auth: X-Streaming-Domain not found [%s]", 
					r->unparsed_uri );
			}
		}
	}

	return HTTP_OK;
}

#endif // __CLOUD_STREAMING__

int dav_auth_authenticate(request_rec * r)
{
	char *pszSafeUser = NULL;
	char *pRcvdPasswd = NULL;
	char *pszSafePasswd = NULL;
	acct_info stAi;
	int len, result;
	struct account_cache ac;
	apr_status_t rv;
	//int is_user_auth = FALSE, is_band_ctrl = FALSE;
	bool is_anonymous = false;

#ifdef __CLOUD_STREAMING__
	int a = check_cloud_streaming_auth(r) ;
	switch (a)
	{
	case HTTP_UNAUTHORIZED:
		is_anonymous = false;
		return HTTP_UNAUTHORIZED;
		break;
	case HTTP_NON_AUTHORITATIVE:
		is_anonymous = true;
		break;
	default:
		is_anonymous = false;
		break;
	}
#endif // __CLOUD_STREAMING__

	// 2013.01.15 dadmain
	// Axissoft StarPlayer 인증 실패 되어도 해당 요청이 무인증 디렉토리 경우 
	// 인증 처리 되어야 하기 때문에 기존 dav_shared_ar_get_value 함수 호출 될 수 있게 한다.
	/* First, check anonymous */
	if (is_anonymous == true || dav_shared_ar_get_value(r) == OK )  {
		const char *domain;
		char *p;
		char szDomain[1024];        
		//int len;

#define ANONYMOUS_MD5		"246e939eeea2158750a7b68d10dbbf3f"
#define ANONYMOUS_AUTH_MD5	"ANONYMOUSCONNECTEDTHROUGHBROWSER"

#ifdef __OPENDAV_DEBUG_AUTH__
    	DBG0("dav_auth_authenticate: anonymous try to pass");
#endif

    	if (!(domain = apr_table_get(r->headers_in, "RedHost")))
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: anonymous RedHost Not Found. [%s]", r->unparsed_uri);
        	return DECLINED;
		}

		p = strchr(domain, '.');
		len = (p ? p - domain : strlen(domain));

		strncpy(szDomain, domain, len);
#if 0 // add by pizon
		if (len > 1023) len = 1023
#endif
		szDomain[len] = '\0';

		// 2012-04-04 : dadamin
		// dav_auth_get_account_cache < 0 경우 데이터베이스 에러이며, 에러는 503을 리턴 한다.
		result = dav_auth_get_account_cache(r, szDomain, &ac);
		if (result == 0 ) {
			r->user = apr_pstrcat(r->pool, ac.tran_id, "@", ac.seq,	"@", ANONYMOUS_AUTH_MD5, NULL);
			
			if (dav_shared_ac_check_referer(&ac, r) != OK) {
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: Referer Check Done, not permited");
				return HTTP_BAD_REQUEST;
			}
		}
		else if ( result < 0 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV::dav_auth_authenticate Error(ANONYMOUSCONNECTED) result[%s:%d]",szDomain, result);
			return ( result == -2 ? HTTP_SERVICE_UNAVAILABLE : HTTP_BAD_REQUEST );
		}

		return OK;
	}

	if ((rv = dav_auth_get_pw_str(r, (const char **) &pRcvdPasswd)) != OK) {
#if 0
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_authenticate: dav_auth_get_pw_str: failed");
#endif
		return rv;
	}

	/* From here, remained process start */
	/* decode base64 encoding */
	{
		char *decoded, *temp;
		decoded =
			(char *) apr_palloc(r->pool, 8 +
								(len = apr_base64_decode_len(pRcvdPasswd)));
		memset(decoded, 0, len + 7);
		len = apr_base64_decode(decoded, pRcvdPasswd);
		*(decoded + len) = '\0';
		temp = decoded;
#ifdef __OPENDAV_DEBUG_AUTH__
		DBG1("dav_auth_authenticate: decoded: %s", decoded);
#endif
		r->user = ap_getword_nulls(r->pool, (const char **)&temp, ':');
		r->ap_auth_type = "Basic";
#ifdef __OPENDAV_DEBUG_AUTH__
		DBG2("dav_auth_authenticate: decoded split: %s, %s", r->user, temp);
#endif
		pRcvdPasswd = temp; len = len - (strlen(r->user) + 1);
	}

	/* check validate id and password */
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG1("dav_auth_authenticate: CHECK POINT #1 %s", r->user);
#endif
	pszSafeUser = apr_palloc(r->pool, 1 + strlen(r->user));
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG0("dav_auth_authenticate: CHECK POINT #2");
#endif
	pszSafePasswd = apr_palloc(r->pool, 1 + strlen(pRcvdPasswd));
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG0("dav_auth_authenticate: CHECK POINT #3");
	DBG1("dav_auth_authenticate::r->user = %s", r->user);
	DBG2("dav_auth_authenticate::pRcvdPasswd = %s (%d)", pRcvdPasswd, len);
#ifdef __OPENDAV_DEBUG__
	binary_dump(pRcvdPasswd, len);
#endif
#endif
	check_string(pszSafeUser, r->user, strlen(r->user));
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG1("dav_auth_authenticate::pszSafeUser = %s", pszSafeUser);
#endif
	check_string(pszSafePasswd, pRcvdPasswd, strlen(pRcvdPasswd));
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG1("dav_auth_authenticate::pszSafepasswd = %s", pszSafePasswd);
#endif
	rv = at_split(pszSafeUser, &stAi);
	//DBG1("dav_auth_authenticate::1st at_split = %d", res);
	if (rv == -1) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
			"dav_auth_authenticate: at_split fail (%s)", r->user);
		return HTTP_INTERNAL_SERVER_ERROR;
	} else if (rv == -2) { 
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_authenticate: user id format not match (%s)", r->user);
		ap_note_basic_auth_failure(r);
		return HTTP_UNAUTHORIZED;
	}
#ifdef __OPENDAV_DEBUG_AUTH__
	DBG1("dav_auth_authenticate::stAi.szVolumeID = %s", stAi.szVolumeID);
	DBG1("dav_auth_authenticate::stAi.szUserID = %s", stAi.szUserID);
#endif

	// 2012-04-04 : dadamin
	// dav_auth_get_account_cache < 0 경우 데이터베이스 에러이며, 에러는 503을 리턴 한다.
	/* check cached data */
	result = dav_auth_get_account_cache(r, stAi.szVolumeID, &ac);
	if (result < 0 ) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV::dav_auth_authenticate Error(Cache Check) result[%d]", result);
		
		return (result == -2 ? HTTP_SERVICE_UNAVAILABLE : HTTP_BAD_REQUEST);
	}

	if ( strlen(ac.tran_id) == 0 ) 
	{
		ap_note_basic_auth_failure(r);
		return HTTP_UNAUTHORIZED;
	}
	rv = dav_auth_check_basic_user(r, pRcvdPasswd, len, pszSafePasswd, &ac);
	

	if (rv != OK) {
		ap_note_basic_auth_failure(r);
		return rv;
	}

	/* write to cache */
	//dav_shared_ac_add(&ac);

	return OK;
}


int dav_auth_authorization(request_rec * r)
{
	char *user = r->user;
	int m = r->method_number;

	//apr_array_header_t *reqs_arr = (apr_array_header_t *) ap_requires(r);
	apr_array_header_t *reqs_arr =
#if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER >= 3
		NULL;
#else // 2.2.x
		ap_requires(r);
#endif // 2.3 over

	require_line *reqs = reqs_arr ? (require_line *) reqs_arr->elts : NULL;

	register int x;
	const char *t;
	char *w;

#ifdef __OPENDAV_DEBUG_AUTH__
	DBG0("dav_auth_authorization: enter");
#endif

	// 2012.04.23 dadamin : 중복 처리 로직 삭제 처리
	// /dav 인증 시 dav_auth_authenticat -> dav_auth_authorization 호출되며,
	// dav_shared_ar_get_value함수 경우 dav_auth_authenticat 호출하여 처리함	
	//if (dav_shared_ar_get_value(r) == OK)
	//	return OK;

	if (!reqs_arr) {
		return DECLINED;
	}

	for (x = 0; x < reqs_arr->nelts; x++) {

		if (!(reqs[x].method_mask & (1 << m)))
			continue;

		t = reqs[x].requirement;
		w = ap_getword(r->pool, &t, ' ');

		if (!strcmp(w, "valid-user")) {
			return OK;
		}

		if (!strcmp(w, "user")) {
			while (t[0]) {
				w = ap_getword_conf(r->pool, &t);
				if (!strcmp(user, w)) {
					return OK;
				}
			}
		}
	}
	return DECLINED;
}


/* Elenoa: 2007. 2. 2: dav_auth_user serial */

#define RECEIVE_CHUNK	1024
#define RECEIVE_RESERVED	5

static char *dav_auth_user_socket_recv(apr_pool_t *pool, apr_socket_t *sock, char *pszReceive)
{
	char *pszTemp;
	char szReceive[RECEIVE_CHUNK + RECEIVE_RESERVED];
	apr_size_t bytes_read = RECEIVE_CHUNK;
	apr_status_t rv;
	char ebuf[128];

	rv = apr_socket_recv(sock, szReceive, &bytes_read);
	if (rv != APR_SUCCESS) {
		apr_socket_close(sock);

		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"OPENDAV: dav_auth_user_socket_recv: failure to recv message: %s",
				apr_strerror(rv, ebuf, sizeof ebuf));
		return NULL;
	}
	szReceive[bytes_read] = '\0';

	if (pszReceive == NULL) {
		pszTemp = apr_pstrdup(pool, szReceive);
	} else {
		pszTemp = apr_pstrcat(pool, pszReceive, szReceive, NULL);
	}
	return pszTemp;
}


#define CONTENT_LENGTH		"Content-Length"
#define TRANSFER_CHUNKED	"Transfer-Encoding: chunked"

static int dav_auth_user_body_type(char *pszReceive)
{
	char *p, *r;

	p = pszReceive;
	while( (r = strstr(p, "\r\n")) ) 
	{
		// CHG 2012-04-21 huibong
		// 포인터 위치 이동 관련 업무 flow 와 맞지 않아.. 수정 처리함.
		//r += 2; p++;
		
		r += 2; 
		p = r;
		
		if( !strncasecmp(r, CONTENT_LENGTH, strlen(CONTENT_LENGTH))) 
		{
			return 0;
		}
		if( !strncasecmp(r, TRANSFER_CHUNKED, strlen(TRANSFER_CHUNKED))) 
		{
			return 1;
		}
	}

	return -1;
}





#define VALIDATED_STRING	"1000"
#define VALIDATED_FIELD		"code"
#define PERMIT_FIELD		"permitted_size"
#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
#define CLASSIFIER			"classified"
#endif
static int dav_auth_user_validate(request_rec *r, char *pszReceive, int is_chunked, apr_off_t *permitted)
{
	char *p, *p2, *p3, *p4;
	int retval = -1;
#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
	int classified = -1;
#endif // __CLASSIFIED_TRAFFIC_MANAGE__

	p = pszReceive;
	if ((p2 = strstr(p, "\r\n\r\n"))) {
		p2 += 4;
		if (is_chunked) {
			/* we must skip one line more */
			p = p2;
			p2 = strstr(p, "\r\n");
			if (!p2) return -1;
			p2 += 2;
		}

		while (1) {
#ifdef __OPENDAV_DEBUG_USER_AUTH__
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER V (%d) %s", getpid(), p2);
#endif

			if (!strncasecmp(p2, VALIDATED_FIELD, strlen(VALIDATED_FIELD)) &&
				*(p2 + strlen(VALIDATED_FIELD)) == '|') {

#ifdef __OPENDAV_DEBUG_USER_AUTH__
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER V2 (%d) %s", getpid(), (p2 + strlen(VALIDATED_FIELD) + 1));
#endif
				retval = 0;

				if (!strncasecmp((p2 + strlen(VALIDATED_FIELD) + 1),
						VALIDATED_STRING, strlen(VALIDATED_STRING))) {
					retval = 1;
				}
			}
			if (!strncasecmp(p2, PERMIT_FIELD, strlen(PERMIT_FIELD)) &&
				*(p2 + strlen(PERMIT_FIELD)) == '|') {

				if (permitted) {
					*permitted = atoll((p2 + strlen(PERMIT_FIELD) + 1));
				}
			}

#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
			if (!strncasecmp(p2, CLASSIFIER, strlen(CLASSIFIER)) &&
				*(p2 + strlen(CLASSIFIER)) == '|') {

				if (classified == -1) {
					classified = atoi((p2 + strlen(CLASSIFIER) + 1));
				}
			}
#endif

			p3 = strstr(p2, "\r\n");
			p4 = strchr(p2, '\n');

			if (p3 == NULL && p4 == NULL)
				break;

			if (p3 != NULL && (p4 == NULL || (p3 < p4)))
				p2 = p3 + 2;

			if (p4 != NULL && (p3 == NULL || (p4 < p3)))
				p2 = p4 + 1;
		}
	}

#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
	if (classified > -1) {
		acct_info ai;
		int rv = at_split(r->user, &ai);

		if (rv == 1) {
			sprintf(ai.szTCClassID, "%d", classified);

			r->user = apr_pstrcat(r->pool, ai.szVolumeID, ".",
					ai.szTCClassID, "@", ai.szUserID, "@",
					ai.szSessionID, NULL);
		}
	}
#endif

	return retval;
}


static int dav_auth_parse_byterange(char *range, apr_off_t clength,
		apr_off_t *start, apr_off_t *end)
{
	char *dash = strchr(range, '-');
	char *errp;
	apr_off_t number;

	if (!dash) {
		return 0;
	}

	if ((dash == range)) {
		/* In the form "-5" */
		if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) {
			return 0;
		}
		*start = clength - number;
		*end = clength - 1;
	}
	else {
		*dash++ = '\0';
		if (apr_strtoff(&number, range, &errp, 10) || *errp) {
			return 0;
		}
		*start = number;
		if (*dash) {
			if (apr_strtoff(&number, dash, &errp, 10) || *errp) {
				return 0;
			}
			*end = number;
		}
		else {                  /* "5-" */
			*end = clength - 1;
		}
	}

	if (*start < 0) {
		*start = 0;
	}

	if (*end >= clength) {
		*end = clength - 1;
	}

	if (*start > *end) {
		return -1;
	}

	return (*start > 0 || *end < clength);
}


static int dav_auth_parse_range(char *range, apr_off_t total, apr_off_t *req_be_sent, apr_off_t *start)
{
	apr_off_t end = 0;
	int retval;

	*req_be_sent = 0;
	*start = 0;

	if (!range || strncasecmp(range, "bytes=", 6)) {
		return 0;
	}

	range = range + 6;

	retval = dav_auth_parse_byterange(range, total, start, &end);
	*req_be_sent = (end - *start + 1);

	return retval;
}


static void dav_auth_user_set_permitted(request_rec *r, apr_off_t start, apr_off_t permitted)
{
	apr_table_unset(r->headers_in, "Range");
	apr_table_setn(r->headers_in, "Range",
			apr_psprintf(r->pool, "bytes=%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT,
				start, (start + permitted - 1)));
}

/* Elenoa: 2007. 1. 25: Add 'User Authorization' */
int dav_auth_user_start(dav_resource_private *info)
{
	apr_off_t req_be_sent, permitted, start;
	char *range;
	int retval;

	if (!info->db_r || !info->db_r->r)
		return !OK;

	range = (char *)apr_table_get(info->db_r->r->headers_in, "Range");
	range = apr_pstrdup(info->db_r->r->pool, range);
	dav_auth_parse_range(range, info->finfo.size, &req_be_sent, &start);
	if( req_be_sent == 0 ) 
		req_be_sent = info->finfo.size;

	retval = dav_auth_user(info->db_r->r, req_be_sent, &permitted);

	if( retval != OK || permitted < 0 ) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_auth_user_start: checked %s, permitted_size %" APR_OFF_T_FMT", FAILED!", retval == OK ? "OK" : "FAILED", permitted );
		
		return (permitted < 0 ? HTTP_UNAUTHORIZED : retval);
	}


#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER PERMIT (%d) %" APR_OFF_T_FMT" /%" APR_OFF_T_FMT "", getpid(), permitted, req_be_sent);
#endif

	if (permitted > 0 && req_be_sent > permitted) 
	{
		dav_auth_user_set_permitted(info->db_r->r, start, permitted);
	}

	return OK;
}

//2012.03.07 : 상위 모듈에서 사용하고 있는 logio_config_t 값을 참조하기 위한 선언
module AP_MODULE_DECLARE_DATA logio_module;
	typedef struct __dav_logio_config_t {
		apr_off_t bytes_in;
		apr_off_t bytes_out;
} dav_logio_config_t;

void dav_auth_user_end(dav_resource_private *info)
{
	if( !info->db_r || !info->db_r->r )
		return;

	// mod_logio.so의 전송량 참조 : 주의 필요 - access_log 출력 후 관련 값은 초기화됨
	dav_logio_config_t *cf = ap_get_module_config(info->db_r->r->connection->conn_config, &logio_module);

	const char *cp = NULL;
	cp = apr_table_get(info->db_r->r->headers_out, "Content-Length");
	apr_int64_t clen = apr_atoi64(cp);

	if (cf->bytes_out > 0) 
	{
		// 2015.07.07 : dadamin 
		// 헤더가 포함된 값이므로 Content-Length 큰 경우 Content-Length 보냄
		if (cf->bytes_out < clen)
			dav_auth_user(info->db_r->r, cf->bytes_out, NULL);
		else
			dav_auth_user(info->db_r->r, clen, NULL);
	}
	else
		dav_auth_user(info->db_r->r, info->db_r->r->bytes_sent, NULL);
	
	return;
}


static const char c2x_table[] = "0123456789ABCDEF";

static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix, unsigned char *where)
{
#if APR_CHARSET_EBCDIC
	what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what);
#endif /*APR_CHARSET_EBCDIC*/

	*where++ = prefix;
	*where++ = c2x_table[what >> 4];
	*where++ = c2x_table[what & 0xf];

	return where;
}

static char *dav_escape(apr_pool_t *p, const char *path)
{
	char *copy = apr_palloc(p, 3 * strlen(path) + 3);
	const unsigned char *s = (const unsigned char *)path;
	unsigned char *d = (unsigned char *)copy;
	unsigned c;

	while( (c = *s) ) 
	{
		if( !isalpha(c) && !isdigit(c) ) 
		{
			d = c2x(c, '%', d);
		} 
		else 
		{
			(*d) = c;
			d++;
		}
		++s;
	}

	*d = '\0';
	return copy;
}

#ifdef __OPENDAV_USER_AUTH_GET
#define AUTH_USER_HEADER	\
	"GET %s?session_id=%s HTTP/1.1\r\n"	\
	"Host: %s:%d\r\n"	\
	"User-Agent: Opendav-%s\r\n"	\
	"\r\n"
#else
#define AUTH_USER_HEADER	\
	"POST %s HTTP/1.1\r\n"	\
	"Host: %s:%d\r\n"	\
	"Content-Type: application/x-www-form-urlencoded\r\n"	\
	"User-Agent: Opendav-%s\r\n"	\
	"Connection: Keep-Alive\r\n"	\
	"Content-Length: %d\r\n"	\
	"\r\n"	\
	"%s"
#endif

#define AUTH_USER_BODY_START	\
	"session_id=%s&"	\
	"uri=%s&"	\
	"ip=%s&"	\
	"req_size=%" APR_OFF_T_FMT ""

// 2017-11-03 CHG huibong (revision 1426, ir #31497)
// - 토토디스크 측에서 BILL 정보에 request_time, end_time 항목 추가를 요청하여 기능 추가
// - 현재는 일반 고객은 없지만 향후 발생될 수 있는 신규 고객에도 동일하게 제공
#define AUTH_USER_BODY_END		\
	"session_id=%s&"	\
	"uri=%s&"	\
	"ip=%s&"	\
	"sent_size=%" APR_OFF_T_FMT ""	\
	"&request_time=%" APR_TIME_T_FMT ""	\
	"&end_time=%" APR_TIME_T_FMT ""

#define AUTH_USER_BODY_REFERER	\
	"&referer=%s"

#define AUTH_USER_BODY_TIMESLICE	\
	"&time_%d=%" APR_UINT64_T_FMT ""

// 소프트 라인 업로드 파일 싱크 및 다운로드 과금 처리를 위한 필요한 KEY 값 정의
/// 2013-06-25 : SDK확장 정보(헤더에 SB_ExtSession) 추가로 고객사에서 구분할 수 있으므로 해당 값 정의 필요 없음
#ifdef __USE_AUTH_SOFTLINE_ONLY__
#define	BILL_DOWNLOAD	
#define BILL_UPLOAD		

// 업로드 정보 전송관련 time out
#define  BILL_UPLOAD_END_SEND_TIMEOUT	5 // sec
#define  BILL_UPLOAD_END_RECV_TIMEOUT	1 // sec

#define BILL_UPLOAD_END	\
	"session_id=%s&"	\
	"uri=%s&"	\
	"ip=%s&"	\
	"file_size=%" APR_OFF_T_FMT ""
#endif // __USE_AUTH_SOFTLINE_ONLY__

/*
#define STRICTLY_AUTH_CODE	"100"
#define IS_STRICTLY_AUTH(ac)	\
	(!strncmp(ac->mode, STRICTLY_AUTH_CODE, strlen(STRICTLY_AUTH_CODE)))
*/

#define IS_STRICTLY_AUTH(ac)	(ac->mode[0] == '1')
#define IS_PERMIT_ANONYMOUS(ac)	(ac->mode[1] == '0')
#define IS_PERMIT_DAV_AUTH(ac)	(ac->mode[2] == '0')
#define IS_STRICTLY_ANONYMOUS(ac)	(strlen(ac->mode) > 3 && ac->mode[3] == '1')
#define IS_PERMIT_EMPTY_REFERER(ac)	(strlen(ac->mode) > 4 && ac->mode[4] == '1') /* using in "share.c" by pizon */

#define AU_PORT		(permitted ? ac->auth_port : ac->bill_port)
#define AU_URI		(permitted ? ac->auth_uri  : ac->bill_uri)
#define AU_ADDR		(permitted ? ac->auth_addr : ac->bill_addr)
#define AU_TIMEOUT	(permitted ? ac->auth_timeout : ac->bill_timeout)

/*
** r         [in] request 정보를 저장한 request_rec 객체에 대한 포인터
** content   [in] 전송 요청 또는 실제 전송한 content size 정보
** permitted [out] 허용 여부 결과값을 저장할 포인터
**                 AUTH 요청인 경우 해당 객체는 NULL 아니지만, BILL 요청인 경우 NULL
*/
int dav_auth_user(request_rec *r, apr_off_t content, apr_off_t *permitted)
{
	acct_info ai;
	apr_status_t rv;
	apr_socket_t *sock;
	apr_sockaddr_t *sa;
	char ebuf[128]; //, buf[120];
	char *pszMessage
#ifndef __OPENDAV_USER_AUTH_GET
		, *pszBody, *uri
#endif
		;
	apr_size_t bytes_send;
	struct account_cache *ac;
	const char *referer = apr_table_get(r->headers_in, "Referer");

	char *pszReceive = NULL;
	int is_chunked = 0, is_code;

	// 2017-11-03 CHG huibong (revision 1426, ir #31497)
	// - httpd version 별 request_rec 의 client ip 항목이 서로 달라 변수 처리.
	const char *pszClientIP = NULL;
	

	// 2018-01-25 huibong 토토지원 과금 IP 확인용 임시 코드
	char tempBuffer[40];
	memset( tempBuffer, 0x00, sizeof( tempBuffer ) );


	if (permitted) *permitted = 0;

#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER START (%d) %s", getpid(), r->user);
#endif


	/* get session key from user string */
	rv = at_split(r->user, &ai);
	if (rv == -1) {
		return HTTP_INTERNAL_SERVER_ERROR;
	} else if (rv == -2) { 
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_user: user id format not match (%s)", r->user);
		return HTTP_UNAUTHORIZED;
	}

	/* get cache info */
	ac = dav_shared_ac_get_by_tranid(ai.szVolumeID);
	if (!ac) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_user: cache not found.. is possible? (%s)", r->user);
		/* Internal Error Condition, i know.
		 * if process this routine, old-type-anonymous auth type.
		 * to prevent smiliar type of error, return OK */
		return OK;
		//return HTTP_INTERNAL_SERVER_ERROR;
	}


	if ((permitted ? !ac->auth_uri[0] : !ac->bill_uri[0])) {
		return OK;
	}

#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER MODE %c%c%c",
			ac->mode[0], ac->mode[1], ac->mode[2]);
#endif
	/* if old-auth or dav-auth */
	if (ai.szSessionID[0] == '\0') {
		if (IS_PERMIT_DAV_AUTH(ac)) {
			return OK;
		} else {
			return HTTP_UNAUTHORIZED;
		}
	}
	
	if (!strcmp(ai.szSessionID, ANONYMOUS_AUTH_MD5)) {
		if (!IS_STRICTLY_ANONYMOUS(ac))
			return OK;
	}
	
	/* if anonymous */
	/* soft-line auth 내부 test 시 주석처리 */
	if (!strcmp(ai.szSessionID, ANONYMOUS_MD5)) {
		if (IS_PERMIT_ANONYMOUS(ac)) {
			return OK;
		} else {
			return HTTP_UNAUTHORIZED;
		}
	}

	// fill socket address structure */
	rv = apr_sockaddr_info_get(&sa, AU_ADDR, APR_INET, AU_PORT, 0, r->pool);
	//rv = apr_sockaddr_info_get(&sa, "1.2.3.4", APR_INET, ac->port, 0, r->pool);
	if (rv != APR_SUCCESS) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"OPENDAV: dav_auth_user: failure to open to '%s:%d': %s",
				AU_ADDR, AU_PORT, apr_strerror(rv, ebuf, sizeof ebuf));
		return HTTP_INTERNAL_SERVER_ERROR;
	}

	/* make socket */
	rv = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP, r->pool);
	if (rv != APR_SUCCESS) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"OPENDAV: dav_auth_user: create socket: %s",
				apr_strerror(rv, ebuf, sizeof ebuf));
		return HTTP_INTERNAL_SERVER_ERROR;
	}

	/* set timeout */
	rv = apr_socket_timeout_set(sock, apr_time_from_sec(AU_TIMEOUT));
	if (rv != APR_SUCCESS) {
		apr_socket_close(sock);
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"OPENDAV: dav_auth_user: set timeout: %s",
				apr_strerror(rv, ebuf, sizeof ebuf));
		return HTTP_INTERNAL_SERVER_ERROR;
	}




	/* connect */
	rv = apr_socket_connect(sock, sa);

	// 2018-01-25 huibong 토토지원 과금 IP 확인용 임시 코드
	if( inet_ntop( AF_INET, &( sa->sa.sin.sin_addr ), tempBuffer, sizeof( tempBuffer ) ) == NULL )
	{
		sprintf( tempBuffer, "unknown" );
	}

	if (rv != APR_SUCCESS) 
	{
		ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL,
					  "OPENDAV: dav_auth_user: connect socket error: (%s/%s/%s) %s(%s):%d/%s: %s",
					  ac->tran_id, ac->seq, ai.szSessionID, AU_ADDR, tempBuffer, AU_PORT,
					  AU_URI, apr_strerror( rv, ebuf, sizeof ebuf ) );

		return IS_STRICTLY_AUTH(ac) ? HTTP_UNAUTHORIZED : HTTP_INTERNAL_SERVER_ERROR;
	}
	/*
	else
	{
		ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL,
					  "OPENDAV: dav_auth_user: connect socket ok: (%s/%s/%s) %s(%s):%d/%s",
					  ac->tran_id, ac->seq, ai.szSessionID, AU_ADDR, tempBuffer, AU_PORT,
					  AU_URI );
	}
	*/


	/* make message */
#ifdef __OPENDAV_USER_AUTH_GET
	pszMessage = apr_psprintf(r->pool, AUTH_USER_HEADER,
		AU_URI, ai.szSessionID, AU_ADDR, AU_PORT, VERSION);
#else
	//uri = ap_os_escape_path(r->pool, r->uri, 1);
	//uri = r->uri;
	//uri = ap_escape_logitem(r->pool, r->uri);
	uri = apr_pcalloc(r->pool, strlen(r->uri) + 3);
	dav_repos_restore_unsafe_chars(r->uri, uri);
	*(uri + strlen(r->uri)) = '\0';


#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER URI %s (%d)", uri, getpid());
#endif
	//uri = dav_escape(r->pool, r->uri);
	uri = dav_escape(r->pool, uri);
#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER ESCAPE URI %s (%d)", uri, getpid());
#endif


	
	// 2017-11-03 CHG huibong (revision 1426, ir #31497)
	// - Apache 버전별 client ip 정보 추출
#if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER >= 3	
	pszClientIP = r->connection->client_ip;	
#else // 2.2.x
	pszClientIP = r->connection->remote_ip;	
#endif // 2.3 over
	

#ifdef __USE_AUTH_SOFTLINE_ONLY__
	
	// 2017-11-03 CHG huibong (revision 1426, ir #31497)
	// - 토토디스크 측에서 BILL 정보에 request_time, end_time 항목 추가를 요청하여 기능 추가
	// - 기존에는 AUTH, BILL 인자 항목이 동일하여 문제 없었지만, BILL 항목에 2개가 추가되어 코드 분리 처리함.

//	pszBody = apr_psprintf( r->pool,
//		( permitted ?
//			( referer ? AUTH_USER_BODY_START AUTH_USER_BODY_REFERER : AUTH_USER_BODY_START )
//		  : ( referer ? BILL_DOWNLOAD AUTH_USER_BODY_END AUTH_USER_BODY_REFERER : BILL_DOWNLOAD AUTH_USER_BODY_END ) ),
//							ai.szSessionID,
//#if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER >= 3
//							uri, r->connection->client_ip, content, referer );
//#else // 2.2.x
//		uri, r->connection->remote_ip, content, referer);
//#endif // 2.3 over

	if( permitted )	// AUTH 관련 처리인 경우
	{
		pszBody = apr_psprintf( r->pool, ( referer ? AUTH_USER_BODY_START AUTH_USER_BODY_REFERER : AUTH_USER_BODY_START )
								, ai.szSessionID 
								, uri, pszClientIP, content
								, referer );
		
	}
	else	// BILL 관련 처리인 경우
	{
		pszBody = apr_psprintf( r->pool, ( referer ? BILL_DOWNLOAD AUTH_USER_BODY_END AUTH_USER_BODY_REFERER : BILL_DOWNLOAD AUTH_USER_BODY_END )
								, ai.szSessionID
								, uri, pszClientIP, content, r->request_time, apr_time_now()
								, referer );
	}


#else // !__USE_AUTH_SOFTLINE_ONLY__

//	pszBody = apr_psprintf(r->pool,
//			(permitted ?
//			 (referer ? AUTH_USER_BODY_START AUTH_USER_BODY_REFERER :
//					AUTH_USER_BODY_START)
//			 :
//			 (referer ? AUTH_USER_BODY_END AUTH_USER_BODY_REFERER :
//					AUTH_USER_BODY_END)),
//			ai.szSessionID,
//#if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER >= 3
//			uri, r->connection->client_ip, content, referer);
//#else // 2.2.x
//			uri, r->connection->remote_ip, content, referer);
//#endif // 2.3 over
		
	if( permitted )	// AUTH 관련 처리인 경우
	{
		pszBody = apr_psprintf( r->pool, ( referer ? AUTH_USER_BODY_START AUTH_USER_BODY_REFERER : AUTH_USER_BODY_START )
								, ai.szSessionID
								, uri, pszClientIP, content
								, referer );
	}
	else	// BILL 관련 처리인 경우
	{
		pszBody = apr_psprintf( r->pool, ( referer ? AUTH_USER_BODY_END AUTH_USER_BODY_REFERER : AUTH_USER_BODY_END )
								, ai.szSessionID
								, uri, pszClientIP, content, r->request_time, apr_time_now()
								, referer );
	}
		
	
#endif // !__USE_AUTH_SOFTLINE_ONLY__
	
	
	// 2015.07.16 dadamin
	// 해당 인터페이스  공통(SDK)으로 적용되었으므로 모듈에서 동일하게 적용함
	if (!permitted)
	{
		// 2013-03-26 : dadamin
		// 고객사(토토로사) 요청으로 웹하드 등록제 통과를 위해서 다운로드 완료 시
		// 추가된 header data를 추가로 전송될 수 있게 한다.
		const char *ExtSessionID;
		if ((ExtSessionID = apr_table_get(r->headers_in, "SB_ExtSession")))
		{
			pszBody = apr_psprintf(r->pool,"%s&%s", pszBody, ExtSessionID);
		}
		else 
		{
			// 해당 header가 없을 경우 단순 log 처리
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Extend the session does not exist.[%s]", uri);
		}
	}

#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER MKBODY (%d)", getpid());
#endif
#ifdef __USE_AUTH_SOFTLINE_ONLY__
	char szagent[20];
	int ncontentlen = (int) strlen(pszBody);

	// 소프트 라인에서 사용하고 있는 auth 경우 hearder가 고정크기이다.
	// auth 경우 : 14+181, 과금 경우 : 14 + 184
	// 고려 사항 : content length 경우 두자리, 네자리 숫자 염두
	memset(szagent, 0, sizeof(szagent));
	if( 9 < ncontentlen &&  ncontentlen < 100 )  // 두자리
	{
		strcpy(szagent, "WITH SOFTLINE ***");
	}
	else if ( 999 < ncontentlen ) // 네자리
	{
		strcpy(szagent, "WITH SOFTLINE *");
	}
	else // 나머지 (한자리,다섯자리 이상은 무시)
	{
		strcpy(szagent, "WITH SOFTLINE **");
	}

	pszMessage = apr_psprintf(r->pool, AUTH_USER_HEADER,
			AU_URI, AU_ADDR, AU_PORT,
			szagent, (int)strlen(pszBody), pszBody);
#else  //  __USE_AUTH_SOFTLINE_ONLY__
	pszMessage = apr_psprintf(r->pool, AUTH_USER_HEADER,
			AU_URI, AU_ADDR, AU_PORT,
			VERSION, (int)strlen(pszBody), pszBody);
#endif //  __SOFT_LINE_AUTH
#endif

#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER E (%d) %s", getpid(), pszMessage);
#endif

	/* send message */
	bytes_send = strlen(pszMessage);
	rv = apr_socket_send(sock, pszMessage, &bytes_send);
	if (rv != APR_SUCCESS) {
		apr_socket_close(sock);

		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"OPENDAV: dav_auth_user: failure to send message: (%s/%s/%s) %s:%d/%s: %s",
				ac->tran_id, ac->seq, ai.szSessionID, AU_ADDR, AU_PORT,
				AU_URI, apr_strerror(rv, ebuf, sizeof ebuf));

		return IS_STRICTLY_AUTH(ac) ? HTTP_UNAUTHORIZED : HTTP_INTERNAL_SERVER_ERROR;
	}

	// 2011-03-20 소프트라인의 경우 인증은 응답대기, 과금은 응답대기하지 않도록 함.
#ifdef __USE_AUTH_SOFTLINE_ONLY__
	if(permitted == NULL )
	{
		apr_socket_close(sock);
		return OK;
	}

#endif

	/* receive message */
	/* step 0: get header */
	pszReceive = dav_auth_user_socket_recv(r->pool, sock, pszReceive);
	if (pszReceive == NULL) {
		apr_socket_close(sock);
		return IS_STRICTLY_AUTH(ac) ? HTTP_UNAUTHORIZED : HTTP_INTERNAL_SERVER_ERROR;
	}

	/* step 1: parse content-length (or chunked) */
	while ((is_chunked = dav_auth_user_body_type(pszReceive)) < 0) {
		pszReceive = dav_auth_user_socket_recv(r->pool, sock, pszReceive);
		if (pszReceive == NULL) {
#ifdef __OPENDAV_DEBUG_USER_AUTH__
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER ERR (%d) %s", getpid(), pszReceive);
#endif
			apr_socket_close(sock);
			return IS_STRICTLY_AUTH(ac) ? HTTP_UNAUTHORIZED : HTTP_INTERNAL_SERVER_ERROR;
		}
	}

	/* step 2: get body */
	/* step 3: check authrization */


	while ((is_code = dav_auth_user_validate(r, pszReceive, is_chunked, permitted)) < 0) {
		pszReceive = dav_auth_user_socket_recv(r->pool, sock, pszReceive);
		if (pszReceive == NULL) {
#ifdef __OPENDAV_DEBUG_USER_AUTH__
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER ERR (%d) %s", getpid(), pszReceive);
#endif
			apr_socket_close(sock);
			return IS_STRICTLY_AUTH(ac) ? HTTP_UNAUTHORIZED : HTTP_INTERNAL_SERVER_ERROR;
		}
	}

#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER D (%d) %s", getpid(), pszReceive);
#endif

	apr_socket_close(sock);

	return (is_code == 1 ? OK : HTTP_UNAUTHORIZED);

}

#ifdef __USE_AUTH_SOFTLINE_ONLY__
int dav_auth_upload_end_softline(request_rec *r, apr_off_t content, apr_off_t *permitted)
{
	acct_info ai;
	apr_status_t rv;
	apr_socket_t *sock;
	apr_sockaddr_t *sa;
	char ebuf[128]; //, buf[120];
	char *pszMessage
#ifndef __OPENDAV_USER_AUTH_GET
		, *pszBody, *uri
#endif
		;
	apr_size_t bytes_send;
	struct account_cache *ac;
	const char *referer = apr_table_get(r->headers_in, "Referer");

	if (permitted) *permitted = 0;

#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER START (%d) %s", getpid(), r->user);
#endif

	// 2016.05.27 
	// 위즈릴 제휴 컨텐츠 경우 FTP(SolDrive 마운트)를 통해서 업로드됨
	// 이 경우 해당 과금 서버로 다수 요청될 수 있으므로 해당 과금 서버 안정성을 위해서 예외 처리함.
	// 참고 : 해당 컨텐츠 업로드 완료 후 연동되는 인터페이스(ASP 구성된 서버)에서 과금서버(트랜스코딩요청)호출됨
	const char * userAgent = NULL;
	userAgent = apr_table_get( r->headers_in, "User-Agent");
	if(userAgent != NULL && strncmp(userAgent, "NetCache", 8) == 0 ) 
	{
		if(r && r->server)
			ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "SKIP : Soldrive upload.[%s]", r->uri);
		return OK;
	}


	/* get session key from user string */
	rv = at_split(r->user, &ai);
	if (rv == -1) {
		return HTTP_INTERNAL_SERVER_ERROR;
	} else if (rv == -2) { 
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_upload_end_softline: user id format not match (%s)", r->user);
		return HTTP_UNAUTHORIZED;
	}

	/* get cache info */
	ac = dav_shared_ac_get_by_tranid(ai.szVolumeID);
	if (!ac) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"dav_auth_upload_end_softline: cache not found.. is possible? (%s)", r->user);
		/* Internal Error Condition, i know.
		 * if process this routine, old-type-anonymous auth type.
		 * to prevent smiliar type of error, return OK */
		return OK;
		//return HTTP_INTERNAL_SERVER_ERROR;
	}


	if ((permitted ? !ac->auth_uri[0] : !ac->bill_uri[0])) {
		return OK;
	}

#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER MODE %c%c%c",
			ac->mode[0], ac->mode[1], ac->mode[2]);
#endif
	/* if old-auth or dav-auth */
	if (ai.szSessionID[0] == '\0') {
		// PUT not used;
	}
	
	if (!strcmp(ai.szSessionID, ANONYMOUS_AUTH_MD5)) {
		if (!IS_STRICTLY_ANONYMOUS(ac))
			return OK;
	}
	
	/* if anonymous */
	/* soft-line auth 내부 test 시 주석처리 */
	if (!strcmp(ai.szSessionID, ANONYMOUS_MD5)) {
		if (IS_PERMIT_ANONYMOUS(ac)) {
			return OK;
		} else {
			return HTTP_UNAUTHORIZED;
		}
	}

	// fill socket address structure */
	rv = apr_sockaddr_info_get(&sa, AU_ADDR, APR_INET, AU_PORT, 0, r->pool);
	//rv = apr_sockaddr_info_get(&sa, "1.2.3.4", APR_INET, ac->port, 0, r->pool);
	if (rv != APR_SUCCESS) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"OPENDAV: dav_auth_upload_end_softline: failure to open to '%s:%d': %s",
				AU_ADDR, AU_PORT, apr_strerror(rv, ebuf, sizeof ebuf));
		return HTTP_INTERNAL_SERVER_ERROR;
	}

	/* make socket */
	rv = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP, r->pool);
	if (rv != APR_SUCCESS) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"OPENDAV: dav_auth_upload_end_softline: create socket: %s",
				apr_strerror(rv, ebuf, sizeof ebuf));
		return HTTP_INTERNAL_SERVER_ERROR;
	}

	/* set timeout */
	rv = apr_socket_timeout_set(sock, apr_time_from_sec(BILL_UPLOAD_END_SEND_TIMEOUT));
	if (rv != APR_SUCCESS) {
		apr_socket_close(sock);
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"OPENDAV: dav_auth_upload_end_softline: set timeout: %s",
				apr_strerror(rv, ebuf, sizeof ebuf));
		return HTTP_INTERNAL_SERVER_ERROR;
	}

	/* connect */
	rv = apr_socket_connect(sock, sa);
	if (rv != APR_SUCCESS) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"OPENDAV: dav_auth_upload_end_softline: failure to connect socket: (%s/%s/%s - %" APR_OFF_T_FMT ") %s:%d/%s: %s",
				ac->tran_id, ac->seq, r->uri, content, AU_ADDR, AU_PORT,
				AU_URI, apr_strerror(rv, ebuf, sizeof ebuf));

		return IS_STRICTLY_AUTH(ac) ? HTTP_UNAUTHORIZED : HTTP_INTERNAL_SERVER_ERROR;
	}

	/* make message */
#ifdef __OPENDAV_USER_AUTH_GET
	pszMessage = apr_psprintf(r->pool, AUTH_USER_HEADER,
		AU_URI, ai.szSessionID, AU_ADDR, AU_PORT, VERSION);
#else
	//uri = ap_os_escape_path(r->pool, r->uri, 1);
	//uri = r->uri;
	//uri = ap_escape_logitem(r->pool, r->uri);
	uri = apr_pcalloc(r->pool, strlen(r->uri) + 3);
	dav_repos_restore_unsafe_chars(r->uri, uri);
	*(uri + strlen(r->uri)) = '\0';


#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER URI %s (%d)", uri, getpid());
#endif

	//uri = dav_escape(r->pool, uri); 차진호 과장님 요청으로 URI인코딩하지 않음. 2011년 03월 21일
#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER ESCAPE URI %s (%d)", uri, getpid());
#endif

#define FIXED_SESSION_ID	"201S103wolt1920220h893ds10da3da3"

	pszBody = apr_psprintf(r->pool,
		(permitted ?
		(referer ? AUTH_USER_BODY_START AUTH_USER_BODY_REFERER :
				AUTH_USER_BODY_START)
		:
		(referer ? BILL_UPLOAD BILL_UPLOAD_END AUTH_USER_BODY_REFERER :
				BILL_UPLOAD BILL_UPLOAD_END)),
		FIXED_SESSION_ID,
#if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER >= 3
		uri, r->connection->client_ip, content, referer);
#else // 2.2.x
		uri, r->connection->remote_ip, content, referer);
#endif // 2.3 over

#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER MKBODY (%d)", getpid());
#endif

	if(!permitted)
	{
		// 2013-06-26 : dadamin
		// 고객사(토토로사) 요청으로 솔루션 연동을 위해서 업로드 완료 후 
		// 추가된 header data를 추가로 전송될 수 있게 한다.
		const char *ExtSessionID;
		if ((ExtSessionID = apr_table_get(r->headers_in, "SB_ExtSession")))
		{
			pszBody = apr_psprintf(r->pool,"%s&%s", pszBody, ExtSessionID);
		}
		else 
		{
			// 해당 header가 없을 경우 단순 log 처리
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "PUT Extend the session does not exist.[%s]", uri);
		}
	}

	char szagent[20];
	int ncontentlen = (int) strlen(pszBody);

	// 소프트 라인에서 사용하고 있는 auth 경우 hearder가 고정크기이다.
	// auth 경우 : 14+181, 과금 경우 : 14 + 184
	// 고려 사항 : content length 경우 두자리, 네자리 숫자 염두
	memset(szagent, 0, sizeof(szagent));
	if( 9 < ncontentlen &&  ncontentlen < 100 )  // 두자리
	{
		strcpy(szagent, "WITH SOFTLINE ***");
	}
	else if ( 999 < ncontentlen ) // 네자리
	{
		strcpy(szagent, "WITH SOFTLINE *");
	}
	else // 나머지 (한자리,다섯자리 이상은 무시)
	{
		strcpy(szagent, "WITH SOFTLINE **");
	}

	pszMessage = apr_psprintf(r->pool, AUTH_USER_HEADER,
			AU_URI, AU_ADDR, AU_PORT,
			szagent, (int)strlen(pszBody), pszBody);

#endif

#ifdef __OPENDAV_DEBUG_USER_AUTH__
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "P_USER E (%d) %s", getpid(), pszMessage);
#endif

	/* send message */
	bytes_send = strlen(pszMessage);
	rv = apr_socket_send(sock, pszMessage, &bytes_send);
	if (rv != APR_SUCCESS) {
		apr_socket_close(sock);

		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
				"OPENDAV: dav_auth_upload_end_softline: failure to send message: (%s/%s/%s - %" APR_OFF_T_FMT ") %s:%d/%s: %s",
				ac->tran_id, ac->seq, uri, content, AU_ADDR, AU_PORT,
				AU_URI, apr_strerror(rv, ebuf, sizeof ebuf));

		return IS_STRICTLY_AUTH(ac) ? HTTP_UNAUTHORIZED : HTTP_INTERNAL_SERVER_ERROR;
	}

	// 2011-03-20 huibong
	// Upload 정보를 보내고 소프트라인 서버에 대한 응답대기를 하지 않는다. ( 차진호 과장 확인사항 )
	apr_socket_close(sock);

	return OK;
}

void dav_auth_upload_end(dav_resource_private *info) 
{
#ifdef __USE_AUTH_SOFTLINE_ONLY_UPLOAD_END_SEND_
	if (!info->db_r || !info->db_r->r)
		return;
	
	dav_auth_upload_end_softline(info->db_r->r, info->db_r->m_get_content_length, NULL);
#endif // __USE_AUTH_SOFTLINE_ONLY_UPLOAD_END_SEND_
	return;
}
#endif // __USE_AUTH_SOFTLINE_ONLY__
