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

   mod_dav_pgsql (DASL) for apache 2.X
   CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
   CopyRight(C) 2006 Netomi Inc. All Rights reserved.
   Author : elenoa lazyfake (elenoa@solutionbox.co.kr)

   $Id: share.c,v 1.8 2007/02/22 08:22:13 elenoa Exp $

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

 ****************************************************************************/
#include <unistd.h>
#include <ctype.h>
#include <math.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 <apr_strings.h>
#include <apr_signal.h>

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

/* for anonymous */
static struct shm_mgnt anon_mgnt = {
	NULL, NULL, SHM_ANON, SHM_ANON_LOCK, 0, 0
};

struct anonymous *g_ar;
static int maxanon = 0;

int dav_initialize_shared_anonymous(dav_repos_server_conf *dsc, server_rec *s, apr_pool_t *p)
{
	if (!dsc->max_anony_realm || (maxanon = atoi(dsc->max_anony_realm)) <= 0) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "OPENDAV: initialize_shared_memory: "
			"MaxAnonymous <= 0 (%d)", maxanon);
		return !OK;
	}

	/* anonymous control : calculate shared memory size from max_anony_realm value */
	anon_mgnt.size = (sizeof(struct anonymous) * maxanon);
	g_ar = (struct anonymous *)dav_shared_get_new_memory(&anon_mgnt, s, p, false);

	if (!g_ar) return !OK;

	return OK;
}

void dav_cleanup_shared_anonymous()
{
	if (anon_mgnt.shm) {
		apr_shm_detach(anon_mgnt.shm);
	}

	if (anon_mgnt.lock) {
		apr_global_mutex_destroy(anon_mgnt.lock);
	}
}

// CHG 2012-04-20 huibong
// 본 함수는 HOOK 처리된 opendav 의 최초 진입함수로서 요청에 대한 무인증 여부를 확인함.
// 하지만 본 함수의 flow 가 불필요 loop 를 돌려서 처리하기 때문에.. 무인증 설정 정보가 많을 경우..
// 전체 응답 성능을 저하시켜... 전체적으로 수정 처리함.

static int dav_find_location(request_rec *r, const char * svc_name, const char * uri, bool uricaseignore, bool urihassvcname)
{
	// DB 처리용 변수 생성.
	char * temp_uri = NULL;
	char * db_uri = apr_pcalloc(r->pool, MaxSizeOfPathLength);
	char * red_uri = NULL;
	char * e_uri = NULL;

	char * query;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;
	char tblname[20] = { 0 };

	int ntuple;
	char * host_name = NULL;
	unsigned int n = 0;

	char * p3 = NULL;

	// loop 처리 관련 변수
	int j;

	struct account_cache * ac = NULL;
	ac = dav_shared_ac_get_by_vol_not_timeout(svc_name);
	if (ac == NULL)
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_find_location: svcname[%s] info not found.", svc_name);
		return !OK;
	}

	/* UTF-8. change UHC(CP949) */
	temp_uri = transformDBCharacter(r, (char *)uri, "dav_find_location");

	// 2012-08-10 CHG huibong
	// 무인증 URI 상에 \ 및 ' 의 특수문자가 포함된 경우 RCDB 상에 쿼리가 정상적으로 되지 않는 문제가 발생.
	// 이를 해결하기 위해 특수문자를 RCDB 가 인식할 수 있도록 dav_repos_replace_unsafe_chars() 함수를 이용하여 변환 처리하도록 수정
	memset(db_uri, 0x00, MaxSizeOfPathLength);
	dav_repos_replace_unsafe_chars(temp_uri, db_uri);

	p3 = strchr(db_uri, '?');
	if (p3)
		*p3 = '\0';

	// 2014-04-24 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	int nDepth;
	nDepth = ap_count_dirs(db_uri);

	/* remake redirected uri */
	// CHG 2010-08-11 대소문자 구분여부에 따른 Query 변경.

	// ChG 2012-04-19 huibong
	// - Query 구문상에 MAX, GROUP BY, ORDER BY 조건식이 아래 코드의 Random 선택 코드로 인해 무의미하므로 수정 처리.

	if (uricaseignore == true)	// URI 대소문자 무시 서비스인 경우
	{
		query = apr_psprintf(r->pool,
			"SELECT host_name, is_cache, filename_hash "
			"FROM %s "
			// CHG 2014.10.02 dadamin
			// WHERE 절 구문 중 조건과 조건 사이 공백 추가
			// 현재 정상 수행됨을 확인 하였지만 만약에 있을 오류 대비하여 수정함
			// e.g) SELECT ...  WHERE sp_svc_tran_id = 214AND depth = 3... (정상 수행됨)
			"WHERE sp_svc_tran_id = %s "
			"AND depth = %d "
			"AND %s = lower('/%s%s') AND deleted_yn = 'N' "
			, dav_get_target_table(ac->tran_id, tblname)
			, ac->tran_id, nDepth
			// 2014.06.12
			// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
			, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
			, ac->tran_id, db_uri
			);
	}
	else
	{
		// 대소문자 구분을 하는 경우
		query = apr_psprintf(r->pool,
			"SELECT host_name, is_cache, filename_hash "
			"FROM %s "
			// CHG 2014.10.02 dadamin
			// WHERE 절 구문 중 조건과 조건 사이 공백 추가
			// 현재 정상 수행됨을 확인 하였지만 만약에 있을 오류 대비하여 수정함
			// e.g) SELECT ...  WHERE sp_svc_tran_id = 214AND depth = 3... (정상 수행됨)
			"WHERE sp_svc_tran_id = %s "
			"AND depth = %d "
			"AND uri = '/%s%s' AND deleted_yn = 'N' "
			, dav_get_target_table(ac->tran_id, tblname)
			, ac->tran_id, nDepth, ac->tran_id, db_uri
			);
	}

	db = dav_repos_get_db(r);
	if (db == NULL)
	{
		db_error_message(db, "dav_find_location: database acquire timeout", 0);
		return DECLINED;
	}

	rv = apr_dbd_select(db->driver, r->pool, db->handle, &res, query, 1);
	if (rv)
	{
		db_error_message(db, "dav_find_location: select", rv);;
		dav_repos_release_db(r, db, rv);

		return DECLINED;
	}

	ntuple = apr_dbd_num_tuples(db->driver, res);

	// 조회 결과가 존재하지 않을 경우
	if (ntuple < 1)
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_find_location: request uri not found [%s][%s]", svc_name, db_uri);
		dav_repos_release_db(r, db, rv);
		return DECLINED;
	}

	// CHG 2012-04-19 huibong
	// Redirection 처리 장비 선택 로직 전면 수정..
	// 기존 코드 로직 중 불필요한 loop 로직 존재.. 
	// 그리고 무조건 301 Redirection 응답이므로... 부하 분산을 위해 자기 자신 장비가 우선이 아니라.. 살아 있는 장비 중 random 선택 처리.
	host_name = NULL;

	// 해당 장비가 1대 인 경우
	if (ntuple == 1)
	{
		rv = solbox_apr_dbd_get_row(db->driver, r->pool, res, &row, 0);
		host_name = apr_pstrdup(r->pool, apr_dbd_get_entry(db->driver, row, 0));
	}
	else	// 해당 장비가 여러 대인 경우 
	{
		// C99을 지원하는 컴파일일 경우 배열의 인자로 처리 가능 만약을 대비하여 동적할당 방식으로 수정
		int * host_alive_index;
		int host_alive_count = 0;

		// 메모리 할당
		host_alive_index = (int *)calloc(ntuple, sizeof(int));

		// 메모리 할당 실패 시 => 첫번째 row의 값을 사용
		if (host_alive_index == NULL)
		{
			rv = solbox_apr_dbd_get_row(db->driver, r->pool, res, &row, 0);
			host_name = apr_pstrdup(r->pool, apr_dbd_get_entry(db->driver, row, 0));
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_find_location: out of memory. default return first [%s][%s][%s]", svc_name, db_uri, host_name);
		}
		else
		{
			// 조회된 장비가 Alive 상태인지 검사한다.
			for (j = 0; j < ntuple; j++)
			{
				rv = solbox_apr_dbd_get_row(db->driver, r->pool, res, &row, j);

				if (dav_shared_du_is_alive(apr_dbd_get_entry(db->driver, row, 0)) != 0)
				{
					host_alive_index[host_alive_count] = j;
					host_alive_count++;
				}
			}

			// Alive 장비의 갯수 확인
			if (host_alive_count <= 0)
			{
				// Alive 상태 장비가 없는 경우... 첫번째 장비 정보 사용.
				rv = solbox_apr_dbd_get_row(db->driver, r->pool, res, &row, 0);
				host_name = apr_pstrdup(r->pool, apr_dbd_get_entry(db->driver, row, 0));
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_find_location: alive host nothing. default return first [%s][%s][%s]", svc_name, db_uri, host_name);
			}
			else
			{
				// Alive 상태의 장비가 여러대 인 경우...
				// Random 선택
				apr_generate_random_bytes((unsigned char *)&n, sizeof(int));
				n = n % host_alive_count;

				rv = solbox_apr_dbd_get_row(db->driver, r->pool, res, &row, host_alive_index[n]);
				host_name = apr_pstrdup(r->pool, apr_dbd_get_entry(db->driver, row, 0));

				ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "dav_find_location: alive target random select [%s][%s][%s]", svc_name, db_uri, host_name);
			}

			// 메모리 해제
			free(host_alive_index);
		}
	}

	// RCDB 접속 해제
	dav_repos_release_db(r, db, rv);

	// 2014-06-11 dadamin uri query 구문 인코딩 방지
	p3 = strchr(uri, '?');
	if (p3)
		*p3 = '\0';

	// Redirect 처리용 URI 생성
	if (urihassvcname)
		red_uri = apr_pstrcat(r->pool, dav_http_scheme(r), "://", host_name, "/dav/", svc_name, uri, NULL);
	else
		red_uri = apr_pstrcat(r->pool, dav_http_scheme(r), "://", host_name, "/dav", uri, NULL);

	e_uri = ap_os_escape_path(r->pool, red_uri, 1);

	if (r->parsed_uri.query)
		e_uri = apr_pstrcat(r->pool, e_uri, "?", r->parsed_uri.query, NULL);

	//ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "dav_shared_ar_check_request: redirect uri[%s] escape uri[%s]",  red_uri, e_uri);

	apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, e_uri));
	ap_parse_uri(r, e_uri);

	return OK;
}

int dav_shared_ar_check_request(request_rec *r)
{

#if defined(__OPENDISK_SOLUTION__) || defined(__OPENDAV_SHARED_NOT_USE)
	return DECLINED;
#else

	dav_repos_server_conf *dsc = NULL;

	const char * domain_name;
	const char * p1;
	const char * p2;

	// 서비스명, uri, 무인증 폴더 정보 저장을 위한 변수
	char * svc_name = NULL;
	char * uri = NULL;
	char * folder_name = NULL;
	int svc_name_len = 0;
	int folder_name_len = 0;

	// 무인증에서 redirect 처리되어 들어오는 요청인지 여부 저장.
	bool is_redirect_request = false;

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

	// loop 처리 관련 변수
	int i = 0;


	// request 값이 NULL 경우
	if (!r)
		return DECLINED;


	// 무인증 관련 공유메모리 값이 NULL 인 경우
	if (!g_ar)
		return DECLINED;

	// conf 정보를 가져올 수 없는 경우
	dsc = dav_repos_get_server_conf(r->server);
	if (!dsc)
		return DECLINED;


	// 1. 무인증에 대한 처리는 GET 요청에 대해서만 허용한다. 나머지 요청은 opendav 모듈로 통과 처리.
	if (r->method_number != M_GET)
	{
		return OK;
	}

	// 2. HTTP Header 상에 인증 관련 Authorization 정보가 존재하는 경우.. 양방향 인증으로 처리되도록 통과  처리.
	if (apr_table_get(r->headers_in, "Authorization") != NULL)
	{
		return OK;
	}

	// 3. 요청에서 서비스 name 정보를 추출한다. ( 해당 서비스의 무인증 경로 요청인지 확인하기 위해 필요)
	// - HTTP Header 의 Host: 정보를 추출한다.
	// - 추출된 정보는 nctest.ktsh.co.kr, bd-04-fhs001.ktsh.co.kr, 222.122.165.9 이 될 수 있음.
	// - 추출 정보가 현재 장비명과 동일할 경우... URL 에서 서비스 name 정보 추출 시도.
	// - 추출 정보에서 IDC Domain 정보가 존재하는지 확인하여.. 
	// -- 존재할 경우 서비스 name 정보로 사용한다.
	// -- 존재하지 않을 경우 URL 에서 서비스 name 정보 추출 시도

	if (!(domain_name = apr_table_get(r->headers_in, "Host")))
	{
		// HTTP Header 상에 Host 정보가 존재하지 않을 경우
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_check_request: http Host tag not found. declined.");
		return DECLINED;
	}
	else
	{
		// 추출된 정보가 현재 장비명과 동일하지 비교. ( 대소문자 무시하고 비교 )
		if (strcasecmp(domain_name, dsc->host_name) != 0)
		{
			// 장비명과 동일하지 않은 경우...
			// IDC Domain Name 정보가 동일하지.. Host 추출 정보와 장비명을 비교한다. ( 대소문자 무시 )
			p1 = strchr(domain_name, '.');
			p2 = strchr(dsc->host_name, '.');

			if (p1 != NULL && strcasecmp(p1, p2) == 0)
			{
				// 동일한 경우...
				// 서비스 name 정보로 사용한다.
				svc_name_len = (p1 - domain_name);

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


	// 위에서 서비스 Name 추출이 실패한 경우.. URL 에서 서비스 Name 정보를 추출 시도한다.

	// 4. 우선 URL 에 대한 확인 및 필요시 변환 처리.
	ap_unescape_url(r->unparsed_uri);
	// 공유 메모리에 UTF-8로 저장되어 있기 때문에 변환 필요
	uri = transformUTF8(r, r->unparsed_uri, "dav_shared_ar_check_request");

	// 5. URL 에서 무인증 폴더 정보를 추출한다.
	//    추가로 앞에서 서비스 Name 정보를 추출하지 못한 경우.. 서비스 Name 정보도 추출 처리한다.
	// 일반, 무인증  요청      : /svc1_test/TEMP7.txt  or  /a 
	// 양방향 관련 요청        : /dav/svc1_test/temp1.txt	( /dav/ 경로명 : HTTP Authorization tag 존재로 인해 상위에서 우선  return 처리됨.)
	// 무인증 redirect 처리 후 : /dav/nctest/SVC1_TEST/temp2.txt   ( /dav/ 서비스 Name / 경로명 )

	// 5-1 uri 의 길이값을 확인
	//    /로 시작해야 하고.. 뒤에 경로 정보가 들어 있어야 하므로... 
	//    최소 형식은 /a/a, /dav 의 4 byte 이상이어야 한다.
	if (strlen(uri) < 4)
	{
		// 무인증 uri 규칙 위배 상황이고.. Authorization 정보도 존재하지 않으므로.. 거부 처리. 
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_check_request: uri not valid. length too small [%s]", uri);
		return DECLINED;
	}

	// 5-2 우선 /dav/ 로 시작하는 URI 인지 확인.
	if (strstr(uri, "/dav/") == uri)
	{
		// /dav/ 로 시작하는 uri 인 경우..
		// /dav/ 로 시작하는 부분을 건너뛰고... 다음 문자를 서비스 Name 으로 처리.... 그 다음을 경로로 처리..

		p1 = uri;
		p1 = p1 + 5;			// p1 ->  nctest/SVC1_TEST/temp2.txt 형식

		p2 = strchr(p1, '/');	// p2 ->  /SVC1_TEST/temp2.txt 형식
		if (p2 == NULL)
		{
			// 무인증 redirect 처리 규칙 위배 상황이고.. Authorization 정보도 존재하지 않으므로.. 거부 처리. 
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_check_request: redirect uri format not valid [%s]", uri);
			return DECLINED;
		}

		// 서비스 Name 정보를 추출하지 못한 경우...
		if (svc_name == NULL)
		{
			svc_name_len = (p2 - p1);
			svc_name = apr_pstrdup(r->pool, p1);
			*(svc_name + svc_name_len) = '\0';
		}

		// 무인증 폴더 정보를 추출한다.
		// 정상 : /SVC1_TEST/a.txt 형식
		// 오류 : /SVC1_TEST , // , //a.txt

		p2 = p2 + 1;			// p2 ->  SVC1_TEST/a.txt 형식
		p1 = strchr(p2, '/');
		if (p1 == NULL)
		{
			// 무인증 redirect 처리 규칙 위배 상황이고.. Authorization 정보도 존재하지 않으므로.. 거부 처리. 
			//ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_check_request: annoymous uri format not valid [%s]", uri );
			return DECLINED;
		}
		else
		{
			folder_name_len = (p1 - p2);

			if (folder_name_len < 1)	// 구조가 a/ 구조가 아닌 경우 즉.. //
			{
				// 무인증 요청 규칙 위배이므로 거부 처리.
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_check_request: annoymous folder name length not valid [%s]", uri);
				return DECLINED;
			}
			else
			{
				folder_name = apr_pstrdup(r->pool, p2);
				*(folder_name + folder_name_len) = '\0';	// folder_name -> SVC1_TEST 형식
			}
		}

		// redirect 처리되어 다시 들어온 요청임.
		is_redirect_request = true;
	}
	else
	{
		// /dav/ 로 시작하지 않는 경우...
		// 정상 : /svc1_test/temp7.txt, 
		// 특수 : /test.html ( 특정 고객사의 alive 체크 관련 지원 사항 ) => 그냥 OK 로 통과 처리.
		// 오류 : //, //a.txt 

		p1 = uri;		// p1 ->  /svc1_test/temp7.txt
		p1 = p1 + 1;	// p1 ->  svc1_test/temp7.txt

		p2 = strchr(p1, '/');	// p2 -> /temp7.txt
		if (p2 == NULL)
		{
			// 원래는 양방향 규칙 위배 상황이지만..
			// 특수한 상황으로 /test.html , /ok.html 등와 같이 고객사 지원 요청이 존재하여..  그냥 통과 처리한다.
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_check_request: uri format not valid.. but pass! [%s]", uri);
			return OK;
		}

		folder_name_len = (p2 - p1);
		if (folder_name_len < 1)	// 구조가 a/ 구조가 아닌 경우 즉.. //
		{
			// 무인증 요청 규칙 위배이므로 거부 처리.
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_check_request: annoymous folder name length not valid [%s]", uri);
			return DECLINED;
		}
		else
		{
			folder_name = apr_pstrdup(r->pool, p1);
			*(folder_name + folder_name_len) = '\0';	// folder_name -> svc1_test 형식
		}
	}

	// 6. 서비스 Name 정보를 확인하여 추출하지 못한 경우.. 서비스 거부 처리.
	if (svc_name == NULL)
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_check_request: svc name not found. http host info[%s] uri[%s]", domain_name, uri);
		return DECLINED;
	}

	// 6-1. 폴더  Name 정보를 확인하여 추출하지 못한 경우..그냥 통과 처리 
	if (folder_name == NULL)
	{
		ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "dav_shared_ar_check_request: folder name not found. svc_name[%s] uri[%s]. pass!", svc_name, uri);
		return OK;
	}

	// 7. 해당 서비스의 대소문자 무시 여부를 판단한다.
	result = dav_shared_is_uri_ignore_case_by_svcname(svc_name);
	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_shared_ar_check_request: dav_shared_is_uri_ignore_case_by_svcname func fail. svc name[%s]. use default", svc_name);

		is_uricaseignore = false;
	}

	// 8. 무인증 정보를 조회하여 해당 정보가 존재하는지 판단한다.
	//   - 무인증 정보가 존재할 경우.. 최초 무인증 요청인 경우.. RCDB 정보를 조회하여 redirect 처리 시켜 준다..
	//   - 최초 무인증 요청이 아니고.. redirect 처리되어 들어온 요청인 경우... Authorization 정보를 설정하여 opendav 로 전달 처리한다.
	for (i = 0; i < maxanon; i++)
	{
		// 무인증 설정 정보가 없는 경우 그냥 통과...
		if ((g_ar + i)->domain[0] == '\0')
			break;

		// 2015.7.15 dadamin
		// version 3.5부터 fimngd 의해서 설정된 flag 참조
		// 1 : 삭제
		if ((g_ar + i)->nFlag == 1)
			continue;
		
		// 대소문자 구분 여부에 따라.. 무인증 폴더 존재 여부 확인..

		// URI 대소문자 무시 서비스인 경우
		if (is_uricaseignore == true)
		{
			result = (!strcasecmp(folder_name, (g_ar + i)->uri)
				&& !strncasecmp(svc_name, (g_ar + i)->domain, svc_name_len)
				&& strlen((g_ar + i)->domain) == svc_name_len);

		}
		else // 양방향 기본 ( URI 대소문자 구분 )
		{
			result = (strcmp(folder_name, (g_ar + i)->uri) == OK
				&& !strncasecmp(svc_name, (g_ar + i)->domain, svc_name_len)
				&& strlen((g_ar + i)->domain) == svc_name_len);
		}

		// 전달받은 URL 이 무인증 폴더상에 존재하는 경우
		if (result)
		{

			// 최초 무인증 요청인 경우 301 redirect 처리..
			// redirect 처리된 요청인 경우... Authorization 설정 후 통과 처리.

			// 이미 redirect 처리되어 들어온 요청인 경우.
			if (is_redirect_request == true)
			{
				//ap_log_error( APLOG_MARK, APLOG_NOTICE, 0, NULL, "dav_shared_ar_check_request: already redirect request. set auth and pass. uri[%s]", uri);

				// HTTP Header 상에 Authorization Header 정보가 존재하지 않는 경우...  해당 정보 강제로 등록 처리.
				apr_table_setn(r->headers_in, "Authorization", apr_pstrdup(r->pool, "Basic REDIRECTED"));
				return OK;
			}
			else
			{
				// 최초 무인증 확인 요청인 경우
				if (dav_find_location(r, svc_name, uri, is_uricaseignore, true) == OK)
					return HTTP_MOVED_PERMANENTLY;

			}
		}
	}

	// 무인증 폴더 정보가 존재하지 않는 경우...
	// 그냥 opendav 로 통과 처리.
	return OK;

#endif // __OPENDISK_SOLUTION__
}

// CHG 2012-04-21 huibong
// 본 함수는 auth.c 에서 인증 관련 여부 확인 처리를 위해 호출되는 함수이지만..
// 거지 같은 처리 flow 로 구성되어...전면 재수정 처리한다.

// 본 함수는 들어온 요청이 무인증 관련 요청인지 확인하고... 
// 무인증 요청인 경우 URL 의 정보를 변경하고.. RedHost 상에 서비스 Name 정보를 전달한다.
int dav_shared_ar_get_value(request_rec *r)
{

#if defined(__OPENDISK_SOLUTION__) || defined(__OPENDAV_SHARED_NOT_USE)
	return DECLINED;
#else

	// 서비스명, uri, 무인증 폴더 정보 저장을 위한 변수
	const char * p1 = NULL;
	char * request_path = NULL;

	char * svc_name = NULL;
	char * uri = NULL;
	char * folder_name = NULL;
	int svc_name_len = 0;
	int folder_name_len = 0;

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

	// loop 처리 관련 변수
	int i;


	// 1. 변수값 검사
	if (!g_ar)
		return DECLINED;

	if (!r)
		return DECLINED;

	// 2. 무인증은 GET 요청만 허용.
	if (r->method_number != M_GET)
	{
		return DECLINED;
	}

	// NEW 2012-04-23 huibong
	// - HTTP Header 상에 인증 관련 Authorization 정보가 존재하고, dav_shared_ar_check_request() 함수에서 입력한 "Basic REDIRECTED" 정보가 아닌 경우.. 양방향 인증으로 처리되도록 처리.
	p1 = apr_table_get(r->headers_in, "Authorization");
	if (p1 != NULL)
	{
		if (strcmp(p1, "Basic REDIRECTED") != 0)
		{
			return DECLINED;
		}
	}

	// 3. URL 추출
	ap_unescape_url(r->unparsed_uri);
	// 공유 메모리에 UTF-8로 저장되어 있기 때문에 변환 필요
	uri = transformUTF8(r, r->unparsed_uri, "dav_shared_ar_get_value");

	// 3. 요청이 무인증 요청인지 확인한다.
	//    - 추가로 무인증 요청인 경우  무인증 정보 확인을 위한 서비스명, 폴더명을 추출한다.
	//  양방향 기본             : /dav/svc1_test/temp1.txt
	//  무인증 redirect 처리 후 : /dav/nctest/SVC1_TEST/temp2.txt   ( /dav/ 서비스 Name / 경로명 )
	//  무인증 최초 요청        : /svc1_test/TEMP7.txt
	//  기타                    : /a , /ok.html 등등.

	// 3-1 uri 의 길이값을 확인
	//    /로 시작해야 하고.. 뒤에 경로 정보가 들어 있어야 하므로... 
	//    최소 형식은 /a/a, /dav 의 4 byte 이상이어야 한다.
	if (strlen(uri) < 4)
	{
		// 무인증 uri 규칙 위배 상황이고.. Authorization 정보도 존재하지 않으므로.. 거부 처리. 
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_get_value: uri not valid. length too small [%s]", uri);
		return DECLINED;
	}

	// 3-2 /dav/ 로 시작해야 함.
	if (strstr(uri, "/dav/") == uri)
	{
		// /dav/ 로 시작하는 uri 인 경우..
		// /dav/ 로 시작하는 부분을 건너뛰고... 다음 문자를 서비스 Name 으로 처리.... 그 다음을 경로로 처리..

		p1 = uri;
		p1 = p1 + 5;						// p1 ->  nctest/SVC1_TEST/temp2.txt 형식

		request_path = strchr(p1, '/');	// request_path ->  /SVC1_TEST/temp2.txt 형식
		if (request_path == NULL)
		{
			// 무인증 redirect 처리 규칙 위배 상황
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_get_value: redirect uri format not valid [%s]", uri);
			return DECLINED;
		}

		// 서비스 Name 정보를 추출..
		svc_name_len = (request_path - p1);
		svc_name = apr_pstrdup(r->pool, p1);
		*(svc_name + svc_name_len) = '\0';

		// 무인증 폴더 정보를 추출한다.
		// 정상 : /SVC1_TEST/a.txt 형식
		// 오류 : /SVC1_TEST , // , //a.txt

		request_path = request_path + 1;	// request_path ->  SVC1_TEST/a.txt 형식
		p1 = strchr(request_path, '/');
		if (p1 == NULL)
		{
			// 무인증 redirect 처리 규칙 위배 상황.. 경로 정보 존재하지 않음.
			//ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_get_value: annoymous uri format not valid [%s]", uri );
			return DECLINED;
		}
		else
		{
			folder_name_len = (p1 - request_path);

			if (folder_name_len < 1)	// 구조가 a/ 구조가 아닌 경우 즉.. //
			{
				// 무인증 요청 규칙 위배이므로 거부 처리.
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_get_value: annoymous folder name length not valid [%s]", uri);
				return DECLINED;
			}
			else
			{
				folder_name = apr_pstrdup(r->pool, request_path);
				*(folder_name + folder_name_len) = '\0';	// folder_name -> SVC1_TEST 형식
			}
		}
	}

	// 4. 서비스 Name, folder name  정보를 확인하여 추출하지 못한 경우.. 무인증 요청이 아님.
	if (svc_name == NULL || folder_name == NULL)
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_ar_get_value: not valid annoymous request [%s]", uri);
		return DECLINED;
	}

	// 5. 해당 서비스의 대소문자 무시 여부를 판단한다.
	result = dav_shared_is_uri_ignore_case_by_svcname(svc_name);
	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_shared_ar_get_value: dav_shared_is_uri_ignore_case_by_svcname func fail. svc name[%s]. use default", svc_name);

		is_uricaseignore = false;
	}


	// 6. 무인증 정보를 조회하여 해당 정보가 존재하는지 판단한다.
	for (i = 0; i < maxanon; i++)
	{
		// 무인증 설정 정보가 없는 경우 그냥 통과...
		if ((g_ar + i)->domain[0] == '\0')
			break;

		// 2015.7.15 dadamin
		// version 3.5부터 fimnd 의해서 설정된 flag 값 참조
		// 1 : 삭제
		if ((g_ar + i)->nFlag == 1)
			continue;

		// 대소문자 구분 여부에 따라.. 무인증 폴더 존재 여부 확인..

		// URI 대소문자 무시 서비스인 경우
		if (is_uricaseignore == true)
		{
			result = (!strcasecmp(folder_name, (g_ar + i)->uri)
				&& !strncasecmp(svc_name, (g_ar + i)->domain, svc_name_len)
				&& strlen((g_ar + i)->domain) == svc_name_len);

		}
		else // 양방향 기본 ( URI 대소문자 구분 )
		{
			result = (strcmp(folder_name, (g_ar + i)->uri) == OK
				&& !strncasecmp(svc_name, (g_ar + i)->domain, svc_name_len)
				&& strlen((g_ar + i)->domain) == svc_name_len);
		}

		// 전달받은 URL 이 무인증 폴더상에 존재하는 경우
		if (result)
		{
			char * new_uri = NULL;

			// URI 부분 중 무인증 경로 관련 부분만 변환 처리 여부를 결정한다.
			if (uri == r->unparsed_uri)
			{
				new_uri = transformDBCharacter(r, request_path, "dav_shared_ar_get_value");
			}

			// iconv 변환 필요가 없거나...변환 실패시... 신규 URL 을 조립.
			if (!new_uri)
			{
				new_uri = apr_pstrcat(r->pool, "/dav/", request_path, NULL);
			}
			else
			{
				new_uri = apr_pstrcat(r->pool, "/dav/", new_uri, NULL);
			}

			ap_parse_uri(r, new_uri);
			ap_getparents(r->uri);

			// HTTP RedHost tag 상에는 서비스 Name 정보를 입력  처리한다.
			apr_table_setn(r->headers_in, "RedHost", svc_name);

			//ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "dav_shared_ar_get_value: new_uri [%s], RedHost [%s]", new_uri, svc_name);

			return OK;
		}
	}

	return DECLINED;


#endif // __OPENDISK_SOLUTION__
}
