Files
interactive/fhs/sh-opendav/repos.c
2026-08-07 17:38:18 +09:00

2701 lines
92 KiB
C

/****************************************************************************
mod_dav_pgsql (DASL) for apache 2.X
CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
Author : sean kim (sean@solutionbox.co.kr)
$Id: repos.c,v 1.6 2007/03/19 08:43:12 elenoa Exp $
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
****************************************************************************/
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <httpd.h>
#include <http_config.h>
#include <http_protocol.h>
#include <http_log.h>
#include <http_request.h>
#define CORE_PRIVATE
#include <http_core.h> /* for ap_construct_url */
#include <apr.h>
#include <apr_hash.h>
#include <apr_tables.h>
#include <apr_strings.h>
#include <apr_file_io.h>
#include "dav_repos.h"
#include "dbms.h"
#include "util.h"
#include "share_common.h"
#include "share_account.h"
#include "share_statistic.h"
#include "share_fhs_stat.h"
#include "transfer.h"
/*****************************/
/* common functions (static) */
/* to assist in debugging mod_dav's GET handling */
static const dav_hooks_liveprop dav_repos_hooks_liveprop;
/*
** The namespace URIs that we use. This list and the enumeration must
** stay in sync.
*/
static const char *const dav_repos_namespace_uris[] = {
"DAV:",
DEFAULT_NAMESPACE_URI,
NULL /* sentinel */
};
enum
{
dav_repos_URI_DAV, /* the DAV: namespace URI */
dav_repos_URI_MYPROPS /* the namespace URI for our custom props */
};
// DAV: namespace
#define DAV_PROPID_hidden DAV_PROPID_END +1
/*
** The single property that we define (in the DAV_FS_URI_MYPROPS namespace)
*/
#define DAV_PROPID_FS_executable 1
#define DAV_PROPID_FS_redhost 2
static const dav_liveprop_spec dav_repos_props[] = {
{
dav_repos_URI_DAV,
"creationdate",
DAV_PROPID_creationdate,
0},
{
dav_repos_URI_DAV,
"displayname",
DAV_PROPID_displayname,
0},
{
dav_repos_URI_DAV,
"getcontentlanguage",
DAV_PROPID_getcontentlanguage,
0},
{
dav_repos_URI_DAV,
"getcontentlength",
DAV_PROPID_getcontentlength,
0},
{
dav_repos_URI_DAV,
"getcontenttype",
// 2013.08.14 dadamin
// 잘못설정된 값 수정
DAV_PROPID_getcontenttype,
0},
{
dav_repos_URI_DAV,
"getetag",
DAV_PROPID_getetag,
0},
{
dav_repos_URI_DAV,
"getlastmodified",
DAV_PROPID_getlastmodified,
1},
{
dav_repos_URI_DAV,
"resourcetype",
DAV_PROPID_resourcetype,
0},
{
dav_repos_URI_DAV,
"source",
DAV_PROPID_source,
0 /* redirect host */
},
// 2013.08.16 dadamin
// DAV: 네임스페이스 hidden 속성 추가
{
dav_repos_URI_DAV,
"hidden",
DAV_PROPID_hidden,
1 /* handled special in dav_fs_is_writable */
},
{
dav_repos_URI_MYPROPS,
"executable",
DAV_PROPID_FS_executable,
0 /* handled special in dav_fs_is_writable */
},
{0} /* sentinel */
};
const dav_liveprop_group dav_repos_liveprop_group = {
dav_repos_props,
dav_repos_namespace_uris,
&dav_repos_hooks_liveprop
};
/* Find love prop and return its propid */
int dav_repos_find_liveprop( const dav_resource * resource, const char *ns_uri, const char *name, const dav_hooks_liveprop ** hooks )
{
/* don't try to find any liveprops if this isn't "our" resource */
if( resource->hooks != &dav_repos_hooks_repos )
return 0;
// 2013.08.14 dadamin
// mod_dav_fs 와 동일하게 구현함
return dav_do_find_liveprop(ns_uri, name, &dav_repos_liveprop_group, hooks);
/* Not found */
return 0;
}
/* Inser all live props from live prop hash */
void dav_repos_insert_all_liveprops( request_rec * r, const dav_resource * resource, dav_prop_insert what, apr_text_header * phdr )
{
apr_ssize_t klen;
const char *s;
const char *key, *val;
apr_hash_index_t *hindex;
dav_repos_resource *db_r;
/* don't try to find any liveprops if this isn't "our" resource */
if (resource->hooks != &dav_repos_hooks_repos)
return;
db_r = (dav_repos_resource *) resource->info->db_r;
if( !resource->exists || db_r == NULL )
{
/* a lock-null resource */
/*
** ### technically, we should insert empty properties. dunno offhand
** ### what part of the spec said this, but it was essentially thus:
** ### "the properties should be defined, but may have no value".
*/
apr_text_append(r->pool, phdr, "<D:resourcetype/>");
return;
}
/* Read live props from hash */
for( hindex = apr_hash_first(r->pool, db_r->lpr_hash); hindex; hindex = apr_hash_next(hindex) )
{
apr_hash_this(hindex, (void *) &key, &klen, (void *) &val);
s = apr_psprintf(r->pool, "<D:%s>%s</D:%s>" DEBUG_CR, key, val, key);
apr_text_append(r->pool, phdr, s);
}
}
/*
** get resource and return to result_resource
*/
// 2012.04.17 : dadamin, 리턴 코드 별 redirect head code setting
static void dav_repos_set_redirect_head(request_rec *r, dav_repos_resource *db_r, int respcode, const char * redirect_domain)
{
if( redirect_domain )
{
char* sz_uri_tmp = NULL;
char* szuri = NULL;
char szsrc[4096] = { 0 };
// 2013.01.11 dadamin
// 무인증 요청에 대한 301 경우 /dav/서비스이름/파일URI로 리턴 해줘야함
const char* redhost = NULL;
redhost = apr_table_get(r->headers_in, "RedHost");
// 2016.01.18 dadamin
// SQL escape string 제거
dav_repos_restore_unsafe_chars(db_r->m_uri, szsrc);
if(redhost)
{
char *sztmp = apr_pstrcat(r->pool, "/dav/", redhost, szsrc + 4, NULL);
szuri = ap_escape_uri(r->pool, sztmp);
}
else
{
// 2012.07.06 : 사용하고 있는 m_uri 값은 unescape 한 값이므로 이를 리턴 시
// 오동작하는 클라이언트 존재(NetCache - 공백오류) 하기 때문에 해당 값을 escape한 값으로 변경하여 리턴
szuri = ap_escape_uri(r->pool, szsrc);
}
sz_uri_tmp = apr_pstrcat(r->pool, dav_http_scheme(r), "://", redirect_domain, szuri, NULL);
if (r->parsed_uri.query)
{
sz_uri_tmp = apr_pstrcat(r->pool, sz_uri_tmp, "?", r->parsed_uri.query, NULL);
}
switch(respcode)
{
case HTTP_MOVED_PERMANENTLY:
apr_table_setn(r->headers_out, "Location", sz_uri_tmp);
break;
case HTTP_NOT_FOUND:
apr_table_setn(r->err_headers_out, "Location", sz_uri_tmp);
apr_table_setn(r->err_headers_out, "Redirect", sz_uri_tmp);
break;
default:
apr_table_setn(r->headers_out, "Location", sz_uri_tmp);
break;
}
}
}
// 2012.04.16 : dadamin, get_resource함수에서 PUT 관련 처리
static dav_error *dav_repos_get_resource_put(int dbresult, request_rec * r, dav_repos_resource *db_r, dav_resource_private *ctx, bool is_uricaseignore)
{
int res = 0;
dav_repos_server_conf *dsc = dav_repos_get_server_conf(r->server);
// 세션에 대한 input 트래픽 통계 필터 활성화
ap_add_input_filter("dav_input_statistic", NULL, r, r->connection);
// file exists
if( dbresult > 0 )
{
const char *p_length = apr_table_get(r->headers_in, "Content-Length");
const char *p_modify_time = apr_table_get(r->headers_in, "ModifyTime");
// PUT 요청 중 SBFS, NetCache 지원을 위한 PUT 0 요청인지 검사
if(p_length && p_modify_time)
{
long long ll_length = atoll(p_length);
if(ll_length == 0) // PUT 요청이지만.. Content length 값이 0 인 PUT 0 요청이고 기존 Content 가 존재.. file_lastmodified 값에 대한 업데이트 요청인 경우
{
// CHG 2010-01-18 huibong
// ModifyTime info save function changed ( Do not use dbms_update_getlastmodified )
// CHG 2012-03-23 huibong
// 서비스별 URI 대소문자 구분/무시 기능 추가 관련 인자값 추가.
int ret = dbms_update_file_lastmodified(db_r, atoll(p_modify_time), is_uricaseignore );
if(ret == -1)
{
// Meta 정보 업데이트 실패시
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: PUT 0 [%s] ModifyTime[%s] update fail" , db_r->m_fake_uri, p_modify_time);
return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "dav_repos_get_resource(dav_repos_get_resource_put ModifyTime fail)");
}
else
{
// 정상 처리시 HTTP 204 NO_CONTENT 값 반환 처리
return solbox_dav_new_error(r->pool, HTTP_NO_CONTENT, 0, "dav_repos_get_resource(dav_repos_get_resource_put ModifyTime success)");
}
}
}
// 기존 정보가 존재하지만 PUT 0 처리 요청이 아닌 경우
if( strcmp(db_r->m_host_name, dsc->host_name) && !r->header_only)
{
// 기존 존재하는 Content 에 대한 PUT 요청이지만... 해당 Content 를 저장한 원본 장비가 아닌 경우....
// 해당 Content 를 가진 원본 장비로 redirect 처리.
//dav_repos_set_redirect_head(r, db_r, HTTP_MOVED_PERMANENTLY, db_r->m_host_name);
//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: request[PUT] redirect info set [%s]. local[%s]", db_r->m_host_name, dsc->host_name);
//return solbox_dav_new_error(r->pool, HTTP_MOVED_PERMANENTLY, 0, "Moved Permanently");
// 2018-02-27 CHG huibong
// - NetCache 업로드 서버 관련 PUT 요청에 대해
// - 원본 서버가 죽은 경우 HTTP 301 을 주면 죽은 장비로 접속을 시도하면서 문제가 RC offline 문제가 발생해서
// - 다른 업로드 역시 문제가 생김
// - 이를 해결하기 위해서... NetCache 업로드 서버의 PUT 요청에서 원본 파일을 가진 FHS 가 죽은 경우
// - 무조건 HTTP 301 이 아닌 HTTP 410 응답을 주도록 한다.
// - 본 수정 사항에 의한 효과는 장애 탐지 약 10 sec 정도 걸리므로 거의 효과는 없는 것으로 판단됨.
const char * userAgent = NULL;
userAgent = apr_table_get( r->headers_in, "User-Agent" );
// NetCache 업로드 서버인 경우
if( userAgent != NULL && ( strncmp( userAgent, "NetCache", 8 ) == 0 ) )
{
// 원본 서버가 살아 있는지 검사한다.
if( dav_shared_du_is_alive( db_r->m_host_name ) != 1 )
{
// 죽은 경우
ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: request[PUT] source[%s] dead. return HTTP_GONE.", db_r->m_host_name );
return solbox_dav_new_error( r->pool, HTTP_GONE, 0, "Source server dead" );
}
}
// 그 외 상황인 경우.. 죽든 살았던 원본 FHS 로 Redirection 처리
dav_repos_set_redirect_head( r, db_r, HTTP_MOVED_PERMANENTLY, db_r->m_host_name );
ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: request[PUT] redirect info set [%s]. local[%s]", db_r->m_host_name, dsc->host_name );
return solbox_dav_new_error( r->pool, HTTP_MOVED_PERMANENTLY, 0, "Moved Permanently" );
}
}
else
{
char *tmpPathName = apr_pcalloc(r->pool, MaxSizeOfPathLength);
char *tmpSubPath = apr_pcalloc(r->pool, MaxSizeOfPathLength);
char *tmpFileName = apr_pcalloc(r->pool, MaxSizeOfFileNameLength);
char *pPlainPathName = NULL;
char *pFileToBeHashed = NULL;
char *pHashedPathName = NULL;
int bPut0Mode = 0;
const char *p_mode = NULL;
const char *p_slength = NULL;
const char *force = NULL;
long long slength = -1;
char *domain = NULL;
/* Get plain filepath */
/* Get pathname and filename from pPlainPathName */
res = get_filename_in_path( db_r->m_fake_uri, tmpPathName, tmpFileName );
if(res == -1)
{
return solbox_dav_new_error(r->pool, HTTP_BAD_REQUEST, 0,"Bad request(dav_repos_get_resource_put,get_filename_in_path)");
}
else if( res == -2 )
{
return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,"Internal server error(dav_repos_get_resource_put, get_filename_in_path)");
}
tmpSubPath[0] = '\0';
#ifdef __USE_MULTIPLE_STORAGE__
if( get_writable_path(r->pool, dsc->storage_dir, tmpSubPath) == -1 )
{
// 2014.02.28 dadamin
// 쓰기할 디스크 선택 실패 시 에러 리턴 : 507
return solbox_dav_new_error(r->pool, HTTP_INSUFFICIENT_STORAGE, 0,"There is logically not enough storage to write to this resource.(get_writable_path error)");
}
#endif
pPlainPathName = apr_psprintf(r->pool, "%s/%" APR_INT64_T_FMT "/%s",
#ifdef __USE_MULTIPLE_STORAGE__
(tmpSubPath[0] ? tmpSubPath :
#endif
dsc->storage_dir
#ifdef __USE_MULTIPLE_STORAGE__
)
#endif
, ctx->user_id
, tmpFileName);
// 2014.06.19 dadamin
// 동시간대 동일한 uri에 대한 hash 충돌 최소화를 위해서 microseconds 단위 값으로 time 구함
/* Get filename hashed */
pFileToBeHashed = apr_psprintf(r->pool, "%" APR_INT64_T_FMT "%s%s%s%" APR_TIME_T_FMT"", ctx->user_id
, db_r->m_fake_uri
, tmpFileName
, MD5Private
, apr_time_now());
// CHG 2010-08-10 huibong
// get_hashed_filepath 함수 정의 변경에 따른 코드 수정
char szHashedFileName[APR_MD5_DIGESTSIZE * 2 + 1];
if( get_hashed_filepath( pFileToBeHashed, szHashedFileName, sizeof(szHashedFileName) ) == false )
{
// Write error log
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource:get_hashed_filepath return false.[%s][%s][%zu]", pFileToBeHashed, szHashedFileName, sizeof(szHashedFileName));
// Error Return.
return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,"Internal server error(get_hashed_filepath)");
}
pHashedPathName = apr_psprintf(r->pool, "%s/%" APR_INT64_T_FMT "/%s",
#ifdef __USE_MULTIPLE_STORAGE__
(tmpSubPath[0] ? tmpSubPath : dsc->storage_dir)
#else
dsc->storage_dir
#endif
, ctx->user_id, szHashedFileName );
db_r->m_filename_org = pPlainPathName;
db_r->m_filename_hash = pHashedPathName;
ctx->pathname = pHashedPathName;
// NEW 2011-11-29 huibong
// request_rec 구조체 canonical_filename 필드상에 실제 Local 물리 파일 정보가 저장되도록 기능 추가
// 이는 Transfer Log 정보상에 물리 파일 정보가 저장되도록 처리하기 위함임.
// 본 코드는 Content 업로드 처리시 생성되는 물리 파일명을 설정하기 위한 코드임.
r->canonical_filename = pHashedPathName;
// PUT 0 Force Mode Check
// PUT 0 Force mode
// => Redirect 처리를 수행하지 않고 201을 반환 (Location 정보 처리)
// PUT 0 mode
// => Redirect 여부 검사, Redirect 대상이 자기자신인 경우 201 반환 (Location 정보처리)
p_mode = apr_table_get(r->headers_in, "Mode");
p_slength = apr_table_get(r->headers_in, "Content-Length");
force = apr_table_get(r->headers_in, "Force");
if(p_slength)
slength = atoll(p_slength);
// M_PUT method, Length=0 => PUT 0 MODE
if( (slength == 0) && p_mode!=NULL && (strncmp(p_mode, "Create", 6) == 0))
bPut0Mode = 1;
// PUT 0 Force Mode
if( bPut0Mode == 1 )
{
if(force)
{
// PUT 0 Location Setting function call => 201 Code & Location info return..
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: PUT0 force mode. return 201 & location[%s]", dsc->host_name);
}
else
{
// Rediection 처리할 domain 정보 검색
// 오류발생시 NULL 을 반환함.
domain = dav_shared_du_put_target(r->pool, dsc->host_name);
// 현재 장비의 host_name 이 존재하고
// dav_shared_du_put_target() 함수의 반환값이 NULL 이 아니며
// host_name 과 dav_shared_du_put_target() 함수의 반환값이 다른 경우 => 서로 다른 장비
// => 기본 Redirection 처리
if(dsc->host_name && domain && strncmp(dsc->host_name, domain, strlen(dsc->host_name)))
{
// Send redirect information & error code 301
dav_repos_set_redirect_head(r,db_r, HTTP_MOVED_PERMANENTLY, domain);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: PUT0 redirect 301. target[%s]", domain);
return solbox_dav_new_error(r->pool, HTTP_MOVED_PERMANENTLY, 0, "Moved Permanently");
}
}
}
}
return NULL;
}
// 2012.04.16 : dadamin, get_resource 함수에서 GET 관련 처리
static dav_error *dav_repos_get_resource_get(int dbresult, request_rec * r, dav_repos_resource *db_r, dav_resource_private *ctx, bool is_uricaseignore)
{
const char * reqhost = NULL;
dav_repos_server_conf *dsc = dav_repos_get_server_conf(r->server);
int redirectmode = 0;
// file exists
if( dbresult > 0)
{
reqhost = apr_table_get(r->headers_in, "Host");
if(reqhost && strcmp(reqhost, dsc->host_name) )
{
// 2012.04.26 dadamin : 무인증 요청 대한 판단
// 무인증이면 램덤 선택하지 않고 기존과 동일하게 자신 가진 파일은 서비스함
const char* redhost = NULL;
redhost = apr_table_get(r->headers_in, "RedHost");
if ( redhost )
{
#ifdef __CLOUD_STREAMING__
// 2013.08.06 : Head 요청인 경우 램덤처리
// StarPlayer 경우 RedHost 값을 전달 해주기 때문에
// 램덤 처리 지원을 위해서 해당 요청이 HEAD 요청인 경우
// 램덤 처리 한다.
if(r->header_only)
redirectmode = 1;
else
#endif // __CLOUD_STREAMING__
redirectmode = 3;
}
else
{
// 2012.04.19 dadamin : 해당 장비와 request header의 host 비교 다르면 램덤 처리
redirectmode = 1;
}
}
else if( strcmp(db_r->m_host_name, dsc->host_name) && !r->header_only )
{
// 기존 존재하는 Content 에 대한 GET 요청이지만.. 해당 Content 를 저장한 원본 장비가 아닌 경우....
// replication, cache 된 Content 가 존재하는지 검사하여.. 해당 장비를 정보를 확인한다.
redirectmode = 2;
}
else
{
redirectmode = 0;
}
// 2012.04.19 dadamin
// redirect mode ; 1 - 램덤, 2 - 해당 장비 검사 & 램덤, 3 - 무인증 요청, 해당 장비 검사 & 램덤, 0 - 해당 장비임
if(redirectmode > 0)
{
const char * redirect_domain = NULL;
int error_reponse_code = 0;
redirect_domain = dbms_get_redirect(db_r, is_uricaseignore, (redirectmode == 1), &error_reponse_code );
if( redirect_domain == NULL )
{
// redirect 정보 조회 함수상에서 오류가 발생한 경우 : 전달받은 HTTP error code 반환
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: dbms_get_redirect func failed [mode:%d]. return reponse code [%d]", redirectmode, error_reponse_code );
return solbox_dav_new_error(r->pool, error_reponse_code, 0, "Redirection target select fail");
}
if( strcmp( redirect_domain, dsc->host_name ) )
{
dav_repos_set_redirect_head(r,db_r, HTTP_MOVED_PERMANENTLY, redirect_domain);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: request[GET] redirect[mode:%d] info set [%s]. local[%s]", redirectmode, redirect_domain, dsc->host_name);
return solbox_dav_new_error(r->pool, HTTP_MOVED_PERMANENTLY, 0, "Moved Permanently");
}
}
// 2012.06.13 dadamin
// 장비에 저장 위치가 다를 수 있기 때문에 m_filename_hash를 다시 설정
r->canonical_filename = (char *)db_r->m_filename_hash;
}
return NULL;
}
// 2012.04.16 : dadamin, get_resource 함수에서 PROPFIND 관련 처리
static dav_error *dav_repos_get_resource_propfind(int dbresult, request_rec * r, dav_repos_resource *db_r, dav_resource_private *ctx, bool is_uricaseignore)
{
dav_repos_server_conf *dsc = dav_repos_get_server_conf(r->server);
if( dbresult == 0 )
{
char *domain = NULL;
domain = dav_shared_du_put_target(r->pool, dsc->host_name);
if (domain && strcmp(domain, dsc->host_name))
{
dav_repos_set_redirect_head(r,db_r, HTTP_NOT_FOUND, domain);
#ifdef __OPENDAV_DEBUG__
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: PROPFIND redirect target[%s]", domain);
#endif // __OPENDAV_DEBUG__
}
else
{
// 2012.02.01 : Netcache 경우 Header에 Location 정보 설정되게 한다.
// NetCache Agent 인지를 확인
const char * userAgent = NULL;
userAgent = apr_table_get( r->headers_in, "User-Agent");
if( userAgent != NULL && (strncmp(userAgent, "NetCache", 8)==0) )
{
dav_repos_set_redirect_head(r, db_r, HTTP_NOT_FOUND, dsc->host_name);
#ifdef __OPENDAV_DEBUG__
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: PROPFIND NetCache redirect target myself[%s]", dsc->host_name);
#endif // __OPENDAV_DEBUG__
}
}
}
else
{
// NEW 2011-03-23 dadamin
// 각 서비스 디렉토리 용량 정보 조회 기능을 추가한다.
// Request 가 PROPFIND & HTTP Header 상에 Service-Quota: YES 가 설정되어 있는 경우
// tran id 정보를 이용하여 서비스 용량( Total, 사용가능 용량) 정보를 RCDB 상에서 조회한다.
// 조회된 결과는 HTTP Header 상에 Service-Quota: Total Byte - Used Byte 형식으로 조립되어 전달된다.
// 그리고 조회결과가 유효하지 않는 경우에는 에러를(HTTP_UNPROCESSABLE_ENTITY(422)) 리턴한다.
const char * ServiceQuota = apr_table_get(r->headers_in, "Service-Quota");
if(ServiceQuota != NULL && (strncmp(ServiceQuota, "YES", 3)==0))
{
// 값을 전달받기 위한 변수 초기화.
apr_int64_t service_total_byte = 0;
apr_int64_t used_byte = 0;
char *ptran_id = NULL;
ptran_id = apr_psprintf(r->pool, "%" APR_INT64_T_FMT "", ctx->user_id);
if( dbms_get_service_quotaex( db_r, ptran_id, is_uricaseignore, &service_total_byte, &used_byte ) == 1)
{
// 각 변수값이 유효한지 여부 검사
if( service_total_byte < 0 )
service_total_byte = 0;
if( used_byte < 0 )
used_byte = 0;
// HTTP Header 정보 설정 : 정상인 경우에만 전달되도록 설정
apr_table_setn( r->headers_out, "Service-Quota", apr_psprintf(db_r->p, "%" APR_INT64_T_FMT "-%" APR_INT64_T_FMT"", service_total_byte, used_byte));
// 로깅 처리
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: Service-Quota result tranID[%s] uri[%s] total[%" APR_INT64_T_FMT "] used[%" APR_INT64_T_FMT "]", ptran_id, db_r->m_fake_uri, service_total_byte, used_byte );
}
else
{
// 서비스 용량 정보 조회 실패시 422 에러를 리턴한다.
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: Service-Quota fail. tranID[%s] uri[%s]" , ptran_id, db_r->m_fake_uri );
// 없는 경우 에러를 리턴한다.
return solbox_dav_new_error(r->pool, HTTP_UNPROCESSABLE_ENTITY, 0,"Bad Request status(Service-Quota).");
}
}
// 2012.04.26 dadamin : 응답 header에 해당 서비스에 대한 대소문자 구분 여부 코드 추가
// X-SB-Case-Sensitive : yes (구분), no (무시)
apr_table_setn( r->headers_out, "X-SB-Case-Sensitive", apr_psprintf(db_r->p, "%s", (is_uricaseignore ? "no" : "yes" )));
// 2013.08.23 dadamin
// X-SB-Show-hidden:no 이거나 없으면 파일(디렉토리) 경우 404 리턴함
const char * showhidden = apr_table_get(r->headers_in, "X-SB-Show-hidden");
if(showhidden == NULL || (strncmp(showhidden, "no", 2)==0))
{
if(db_r->m_source )
{
return solbox_dav_new_error(r->pool, HTTP_NOT_FOUND, 0,"The file(directory) is hidden.");
}
}
// 2013.08.23 dadamin
// namespace 정보 초기값(DAV:) 강제 지정한다.
// 이는 하위 정보에 대해서 네임스페이스 해쉬테이블 중복 생성을 막기 위함
if( db_r->ns_hash == NULL)
{
dav_repos_property pr;
pr.m_ns_id = 0;
dbms_set_namespace(db_r, &pr, "DAV:");
dbms_set_ns_id(db_r, "DAV:", &pr);
}
}
return NULL;
}
// 2012.04.16 : dadamin, 기존 dav_repos_get_resource에 대한 정리
// 각 요청에 대한 최초 진입 함수..
static dav_error *dav_repos_get_resource( request_rec * r, const char *root_path, const char *label, int use_checked_in, dav_resource ** result_resource )
{
int res = 0;
dav_resource_private *ctx;
dav_resource *resource;
dav_repos_server_conf *dsc = NULL;
char *pBaseURI = NULL;
char *pOrgURI = NULL;
char *new_uri = NULL;
dav_repos_resource *db_r = NULL;
acct_info user_login_info;
dav_error *err = NULL;
char *tmpstr = apr_pcalloc(r->pool, MaxSizeOfPathLength);
char *tmpstr2 = apr_pcalloc(r->pool, MaxSizeOfPathLength);
// NEW 2012-03-20 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
bool is_uricaseignore = false;
dsc = dav_repos_get_server_conf(r->server);
// 모든 메소드에서 대해서 자신의 상태을 체크 실패 시 500 리턴 하던 구조에서
// User-Agent:NetCache 경우에 대해서 GET/ 단일 파일에 대한 PROPFIND 에 대해서
// alive 상태된 장비가 없을 시 500 에러를 리턴되게 수정함
// Check the status of self.
//if( dav_shared_du_is_alive(dsc->host_name) == 0 )
//{
// ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Check of alive failed. Send HTTP_INTERNAL_SERVER_ERROR. [%s]" , r->uri);
// return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,"Internal server error(My self isn't alive.)");
//}
// Delete the request URI handling multi-slash
memset(tmpstr, 0, MaxSizeOfPathLength);
strcpy(tmpstr, r->the_request);
dav_repos_replace_multi_slash_chars(tmpstr, r->the_request);
memset(tmpstr, 0, MaxSizeOfPathLength);
strcpy(tmpstr, r->uri);
dav_repos_replace_multi_slash_chars(tmpstr, r->uri);
/* Create private resource context descriptor */
ctx = apr_pcalloc(r->pool, sizeof(*ctx));
ctx->finfo = r->finfo;
db_r = apr_pcalloc(r->pool, sizeof(*db_r));
db_r->p = r->pool;
db_r->r = r;
ctx->db_r = db_r;
ctx->finfo = r->finfo;
ctx->pool = r->pool;
ctx->rec = r;
/* webdrive sends "OPTION" to know which properties are supported by
* server, it happens when "connect" button clicked on the login
* configuration screen. */
if( r->user == NULL )
{
resource = apr_pcalloc(r->pool, sizeof(*resource));
resource->type = DAV_RESOURCE_TYPE_REGULAR;
resource->info = ctx;
resource->hooks = &dav_repos_hooks_repos;
resource->pool = r->pool;
resource->exists = 0;
resource->uri = apr_psprintf(r->pool, "%s", "");
*result_resource = resource;
return NULL;
}
/* login info */
if( at_split(r->user, &user_login_info) < 0)
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Login info parser (at_split).");
return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,"Internal server error(at_split)");
}
if (atoi(user_login_info.szVolumeID) <= 0 || atoi(user_login_info.szUserID) <= 0)
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Login info empty.");
return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,"Internal server error(login info)");
}
ctx->user_id = atoll(user_login_info.szVolumeID);
// NEW 2012-03-20 유희곤
// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
res = dav_shared_is_uri_ignore_case_by_tranid( user_login_info.szVolumeID );
if( res == 1 ) // URI 대소문자 무시 서비스인 경우
{
is_uricaseignore = true;
}
else // 양방향 기본 ( URI 대소문자 구분 )
{
// 만약 dav_shared_is_uri_ignore_case_by_tranid 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
if( res == -1 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: dav_shared_is_uri_ignore_case_by_tranid func fail. tranID[%s]" , user_login_info.szVolumeID );
}
is_uricaseignore = false;
}
/*
** If there is anything in the path_info, then this indicates that the
** entire path was not used to specify the file/dir. We want to append
** it onto the filename so that we get a "valid" pathname for null
** resources.
*/
pBaseURI = apr_psprintf(r->pool, "%s", dsc->base_uri);
dav_repos_no_trail(pBaseURI);
/* get rid of base uri */
pOrgURI = apr_pstrdup(r->pool, r->uri);
dav_repos_no_trail(pOrgURI);
memset(tmpstr, 0, MaxSizeOfPathLength);
memset(tmpstr2, 0, MaxSizeOfPathLength);
dav_repos_remove_base_uri(pBaseURI, r->uri, tmpstr);
/* get rid of unsafe chars, which is like " ' ", " \ " */
dav_repos_replace_unsafe_chars(tmpstr, tmpstr2);
r->uri = apr_pstrdup(r->pool, tmpstr2);
dav_repos_no_trail(r->uri);
/* make sure the URI does not have a trailing "/" */
new_uri = apr_psprintf(r->pool, "/%" APR_INT64_T_FMT "%s", ctx->user_id, r->uri);
dav_repos_no_trail(new_uri);
db_r->m_fake_uri = apr_pstrdup(r->pool, new_uri);
memset(tmpstr, 0, MaxSizeOfPathLength);
dav_repos_replace_unsafe_chars(pOrgURI, tmpstr);
db_r->m_uri = apr_pstrdup(r->pool, tmpstr);
// 2014-04-24 dadamin
// depth - uri 기준의 인덱스 수행하기 위해서 현재 path의 depth를 구한다.
/* Calculate depth */
db_r->m_depth = ap_count_dirs(db_r->m_fake_uri) - 1;
/* Create resource descriptor */
resource = apr_pcalloc(r->pool, sizeof(*resource));
resource->type = DAV_RESOURCE_TYPE_REGULAR;
resource->info = ctx;
resource->hooks = &dav_repos_hooks_repos;
resource->pool = r->pool;
db_r->r = r;
// 해당 URI 가 이미 존재하는지 확인
res = dbms_get_property(db_r, is_uricaseignore );
if ( res < 0)
{
// 2012-04-04 : dadamin
// database에 대한 오류 처리하며,에러코드는 503 에러를 리턴함
if(res == -2)
return solbox_dav_new_error(r->pool, HTTP_SERVICE_UNAVAILABLE, 0, "dav_repos_get_resource : Database error.");
else if (res == -5)
{
// 2012.06.13 : dadamin
// NetCahce 경우 alive 된 FHS 없을 경우 에러를 리턴함
//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "NetCache PROPFIND random Error. return HTTP_INTERNAL_SERVER_ERROR. [%s]" , r->uri);
//return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,"Internal server error(Empty Alive FHS)");
// 2018-02-12 CHG huibong
// - alive FHS 가 없는 경우 NetCache Client 한데.. HTTP 500 응답을 주면 RC 을 OFFLINE 처리하므로... 다른 content 관련 장애 발생
// - 따라서 FHS 가 일시적으로 죽은 경우 HTTP 500 이 아닌 410 응답을 주도록 한다.
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "NetCache PROPFIND random Error. return HTTP_GONE. [%s]" , r->uri);
return solbox_dav_new_error(r->pool, HTTP_GONE, 0, "Alive server not found" );
}
else
return solbox_dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, "dav_repos_get_resource : SQL error.");
}
// uri exists.
if( res > 0 )
{
// 2012.04.16 : dadamin
// 원본이 2개 이상인 경우 에러 처리하지 않으며, 단순히 error log에 로깅한다.
if ( res > 1 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_resource: There are two or more sources. uri [%s]" , db_r->m_fake_uri);
}
resource->exists = 1;
ctx->pathname = db_r->m_filename_hash;
// NEW 2011-11-29 huibong
// request_rec 구조체 canonical_filename 필드상에 실제 Local 물리 파일 정보가 저장되도록 기능 추가
// 이는 Transfer Log 정보상에 물리 파일 정보가 저장되도록 처리하기 위함임.
// 본 코드는 기존 Content 가 존재하는 경우 RCDB 에서 조회된 물리 파일명을 저장하는 코드임.
r->canonical_filename = (char *)db_r->m_filename_hash;
/* Is collection ? */
if( db_r->m_resource_type == dav_repos_COLLECTION )
{
resource->collection = APR_DIR;
}
ctx->finfo.fname = r->finfo.fname = db_r->m_filename_org;
ctx->finfo.size = r->finfo.size = db_r->m_get_content_length;
}
else
{
resource->exists = 0;
/* FIXME: Hum. Deal locknull */
r->path_info = "";
}
switch (r->method_number)
{
case M_PUT:
{
err = dav_repos_get_resource_put(res, r, db_r, ctx, is_uricaseignore);
}
break;
case M_GET:
{
// 2012.07.26 dadamin
// HTTP HEAD 요청에 대해서 apache 는 GET, header only로 설정된다.
// 파일 경우 : 해당 로직 수행
// 디렉토리 경우 : 해당 로직 수행하지 않음
if(r->header_only && db_r->m_resource_type != 0)
{
// nothing and 200 return.
}
else if( db_r->m_resource_type != 0 )
{
// NEW 2019-11-21 huibong 폴더에 대한 GET 응답 관련 기능 개선 (#32860)
// - 폴더에 대한 GET 요청시 불필요한 HTTP 301 발생 후 HTTP 409 응답 발생
// - 최상위 폴더에 대한 GET 요청시 무한 HTTP 301 발생
// - 이를 해결하기 위해 폴더에 대한 GET 요청은 HTTP 400 Bad Request 로 응답하도록 수정
ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "direcotory GET request. return HTTP_BAD_REQUEST. [%s]", r->uri );
return solbox_dav_new_error( r->pool, HTTP_BAD_REQUEST, 0, "dav_repos_get_resource: directory GET request is bad request." );
}
else
{
err = dav_repos_get_resource_get(res, r, db_r, ctx, is_uricaseignore);
}
}
break;
case M_PROPFIND:
{
err = dav_repos_get_resource_propfind(res, r, db_r, ctx, is_uricaseignore);
}
break;
default:
{
err = NULL;
if( res == 0)
{
// 2012.04.16 : dadamin
// move, copy 시 dest uri에 대하여 상위 로직에서 해당 값을 참조 시
// Segmentation fault (11) 오류 방지하기 위해서 임시 파일명을 부여한다.
ctx->pathname = apr_psprintf(r->pool,"/tmp/path%s",r->uri);
}
}
break;
}
if(err != NULL)
return err;
//execute_remain:
/* make sure the URI does not have a trailing "/" */
char *s = apr_pstrdup(r->pool, db_r->m_uri);
dav_repos_no_trail(s);
resource->uri = s;
/* Set return value */
*result_resource = resource; /* <- "/repos" */
return NULL;
}
/* Should we return NULL, if no parent ? */
// 2012-03-21 huibong 함수 내역 추가
// 본 함수는 주어진 URI 에 대한 상위 경로 정보가 존재하는지 확인하기 위한 함수이다.
// 본 함수의 반환값은 현재 무조건 NULL 이며....
// result_parent 값에 상위 정보를 입력하여 반환처리한다...
static dav_error *dav_repos_get_parent_resource(const dav_resource * resource, dav_resource ** result_parent)
{
char *szRealURI = apr_pcalloc(resource->pool, MaxSizeOfPathLength);
dav_resource_private *ctx = resource->info;
dav_resource_private *parent_ctx;
dav_resource *parent_resource;
dav_repos_resource *db_r;
char *dirpath;
char *parenturi = NULL;
// NEW 2012-03-20 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
int result = 0;
bool is_uricaseignore = false;
/* If given resource is root, then there is no parent */
if( strcmp(resource->uri, "/") == 0 || strcmp(ctx->pathname, "/") == 0 )
{
*result_parent = NULL;
return NULL;
}
if( resource->uri != NULL )
{
parenturi = ap_make_dirstr_parent(resource->pool, resource->info->db_r->m_fake_uri);
dav_repos_no_trail(parenturi);
}
/* fill DBR to check collection type */
db_r = apr_pcalloc(resource->pool, sizeof(*db_r));
db_r->r = resource->info->db_r->r;
db_r->p = resource->pool;
remove_user_root_dir_from_fake_uri(parenturi, szRealURI);
db_r->m_uri = apr_pstrdup(resource->pool, szRealURI);
db_r->m_fake_uri = apr_pstrdup(resource->pool, parenturi);
// 2014-04-24 dadamin
// depth - uri 기준의 인덱스 수행하기 위해서 현재 path의 depth를 구한다.
/* Calculate depth */
db_r->m_depth = ap_count_dirs(db_r->m_fake_uri) - 1;
// NEW 2012-03-20 유희곤
// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
result = dav_shared_is_uri_ignore_case( resource );
if( result == 1 ) // URI 대소문자 무시 서비스인 경우
{
is_uricaseignore = true;
}
else
{
// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
if( result == -1 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_get_parent_resource: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , resource->info->user_id );
}
is_uricaseignore = false;
}
/* exist check */
if( dbms_get_property( db_r, is_uricaseignore ) == 1 )
{
/* ### optimize this into a single allocation! */
/* Create private resource context descriptor */
parent_ctx = apr_pcalloc(resource->pool, sizeof(*parent_ctx));
/* ### this should go away */
parent_ctx->pool = resource->pool;
//dirpath = ap_make_dirstr_parent(ctx->pool, ctx->pathname);
dirpath = ap_make_dirstr_parent(resource->pool, ctx->pathname);
dav_repos_no_trail(dirpath);
parent_ctx->pathname = dirpath;
/* Set parent resource */
parent_resource = apr_pcalloc(resource->pool, sizeof(*parent_resource));
parent_resource->info = parent_ctx;
parent_resource->hooks = &dav_repos_hooks_repos;
parent_resource->pool = resource->pool;
if( resource->uri != NULL )
{
char *uri = ap_make_dirstr_parent(resource->pool, resource->info->db_r->m_fake_uri);
dav_repos_no_trail(uri);
parent_resource->uri = uri;
}
parent_ctx->db_r = db_r;
parent_resource->exists = 1;
/* Is collection ? */
if (db_r->m_resource_type == dav_repos_COLLECTION)
{
parent_resource->collection = APR_DIR;
}
}
else
{
*result_parent = NULL;
//parent_resource->exists = 0;
return solbox_dav_new_error(resource->pool, HTTP_CONFLICT, 0, "dav_repos_get_parent_resource: No parent collection.");
}
*result_parent = parent_resource;
return NULL;
}
// CHG 2012-03-26 huibong
// 서비스별 URI 대소문자 구분, 무시 여부 기능 추가.
// 본 함수는 MOVE, COPY 등의 작업시 상위 모듈인 mod_dav.c 의 dav_method_copymove() 함수에서
// 동일 URI 에 대한 요청인지 판단하기 위해 호출하기 위한 함수임.
// 두개의 URI 가 동일할 경우 상위 모듈에서 HTTP_FORBIDDEN 403 을 반환처리함.
static int dav_repos_is_same_resource(const dav_resource * res1, const dav_resource * res2)
{
// NEW 2012-03-26 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
int result = 0;
if( res1->hooks != res2->hooks )
return 0;
// NEW 2012-03-26 유희곤
// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
result = dav_shared_is_uri_ignore_case( res1 );
if( result == 1 ) // URI 대소문자 무시 서비스인 경우
{
if( strcasecmp(res1->uri, res2->uri) == 0 )
{
// URI 대소문자를 구분하지 않는 서비스에서... 입력받은 2개의 URI 가 동일한 경우
return 1;
}
else
{
// URI 대소문자를 구분하지 않는 서비스에서... 입력받은 2개의 URI 가 동일하지 경우
return 0;
}
}
else // URI 대소문자를 구분하는 서비스인 경우
{
// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
if( result == -1 )
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_is_same_resource: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , res1->info->user_id );
if( strcmp(res1->uri, res2->uri) == 0 )
{
// URI 대소문자를 구분하는 서비스에서... 입력받은 2개의 URI 가 동일한 경우
return 1;
}
else
{
// URI 대소문자를 구분하는 서비스에서... 입력받은 2개의 URI 가 동일하지 경우
return 0;
}
}
}
/* Check
** parent : res1
** child : res2
*/
// CHG 2012-03-26 huibong
// 서비스별 URI 대소문자 구분, 무시 여부 기능 추가.
// 본 함수는 MOVE, COPY 등의 작업시 상위 모듈인 mod_dav.c 의 dav_method_copymove() 함수에서 호출되는 함수로서
// 두개의 URI 가 동일할 경우 상위 모듈에서 HTTP_FORBIDDEN 403 을 반환처리함.
static int dav_repos_is_parent_resource(const dav_resource * res1, const dav_resource * res2)
{
const char *parent_uri;
// NEW 2012-03-26 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
int result = 0;
if (res1->hooks != res2->hooks)
return 0;
if (res1->exists == 0)
return 0;
parent_uri = ap_make_dirstr_parent(res2->pool, res2->uri);
// NEW 2012-03-26 유희곤
// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
result = dav_shared_is_uri_ignore_case( res1 );
if( result == 1 ) // URI 대소문자 무시 서비스인 경우
{
if( strcasecmp( res1->uri, parent_uri ) == 0 )
{
// URI 대소문자를 구분하지 않는 서비스에서... 입력받은 2개의 URI 가 동일한 경우
return 1;
}
else
{
// URI 대소문자를 구분하지 않는 서비스에서... 입력받은 2개의 URI 가 동일하지 경우
return 0;
}
}
else // URI 대소문자를 구분하는 서비스인 경우
{
// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
if( result == -1 )
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_is_parent_resource: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , res1->info->user_id );
if( strcmp( res1->uri, parent_uri ) == 0 )
{
// URI 대소문자를 구분하는 서비스에서... 입력받은 2개의 URI 가 동일한 경우
return 1;
}
else
{
// URI 대소문자를 구분하는 서비스에서... 입력받은 2개의 URI 가 동일하지 경우
return 0;
}
}
}
/** FIXME: */
/* Elenoa: 2006. 09. 18: make sync fd */
// CHG 2012-03-26 huibong
// dav_repos_sync_open() 함수는 미사용 함수이므로.. 2012-05-31 까지 유지 후 문제 없으면 제거 할 것.
//static void dav_repos_sync_open(const dav_stream *stream, apr_off_t offset)
//{
// const dav_resource *resource = stream->resource;
// request_rec *rec = resource->info->rec;
// //apr_pool_t *pool = resource->info->pool;
// dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;
// const char *slength = NULL;
// dav_repos_server_conf *dsc = NULL;
//
// apr_sockaddr_t *sa;
// apr_socket_t *sock;
// apr_status_t rv;
// char ebuf[128], *p;
// sync_msg_header_t header;
// apr_size_t length;
// char *pszSafeStr;
//
// dsc = dav_repos_get_server_conf(rec->server);
//
// /* open socket */
// rv = apr_sockaddr_info_get(&sa, dsc->sync_host, APR_INET, 13121, 0, db_r->p);
// if (rv != APR_SUCCESS) {
// ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: failure to open sync host: %s", apr_strerror(rv, ebuf, sizeof ebuf));
// return;
// }
// rv = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP, db_r->p);
// if (rv != APR_SUCCESS) {
// ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: failure to create sync socket: %s", apr_strerror(rv, ebuf, sizeof ebuf));
// return;
// }
// rv = apr_socket_timeout_set(sock, apr_time_from_sec(3));
// if (rv != APR_SUCCESS) {
// apr_socket_close(sock);
// ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: failure to set sync timeout: %s", apr_strerror(rv, ebuf, sizeof ebuf));
// return;
// }
// apr_socket_connect(sock, sa);
// db_r->sock = sock;
//
// /* send header */
// memset((char *)&header, 0, sizeof(sync_msg_header_t));
// header.ulMode = htonl(0x0001);
// if (!strncmp(dsc->storage_dir,stream->pathname,strlen(dsc->storage_dir))) {
// //ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: filename case #1 %s", stream->pathname);
// pszSafeStr = apr_pcalloc(db_r->p, strlen(stream->pathname));
// p = (char *)(stream->pathname + strlen(dsc->storage_dir));
// if (*p == '/') p++;
// strcpy(pszSafeStr, p);
// //ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: filename case #1 pszSafeStr %s", pszSafeStr);
// p = strrchr(pszSafeStr, '/');
// if (*p) {
// *p = '\0';
// }
// strcpy(header.szFilePath, pszSafeStr);
// } else {
// //ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: filename case #2 %s", stream->pathname);
// pszSafeStr = apr_pcalloc(db_r->p, strlen(stream->pathname) + 1);
// strcpy(pszSafeStr, stream->pathname);
// p = strrchr(pszSafeStr, '/');
// if (*p) {
// *p = '\0';
// }
// strcpy(header.szFilePath, pszSafeStr);
// }
// if ((p = strrchr(stream->pathname, '/'))) p++;
// strcpy(header.szFileName, (p ? p : stream->pathname));
// slength = apr_table_get(rec->headers_in, "Content-Length");
// header.fileSize = slength ? atoll(slength) : 0;
// header.fileOffSet = offset;
//
// /* send it */
// length = sizeof(sync_msg_header_t);
// rv = apr_socket_send(db_r->sock, (char *)&header, &length);
// if (rv != APR_SUCCESS) {
// apr_socket_close(sock);
// ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: failure to send sync header: %s", apr_strerror(rv, ebuf, sizeof ebuf));
// db_r->sock = NULL;
// return;
// }
//}
static dav_error *dav_repos_open_stream(const dav_resource * resource, dav_stream_mode mode, dav_stream ** stream)
{
apr_int32_t flags = 0;
request_rec *subrec = NULL;
request_rec *rec = resource->info->rec;
apr_pool_t *pool = resource->info->pool;
dav_stream *ds = apr_pcalloc(pool, sizeof(*ds));
dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;
//dav_repos_server_conf *dsc = NULL;
//char mode_str[128];
// NEW 2012-03-20 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
int result = 0;
bool is_uricaseignore = false;
// 2014.07.07 dadamin
// 사용되지 않는 로직 주석 처리
//switch( mode )
//{
// case DAV_MODE_WRITE_SEEKABLE:
// sprintf(mode_str, "%s/%x/%c", "DAV_MODE_WRITE_SEEKABLE", mode, '\0');
// break;
// case DAV_MODE_WRITE_TRUNC:
// sprintf(mode_str, "%s/%x/%c", "DAV_MODE_WRITE_TRUNC", mode, '\0');
// break;
// default:
// sprintf(mode_str, "%s/%x/%c", "DAV_MODE_DEFAULT_READ", mode,'\0');
// break;
//}
//dsc = dav_repos_get_server_conf(rec->server);
switch( mode )
{
default:
flags = APR_READ | APR_BINARY;
ds->is_write = 0;
break;
case DAV_MODE_WRITE_SEEKABLE:
flags = APR_WRITE | APR_CREATE | APR_BINARY;
case DAV_MODE_WRITE_TRUNC:
if (flags == 0)
{
flags = APR_WRITE | APR_CREATE | APR_BINARY | APR_TRUNCATE;
db_r->m_get_content_length = 0;
}
/* get length */
//slength = apr_table_get(rec->headers_in, "Content-Length");
//db_r->m_get_content_length = slength ? atoll(slength) : 0;
/* check remain */
/*
// CHG 2012-02-29 trozan (#8347)
// 계약 용량을 초과할 경우 HTTP 404 (HTTP_FORBIDDEN) 값을 반환 처리하는 부분을
// HTTP 507 (HTTP_INSUFFICIENT_STORAGE) 반환 처리로 수정
//////////////////////////////////////////////////////////////////////////
dav_shared_vu_check() 함수에서 처리하는 HTTP_INSUFFICIENT_STORAGE (507) 관련 부분은
실제 507을 보냈지만, 상위 모듈에서 403으로 다시 에러를 설정하여 보내서 access_log 에 507이 남지 않는다.
그래서 dav_shared_vu_check() 함수를 dav_repos_write_stream() 에서 처리하도록 수정함.
//////////////////////////////////////////////////////////////////////////
if (dav_shared_vu_check(atol(rec->user), db_r->m_get_content_length) != OK)
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL
, "OPENDAV: Debug: dav_shared_vu_check() return error... Insufficient Storage of logically. (tranid '%d')", atoi(rec->user));
return solbox_dav_new_error(pool, HTTP_INSUFFICIENT_STORAGE, 0
, "There is logically not enough storage to write to this resource.");
}
*/
ds->is_write = 1;
#ifndef __OPENDISK_SOLUTION_NOT_USE_TS__
dav_shared_ts_open_stream(rec, INB);
#endif // !__OPENDISK_SOLUTION_NOT_USE_TS__
/* make time */
db_r->m_get_lastmodified = apr_time_now();
if( db_r->m_creation_date == 0 )
{
db_r->m_creation_date = db_r->m_get_lastmodified;
}
/* First , try to get the content-type from client */
if( rec->content_type )
{
db_r->m_get_content_type = apr_pstrdup(pool, rec->content_type);
}
else
{
/* perform a "GET" on the resource's URI (note that the resource may not correspond to the current request!). */
subrec = ap_sub_req_lookup_uri(rec->uri, rec, NULL);
if( subrec && subrec->content_type )
{
db_r->m_get_content_type = apr_pstrdup(pool, subrec->content_type);
}
}
/* No content type */
if( db_r->m_get_content_type == NULL )
{
db_r->m_get_content_type = apr_pstrdup(pool, "application/octet-stream");
}
/* It must be upload (PUT Method) */
/* Let's save db info here */
// NEW 2012-03-23 유희곤
// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
result = dav_shared_is_uri_ignore_case( resource );
if( result == 1 ) // URI 대소문자 무시 서비스인 경우
{
is_uricaseignore = true;
}
else
{
// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
if( result == -1 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_open_stream: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , resource->info->user_id );
}
is_uricaseignore = false;
}
// 2014.06.12 dadamin
// display name 사용하는 형상 지원
if(dav_use_display_name() == 1)
db_r->m_displayname = db_r->m_fake_uri;
if( (db_r->m_resource_id = dbms_set_property(db_r, resource, is_uricaseignore, false, flags, 0)) == -1)
{
/* Error handling */
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_open_stream: dbms_set_property func fail [%s]", db_r->m_fake_uri );
return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "dav_repos_open_stream: meta info set failed.");
}
// NEW 2010-03-24 webting
// dbms.h의 dbms_remove_replicate 함수 정의에서 내용 확인 요망.
if( mode == DAV_MODE_WRITE_SEEKABLE )
dbms_remove_replicate( db_r, is_uricaseignore);
break;
}
ds->p = pool;
ds->pathname = resource->info->pathname;
ds->resource = resource;
// 2014.07.07 dadamin
// setting write flags : 쓰기 완료 시 해당 flag 검사하여 trunc 된 경우 사이즈 오류 방지를 위해서 사용
ds->test_flag = flags;
/* set umask for new file permission, 644 */
if( apr_file_open(&ds->f, ds->pathname, flags, APR_UWRITE|APR_UREAD|APR_GREAD|APR_WREAD, pool) != APR_SUCCESS )
{
#ifndef __OPENDISK_SOLUTION_NOT_USE_TS__
//if (mode == DAV_MODE_WRITE_SEEKABLE || mode == DAV_MODE_WRITE_TRUNC) {
if( ds->is_write )
{
dav_shared_ts_close_stream(rec, INB);
}
#endif // !__OPENDISK_SOLUTION_NOT_USE_TS__
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_open_stream: apr_file_open func fail [%s][%s]", ds->pathname, db_r->m_fake_uri );
return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "dav_repos_open_stream: error occurred while opening resource.");
}
if( ds->is_write )
{
//if (mode == DAV_MODE_WRITE_SEEKABLE || mode == DAV_MODE_WRITE_TRUNC) {
#ifdef __UNUSE_FILELOCK__
; // nothing ~~~
#else
if( apr_file_lock(ds->f, APR_FLOCK_NONBLOCK) != APR_SUCCESS )
{
#ifndef __OPENDISK_SOLUTION_NOT_USE_TS__
dav_shared_ts_close_stream(rec, INB);
#endif // !__OPENDISK_SOLUTION_NOT_USE_TS__
apr_file_close(ds->f);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_open_stream: apr_file_lock func fail [%s][%s]", ds->pathname, db_r->m_fake_uri );
return solbox_dav_new_error(pool, HTTP_METHOD_NOT_ALLOWED, 0, "Write permission occupied by others.");
}
#endif
// CHG 2012-03-21 huibong
// sibling 및 sync_host 기능은 2011 년 BD-06 사내망 제거 이후 더 이상 사용하지 않는 기능임.
// 따라서 아래 관련 코드를 주석 처리함.
///* Elenoa: 2006. 09. 18: make sync fd */
//if( mode == DAV_MODE_WRITE_TRUNC && dsc->sync_host )
//{
// dav_repos_sync_open(ds, 0);
//
// if( db_r->sock == NULL )
// return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "dav_repos_open_stream: error occurred while opening a sync resource");
//}
}
*stream = ds;
return NULL;
}
static dav_error *dav_repos_close_stream(dav_stream * stream, int commit)
{
const dav_resource *resource = stream->resource;
dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;
struct stat fs;
int stat_ret = 0;
// NEW 2012-03-23 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
int result = 0;
bool is_uricaseignore = false;
// 2013.09.27 dadamin
// 의미 없는 시스템콜 삭제 처리
/* get file size */
// 2014.01.09 dadamin
// pool 획득에 따른 잘못된 파일 사이즈 업데이트 막기 위해서 pool 획득 이후 해당 크기를 구함
//memset(&fs , 0x00 , sizeof(struct stat));
//stat_ret = lstat ( stream->pathname , &fs ) ;
if( stream->is_write )
{
#ifndef __OPENDISK_SOLUTION_NOT_USE_TS__
dav_shared_ts_close_stream(resource->info->rec, INB);
#endif // !__OPENDISK_SOLUTION_NOT_USE_TS__
// 2013.09.27 dadamin
// sibling 및 sync_host 기능 제거
//if( db_r->sock )
//{
// apr_socket_close(db_r->sock);
//}
resource->info->rec->read_length = stream->data_read;
//db_r->m_get_content_length = fs.st_size;
if( stat_ret < 0 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_close_stream: write lstat func fail[%d]. uri[%s][%" APR_INT64_T_FMT "] name[%s][%" APR_OFF_T_FMT "] err[%d][%s]"
, stat_ret
, db_r->m_fake_uri, db_r->m_get_content_length
, stream->pathname, fs.st_size
, errno, strerror(errno)
);
if( stream->is_write )
{
#ifdef __UNUSE_FILELOCK__
; // nothing ~~~
#else
apr_file_unlock(stream->f);
#endif
}
apr_file_close(stream->f);
// CHG 2012-03-21 huibong
// Write 실패가 발생하여 기존 RCDB 에 저장된 정보를 삭제 처리해야 하는 경우이므로...
// 이런 경우에는 해당 파일에 대한 대소문자를 구분하여 처리토록 한다.
if( dbms_remove_resource(db_r, false) != 0 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_close_stream: dbms_remove_resource func fail [%s]", db_r->m_fake_uri );
return solbox_dav_new_error(db_r->p, HTTP_INTERNAL_SERVER_ERROR, 0, "An error occurred while deleteing DBMS.");
}
if( apr_file_remove(stream->pathname, stream->p) != APR_SUCCESS )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_close_stream: apr_file_remove func fail [%s][%s]", db_r->m_fake_uri, stream->pathname );
return solbox_dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, "dav_repos_close_stream:removing incompleted file failed");
}
/* Error handling */
return solbox_dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, "Written file length and data length are diffrent");
}
}
// NEW 2012-03-23 유희곤
// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
result = dav_shared_is_uri_ignore_case( resource );
if( result == 1 ) // URI 대소문자 무시 서비스인 경우
{
is_uricaseignore = true;
}
else
{
// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
if( result == -1 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_close_stream: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , resource->info->user_id );
}
is_uricaseignore = false;
}
/* FIXME: Elenoa, Right? */
/* Let's save db info here */
if( (db_r->m_resource_id = dbms_set_property(db_r, resource, is_uricaseignore, true, stream->test_flag, (stream->start_pos + stream->data_read))) == -1 )
{
/* Error handling */
if( stream->is_write )
{
#ifdef __UNUSE_FILELOCK__
; // nothing ~~~
#else
apr_file_unlock(stream->f);
#endif
}
apr_file_close(stream->f);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_close_stream: dbms_set_property func fail [%s]", db_r->m_fake_uri );
return solbox_dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, "dav_repos_close_stream::");
}
if( stream->is_write )
{
#ifdef __UNUSE_FILELOCK__
; // nothing ~~~
#else
apr_file_unlock( stream->f );
#endif
}
apr_file_close( stream->f );
#ifdef __USE_AUTH_SOFTLINE_ONLY__
/// 소프트 라인 - 파일 size 동기화를 휘한 처리 기능
dav_auth_upload_end( resource->info );
#endif // __USE_AUTH_SOFTLINE_ONLY__
return NULL;
}
static dav_error *dav_repos_write_stream(dav_stream * stream, const void *buf, apr_size_t bufsize)
{
apr_status_t status;
const dav_resource *resource = stream->resource;
if( resource == NULL )
{
return solbox_dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0 , "An error occurred while writing to a resource. (resource empty)");
}
// 2015.05.14 dadamin
// version 3.5 부터 서비스 사용량 체크 하지 않기함
/* Keep counting the size */
stream->data_read += bufsize;
// 2015.07.15 dadamin
// version 3.5 부터 input 필터에서 해당 통계 수집 처리
//dav_transfer_progess_generate(rec, INB, bufsize, 0);
status = apr_file_write_full(stream->f, buf, bufsize, NULL);
if( APR_STATUS_IS_ENOSPC( status ) )
{
// CHG 2012-02-29 trozan (#8347)
// 실 용량을 초과할 경우로 리턴 메시지 멘트에 physically 추가
ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_write_stream: apr_file_write_full func fail. physically storage not enough." );
return solbox_dav_new_error(stream->p, HTTP_INSUFFICIENT_STORAGE, 0 , "There is physically not enough storage to write to this resource.");
}
else if( status != APR_SUCCESS )
{
/* ### use something besides 500? */
ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_write_stream: apr_file_write_full func fail[%d]", status );
return solbox_dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, "An error occurred while writing to a resource.");
}
return NULL;
}
static dav_error *dav_repos_seek_stream(dav_stream * stream, apr_off_t abs_pos)
{
//dav_repos_server_conf *dsc = NULL;
//dsc = dav_repos_get_server_conf(stream->resource->info->rec->server);
struct stat fs;
int stat_ret = 0;
int file_exist = 1;
memset(&fs , 0x00 , sizeof(struct stat));
stat_ret = lstat ( stream->pathname , &fs ) ;
if( stat_ret < 0 )
file_exist = 0;
// seek the offset over ranges ~~~
if( apr_file_seek(stream->f, APR_SET, &abs_pos) != APR_SUCCESS || ( 0 == file_exist) )
{
/* ### should check whether apr_file_seek set abs_pos was set to the
* correct position? */
/* ### use something besides 500? */
ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_seek_stream: apr_file_seek func fail [%s]", stream->pathname );
return solbox_dav_new_error( stream->p, HTTP_INTERNAL_SERVER_ERROR, 0, "Could not seek to specified position in the resource." );
}
stream->start_pos = abs_pos;
// CHG 2012-03-21 huibong
// sibling 및 sync_host 기능은 2011 년 BD-06 사내망 제거 이후 더 이상 사용하지 않는 기능임.
// 따라서 아래 관련 코드를 주석 처리함.
///* Elenoa: 2006. 09. 18: make sync fd */
//if (stream->is_write && dsc->sync_host)
//{
// const dav_resource *resource = stream->resource;
// dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;
//
// if (db_r->sock)
// {
// apr_socket_close(db_r->sock);
// db_r->sock = NULL;
// }
//
// dav_repos_sync_open(stream, abs_pos);
//
// if (db_r->sock == NULL)
// return solbox_dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
// "dav_repos_seek_stream() : An error occurred while opening a resource.");
//}
return NULL;
}
static dav_error *dav_repos_set_headers(request_rec * r, const dav_resource * resource)
{
dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;
if( !resource->exists )
return NULL;
/* make sure the proper mtime is in the request record */
ap_update_mtime(r, db_r->m_get_lastmodified);
/* ### note that these use r->filename rather than <resource> */
ap_set_last_modified(r);
/* generate our etag and place it into the output */
apr_table_setn(r->headers_out, "ETag", dav_repos_getetag(resource));
/* we accept byte-ranges */
apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
/* set up the Content-Length header */
ap_set_content_length(r, (apr_off_t)db_r->m_get_content_length);
r->content_type = db_r->m_get_content_type;
apr_table_setn(r->headers_out, "Content-Type", db_r->m_get_content_type);
return NULL;
}
static dav_error *dav_repos_deliver( const dav_resource * resource, ap_filter_t * output )
{
apr_pool_t *pool = resource->pool;
apr_bucket_brigade *bb = NULL;
apr_file_t *fd = NULL;
apr_status_t status;
apr_bucket *bkt = NULL;
dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;
core_dir_config *conf =
(core_dir_config *)ap_get_module_config(db_r->r->per_dir_config, &core_module);
apr_int32_t flag = APR_READ | APR_BINARY;
#ifdef __OPENDAV_DEBUG_USER_AUTH__
// CHG 2011-12-01 huibong
// apr_table_get() 함수 반환 타입이 const char * 이므로 이에 맞도록 형 타입 수정처리.
const char * range;
#endif
/* Check resource type */
if( resource->type != DAV_RESOURCE_TYPE_REGULAR
&& resource->type != DAV_RESOURCE_TYPE_VERSION
&& resource->type != DAV_RESOURCE_TYPE_WORKING)
{
return solbox_dav_new_error(pool, HTTP_CONFLICT, 0, "Cannot GET this type of resource.");
}
if( resource->collection )
{
return solbox_dav_new_error(pool, HTTP_CONFLICT, 0, "There is no default response to GET for a collection.");
}
// 2012.07.06 dadamin
// sendfile 지원
if( conf->enable_sendfile == ENABLE_SENDFILE_ON )
flag |= APR_SENDFILE_ENABLED;
if( (status = apr_file_open(&fd, db_r->m_filename_hash, flag, 0, pool)) != APR_SUCCESS )
{
return solbox_dav_new_error(pool, HTTP_FORBIDDEN, 0, "File permissions deny server access.");
}
// 2015.07.15
// output 트래픽 제어/통계 필터 활성화
ap_add_output_filter("dav_bc_output", NULL, db_r->r, db_r->r->connection);
dav_transfer_progess_generate(db_r->r, OUTB, 0, 0);
bb = apr_brigade_create(pool, output->c->bucket_alloc);
#ifdef __OPENDAV_DEBUG_USER_AUTH__
range = apr_table_get(db_r->r->headers_in, "Range");
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: DELIVER C %d RANGE %s:%s:%" APR_OFF_T_FMT "", getpid(), range, db_r->r->range, resource->info->finfo.size);
#endif
if( (status = dav_auth_user_start(resource->info)) != OK )
{
if( status == HTTP_UNAUTHORIZED )
{
return solbox_dav_new_error(pool, HTTP_PRECONDITION_FAILED, 0, "User authorization from Service Provider FAILURE.");
}
}
apr_brigade_insert_file(bb, fd, 0, resource->info->finfo.size, pool);
bkt = apr_bucket_eos_create(output->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, bkt);
#ifdef __OPENDAV_DEBUG_USER_AUTH__
{
apr_off_t length;
apr_brigade_length(bb, 1, &length);
range = apr_table_get(db_r->r->headers_in, "Range");
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: DELIVER B %d RANGE %s:%s:%" APR_OFF_T_FMT " BRIGADE %" APR_OFF_T_FMT "", getpid(), range, db_r->r->range, resource->info->finfo.size, length);
}
#endif
if( (status = ap_pass_brigade(output, bb)) != APR_SUCCESS )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_deliver: ap_pass_brigade() return error. filter status [%d]", output->r->status );
// 2012.07.03 dadamin
// 로직에서 에러 리턴 후 같은 세션으로 메소드를 호출 시
// client 로 응답 메세지를 보내지 않는 현상이 확인하여(Could not parse response status line)
// 해당 로직 에러시 로깅만하며, 에러를 리턴하지 않게 수정함
return NULL;
}
#ifdef __OPENDAV_DEBUG_USER_AUTH__
{
apr_off_t length = 0;
apr_file_seek(fd, APR_CUR, &length);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: DELIVER A %d RANGE %s BRIGADE %" APR_OFF_T_FMT "", getpid(), db_r->r->range, db_r->r->clength);
}
#endif
dav_auth_user_end(resource->info);
return NULL;
}
static dav_error *dav_repos_create_collection(dav_resource * resource)
{
dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;
// NEW 2012-03-23 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
int result = 0;
bool is_uricaseignore = false;
// NEW 2012-03-23 유희곤
// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
result = dav_shared_is_uri_ignore_case( resource );
if( result == 1 ) // URI 대소문자 무시 서비스인 경우
{
is_uricaseignore = true;
}
else
{
// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
if( result == -1 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_create_collection: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , resource->info->user_id );
}
is_uricaseignore = false;
}
/* let's fill db_r for collection */
db_r->m_get_content_type = apr_pstrdup(db_r->p, DIR_MAGIC_TYPE);
db_r->m_resource_type = dav_repos_COLLECTION;
/* make time */
db_r->m_creation_date = db_r->m_get_lastmodified = apr_time_now();
// 2014.06.12 dadamin
// display name 사용하는 형상 지원
if(dav_use_display_name() == 1)
db_r->m_displayname = db_r->m_fake_uri;
if( (db_r->m_resource_id = dbms_set_property(db_r, resource, is_uricaseignore, false, 0, 0)) == -1 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_create_collection: dbms_set_property func fail [%s]", db_r->m_fake_uri );
return solbox_dav_new_error(db_r->p, HTTP_INTERNAL_SERVER_ERROR, 0, "error occurred while writing to DBMS.");
}
return NULL;
}
static dav_error *dav_repos_copy_resource(const dav_resource * src, dav_resource * dst, int depth, dav_response ** response)
{
dav_repos_resource *db_r_src = (dav_repos_resource *) src->info->db_r;
dav_repos_resource *db_r_dst = (dav_repos_resource *) dst->info->db_r;
// NEW 2012-03-23 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
int result = 0;
bool is_uricaseignore = false;
// NEW 2012-03-26 huibong
// MOVE/COPY 요청시 http header 상에 Overwrite: tag 에 대한 확인을 위한 목적으로 추가한다.
// 이와 관련된 기능은 상위 mod_dav.c 상에서 처리하므로.. 본 함수에서 처리하지 않아도 되지만...
// Overwrite 모드가 아닌 경우에 대한 확인을 위해 로깅 목적으로 추가한다.
// 아래 내역은 참고를 위한 내역이며.. 대분분 상위 모듈에서 처리된다.
// Overwrite: 가 없는 경우 Default T 로 처리된다. ( WebDAV 규약 )
// Overwirte: T => Default. 대상에 대한 덮어쓰기 처리.. 이미 상위 모듈에서 동일 URI 인지 검사한 후 대상 URI 제거를 수행함.
// Overwirte: F => 상위 모듈에서 대상 URI가 존재할 경우 HTTP 412 Precondition Failed 를 반환한다.
// Overwrite: 기타 => HTTP 400 Bad Request 를 반환 처리한다.
const char * p_overwrite = apr_table_get( src->info->rec->headers_in, "Overwrite" );
if( p_overwrite == NULL )
{
// HTTP Header 상에 Overwrite: tag 가 없는 경우.. WebDAV 규약 대로 default T 로 처리한다.
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_copy_resource: overwrite tag not found [%s][%s]", db_r_src->m_fake_uri, db_r_dst->m_fake_uri );
}
else if( strncasecmp( p_overwrite, "F", 1)==0 ) // Overwrite 모드가 아닌 경우
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_copy_resource: overwrite false mode [%s][%s][%s]", p_overwrite, db_r_src->m_fake_uri, db_r_dst->m_fake_uri );
}
// NEW 2012-03-23 유희곤
// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
result = dav_shared_is_uri_ignore_case( src );
if( result == 1 ) // URI 대소문자 무시 서비스인 경우
{
is_uricaseignore = true;
}
else
{
// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
if( result == -1 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_copy_resource: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , src->info->user_id );
}
is_uricaseignore = false;
}
// CHG 2012-03-27
// 상위 모듈에서 원본 및 대상 URI 에 대해 동일한지 dav_repos_is_same_resource() 함수를 호출하여 검사하므로...
// 본 모듈에서 URI 에 대한 동일 여부를 재확인할 필요 없음.
/* copy resources */
if( dbms_copy_resource(db_r_src, db_r_dst, is_uricaseignore ) != 0 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_copy_resource: dbms_copy_resource func fail. src[%s] target[%s]", db_r_src->m_fake_uri, db_r_dst->m_fake_uri );
return solbox_dav_new_error(db_r_src->p, HTTP_INTERNAL_SERVER_ERROR, 0, "An error occurred while copying DBMS.");
}
return NULL;
}
static dav_error *dav_repos_move_resource(dav_resource * src, dav_resource * dst, dav_response ** response)
{
dav_repos_resource *db_r_src = (dav_repos_resource *) src->info->db_r;
dav_repos_resource *db_r_dst = (dav_repos_resource *) dst->info->db_r;
// NEW 2012-03-21 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
int result = 0;
bool is_uricaseignore = false;
// NEW 2012-03-26 huibong
// MOVE/COPY 요청시 http header 상에 Overwrite: tag 에 대한 확인을 위한 목적으로 추가한다.
// 이와 관련된 기능은 상위 mod_dav.c 상에서 처리하므로.. 본 함수에서 처리하지 않아도 되지만...
// Overwrite 모드가 아닌 경우에 대한 확인을 위해 로깅 목적으로 추가한다.
// 아래 내역은 참고를 위한 내역이며.. 대분분 상위 모듈에서 처리된다.
// Overwrite: 가 없는 경우 Default T 로 처리된다. ( WebDAV 규약 )
// Overwirte: T => Default. 대상에 대한 덮어쓰기 처리.. 이미 상위 모듈에서 동일 URI 인지 검사한 후 대상 URI 제거를 수행함.
// Overwirte: F => 상위 모듈에서 대상 URI가 존재할 경우 HTTP 412 Precondition Failed 를 반환한다.
// Overwrite: 기타 => HTTP 400 Bad Request 를 반환 처리한다.
const char * p_overwrite = apr_table_get( src->info->rec->headers_in, "Overwrite" );
if( p_overwrite == NULL )
{
// HTTP Header 상에 Overwrite: tag 가 없는 경우.. WebDAV 규약 대로 default T 로 처리한다.
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_move_resource: overwrite tag not found [%s][%s]", db_r_src->m_fake_uri, db_r_dst->m_fake_uri );
}
else if( strncasecmp( p_overwrite, "F", 1)==0 ) // Overwrite 모드가 아닌 경우
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_move_resource: overwrite false mode [%s][%s][%s]", p_overwrite, db_r_src->m_fake_uri, db_r_dst->m_fake_uri );
}
// NEW 2012-03-21 유희곤
// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
result = dav_shared_is_uri_ignore_case( src );
if( result == 1 ) // URI 대소문자 무시 서비스인 경우
{
is_uricaseignore = true;
}
else
{
// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
if( result == -1 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_move_resource: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , src->info->user_id );
}
is_uricaseignore = false;
}
// CHG 2012-03-27
// 상위 모듈에서 원본 및 대상 URI 에 대해 동일한지 dav_repos_is_same_resource() 함수를 호출하여 검사하므로...
// 본 모듈에서 URI 에 대한 동일 여부를 재확인할 필요 없음.
/* move resources */
if( dbms_move_resource(db_r_src, db_r_dst, src, is_uricaseignore ) != 0 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_move_resource: dbms_move_resource func fail. src[%s] target[%s]", db_r_src->m_fake_uri, db_r_dst->m_fake_uri );
return solbox_dav_new_error(db_r_src->p, HTTP_INTERNAL_SERVER_ERROR, 0, "An error occurred while moving DBMS.");
}
return NULL;
}
static dav_error *dav_repos_remove_resource(dav_resource * resource, dav_response ** response)
{
dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r;
// NEW 2012-03-21 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
int result = 0;
bool is_uricaseignore = false;
// NEW 2012-03-21 유희곤
// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
result = dav_shared_is_uri_ignore_case( resource );
if( result == 1 ) // URI 대소문자 무시 서비스인 경우
{
is_uricaseignore = true;
}
else
{
// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
if( result == -1 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_remove_resource: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , resource->info->user_id );
}
is_uricaseignore = false;
}
/* Delete the resource */
if( dbms_remove_resource(db_r, is_uricaseignore ) != 0 )
{
(*response) = NULL;
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_remove_resource: dbms_remove_resource func fail [%s]", db_r->m_fake_uri );
return solbox_dav_new_error(db_r->p, HTTP_INTERNAL_SERVER_ERROR, 0, "An error occurred while deleteing DBMS.");
}
return NULL;
}
static dav_error *dav_repos_walk(const dav_walk_params * params, int depth, dav_response ** response)
{
dav_error *err = NULL;
apr_pool_t *pool = params->pool;
dav_repos_resource *tmp_r = NULL;
dav_repos_resource *db_r = (dav_repos_resource *) params->root->info->db_r;
dav_walker_ctx *ctx = params->walk_ctx;
char *tmpStr = NULL;
// NEW 2012-03-23 각 서비스별 URI 대소문자 구분 여부 정보를 저장하기 위한 변수. true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
int result = 0;
bool is_uricaseignore = false;
// NEW 2010-03-29 huibong
// PROPFIND 에 따른 조회 처리시 조회 시작 시간 정보를 저장하기 위한 변수 및 파싱 기능 추가.
// 본 기능은 연합뉴스 요구조건을 처리하기 위해 추가함.
const char * startTime = NULL;
startTime = apr_table_get(db_r->r->headers_in, "StartTime");
if( startTime != NULL )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_respos_walk uri[%s] depth[%d] StartTime[%s]" , db_r->m_fake_uri, depth, startTime );
}
/* Let's start with NULL response */
*response = NULL;
// NEW 2012-03-23 유희곤
// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단한다.
result = dav_shared_is_uri_ignore_case( params->root );
if( result == 1 ) // URI 대소문자 무시 서비스인 경우
{
is_uricaseignore = true;
}
else
{
// 만약 dav_shared_is_uri_ignore_case 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
if( result == -1 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_repos_walk: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]" , params->root->info->user_id );
}
is_uricaseignore = false;
}
/*
** search using postgresql
** if is not collection or depth=0, we have enough information
** We support only depth 0 and 1
*/
// CHG 2010-03-29 huibong
// dbms_get_collection_resource 함수상의 startTime 인자 정보 추가
if( db_r->m_resource_type == dav_repos_COLLECTION && depth != 0 )
{
/* Will be filled children */
if( dbms_get_collection_resource( db_r, depth, startTime, is_uricaseignore ) < 0 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_respos_walk: dbms_get_collection_resource func fail [%s]", db_r->m_fake_uri );
return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "error occurred while reading DBMS.");
}
}
// 2013.08.27 dadamin
// SolDirve PROPFIND 시 네임스페이스 사용하기 때문에
// 해당 파일(디렉토리 경우 소속된 모든 정보)에 대해서
// RCDB 네임스페이스 관련 조회 발생으로 응답 성능 저하가
// 발생함으로 opendav 지정된(DAV:)와 기본 속성이 외에 값에
// 대해서 오퍼레이션되지 않게함
#ifdef __USE_NAMESPACE__
// 2013.08.12 dadamin
// PROPPATCH 정상적인 기능을 위해서 필요함(allporp, proname) 에 대해서 추가 속성 조회
// Revision 0150 삭제되 로직임
if (ctx->propfind_type == DAV_PROPFIND_IS_ALLPROP
|| ctx->propfind_type == DAV_PROPFIND_IS_PROPNAME )
{
dbms_fill_dead_property(db_r);
/* Build name space id hash
** It will build db_r->ns_id_hash
*/
dbms_build_ns_id_hash(db_r);
}
#endif // __USE_NAMESPACE__
//2013.08.22 dadamin
// hidden 속성 경우 header에 X-SB-Show-hidden 값이 yes 경우만
// source = 1인 항목을 리턴함
bool is_showhidden = false;
const char * showdidden = apr_table_get(db_r->r->headers_in, "X-SB-Show-hidden");
if( showdidden != NULL && (strncmp(showdidden, "yes", 3)==0) )
{
is_showhidden = true;
}
/*
** Lets walk through the results,
** assemble walk resource, and call walker
*/
for( tmp_r = db_r; tmp_r; tmp_r = tmp_r->next )
{
if( is_showhidden == false && tmp_r->m_source )
{
DBG1("Skip Hidden file [%s]", tmp_r->m_uri);
continue;;
}
/* assemble walk resource */
dav_walk_resource *wres = apr_pcalloc(pool, sizeof(*wres));
dav_resource *resource = apr_pcalloc(pool, sizeof(*resource));
dav_resource_private *info = apr_pcalloc(pool, sizeof(*info));
/* Make info */
info->pool = pool;
info->pathname = tmp_r->m_fake_uri;
info->db_r = tmp_r;
/* Make resource */
resource->exists = params->root->exists; /* nulllock */
resource->collection = tmp_r->m_resource_type;
// 2014.06.12 dadamin
// dispaly name 를 리턴 uri 사용하는 형상 지원
char * display = tmp_r->m_uri;
if( dav_use_display_name() == 1 )
display = (char * ) tmp_r->m_displayname;
//if( db_r->m_fake_uri == tmp_r->m_uri )
// 첫번째 저장된 값은 safe 처리된 문자(') 포함되어 있으므로 원복처리한다.
if(tmp_r == db_r)
{
// 단, display 컬럼 사용하는 경우 해당 값을 사용하기 때문에 원복처리할 필요 없다.
if(display == tmp_r->m_uri)
{
// dav_repos_restore_unsafe_chars
tmpStr = apr_pcalloc(pool, strlen(display) + 5);
dav_repos_restore_unsafe_chars(display, tmpStr);
}
else
{
// 2014.11.27 dadamin
// PROPFIND 응답 시 경로 /dav 로 리턴하도록 수정함
char szRealURI[MaxSizeOfPathLength] = {0};
remove_user_root_dir_from_fake_uri(display, szRealURI);
if(strlen(szRealURI) == 0)
{
// case /dav
tmpStr = tmp_r->m_uri;
}
else
{
// case /dav/????
tmpStr = apr_pcalloc(pool, strlen(tmp_r->m_uri) + 5);
dav_repos_restore_unsafe_chars(tmp_r->m_uri, tmpStr);
}
}
}
else
{
tmpStr = display;
}
if (strcmp(AP_SERVER_BASEVERSION, "Apache/2.2.27") <= 0
&& strcmp(AP_SERVER_BASEVERSION, "Apache/2.2.26") >= 0)
{
// 2014.05.10 dadamin
// apache 2.2.26 이상부터 해당값 대해서 상위 로직(dav_xml_escape_uri)에서
// ap_escape_uri() 함수 호출되지 않아서 한글포함된 경우 제대로된 결과값을
// 얻을 수 없기 때문에 본 로직에서 버전 체크하여 ap_escape_uri() 함수를
// 호출한 값을 전달 될 수 있도록 함
resource->uri = ap_escape_uri(pool, tmpStr);
}
else
{
// 2015.07.07 dadamim
// 최근 릴리즈된 버전(2.4.12, 2.2.29)에 해당 부분이 패치됨
// Changes with Apache 2.2.28 : mod_dav: Fix improper encoding in PROPFIND responses. PR 56480.
resource->uri = tmpStr;
}
resource->info = info;
resource->hooks = params->root->hooks;
resource->pool = pool;
/* Make walk resource */
wres->pool = pool;
wres->resource = resource;
wres->response = *response;
wres->walk_ctx = params->walk_ctx;
/* need to set pool */
tmp_r->p = pool;
tmp_r->r = db_r->r;
/* Need to set ns_id */
tmp_r->ns_id_hash = db_r->ns_id_hash;
tmp_r->ns_hash = db_r->ns_hash;
/*
* Build dead/live props hash
* It should be run even it's nulllock
*/
dav_repos_build_pr_hash(tmp_r);
dav_repos_build_lpr_hash(tmp_r);
/* Fill lock discovery for proppatch prop */
if( ctx->propfind_type == DAV_PROPFIND_IS_PROPNAME || ctx->propfind_type == DAV_PROPFIND_IS_PROP )
dav_repos_insert_lock_prop(params, tmp_r);
/* Call walker */
if( (err = (*params->func) (wres, tmp_r->m_resource_type == dav_repos_COLLECTION ? DAV_CALLTYPE_COLLECTION : DAV_CALLTYPE_MEMBER)) != NULL)
{
/* ### maybe add a higher-level description? */
return err;
}
/* Save response for now */
*response = wres->response;
}
/* Walk locknull files */
if( params->walk_type & DAV_WALKTYPE_LOCKNULL && depth )
{
apr_text_header phdr = { 0 };
apr_text *t;
/* Let's read all locknull lists */
err = dbms_load_locknull_list(db_r->r, db_r->m_fake_uri, &phdr, pool);
if( err != NULL )
return err;
/* For all lists */
for( t = phdr.first; t; t = t->next )
{
dav_lock *locks = NULL;
/* assemble walk resource */
dav_walk_resource *wres = apr_pcalloc(pool, sizeof(*wres));
dav_resource *resource = apr_pcalloc(pool, sizeof(*resource));
dav_resource_private *info = apr_pcalloc(pool, sizeof(*info));
/* Make info */
info->pool = pool;
info->pathname = t->text;
/* Let's make null dbr */
info->db_r = NULL;
/* Make resource */
resource->exists = 0;
resource->uri = t->text;
resource->info = info;
resource->hooks = params->root->hooks;
resource->pool = pool;
/* Make walk resource */
wres->pool = pool;
wres->resource = resource;
wres->response = *response;
wres->walk_ctx = params->walk_ctx;
if( (err = dav_lock_query(params->lockdb, resource, &locks)) != NULL )
{
/* ### maybe add a higher-level description? */
return err;
}
/* call the function for the specified dir + file */
if (locks != NULL && (err = (*params->func) (wres, DAV_CALLTYPE_LOCKNULL)) != NULL)
{
/* ### maybe add a higher-level description? */
return err;
}
/* Save response for now */
*response = wres->response;
}
}
return NULL;
}
/*
** make e tags using index and date
*/
const char *dav_repos_getetag_dbr(const dav_repos_resource * db_r)
{
// CHG 2011-05-16 huibong
// pgpool 사용으로 인해 RCDB 01, 02 간에 resource_id 가 cross 되는 현상이 확인됨.
// 이에 따라 etag 생성 방식을 기존 resource_id - length - creation_date 에서
// filename_hash (32 자리) + length + get_lastmodified 형식으로 변경 처리함.
// BUG 2011-11-29 huibong
// 서비스 개통시 최초 생성되는 depth = 0 인 최상위 폴더에 대해 filename_hash 정보가 비어 있음.
// 따라서 filename_hash 정보를 추출하기 위해 -32 연산을 수행할 경우 temp 포인터가 예상치 못한 구간을 나타낼 수 있는 버그 발생
// 이를 수정하기 위해 filename_hash 의 길이가 32 이하인 경우 filename_hash 정보를 00000000000000000000000000000000 으로 처리.
if( strlen(db_r->m_filename_hash) <= 32 )
{
return apr_psprintf( db_r->p, "\"00000000000000000000000000000000%x%x\"",
(unsigned int)db_r->m_get_content_length,
(unsigned int)db_r->m_get_lastmodified );
}
else
{
const char * temp = (db_r->m_filename_hash) + ( strlen(db_r->m_filename_hash) - 32) ;
return apr_psprintf( db_r->p, "\"%s%x%x\"",
temp,
(unsigned int)db_r->m_get_content_length,
(unsigned int)db_r->m_get_lastmodified );
}
}
const char *dav_repos_getetag(const dav_resource * resource)
{
if (!resource->exists || resource->info == NULL || resource->info->db_r == NULL)
return apr_pstrdup(resource->info->pool, "");
return dav_repos_getetag_dbr(resource->info->db_r);
}
const dav_hooks_repository dav_repos_hooks_repos = {
1, /* special GET handling *//* 1 for GET handling, 0 for generic */
dav_repos_get_resource,
dav_repos_get_parent_resource,
dav_repos_is_same_resource,
dav_repos_is_parent_resource,
dav_repos_open_stream,
dav_repos_close_stream,
dav_repos_write_stream,
dav_repos_seek_stream,
dav_repos_set_headers,
dav_repos_deliver,
dav_repos_create_collection,
dav_repos_copy_resource,
dav_repos_move_resource,
dav_repos_remove_resource,
dav_repos_walk,
dav_repos_getetag,
};
/* Insert prop with propid */
static dav_prop_insert dav_repos_insert_prop(const dav_resource * resource,
int propid, dav_prop_insert what,
apr_text_header * phdr)
{
int i;
const char *value = NULL;
const char *name = NULL;
const char *s = NULL;
apr_pool_t *pool = resource->pool;
dav_repos_resource *dbr = (dav_repos_resource *) resource->info->db_r;
/*
** None of FS provider properties are defined if the resource does not
** exist. Just bail for this case.
**
** Even though we state that the FS properties are not defined, the
** client cannot store dead values -- we deny that thru the is_writable
** hook function.
*/
if (!resource->exists)
return DAV_PROP_INSERT_NOTDEF;
/* find propname using prop id */
for( i = 0; dav_repos_props[i].name; i++ )
{
if( propid == dav_repos_props[i].propid )
{
name = dav_repos_props[i].name;
break;
}
}
/* ### what the heck was this property? */
if( name == NULL )
return DAV_PROP_INSERT_NOTDEF;
/* Get value */
value = apr_hash_get(dbr->lpr_hash, name, APR_HASH_KEY_STRING);
/* ### Not found in the hash */
if( value == NULL )
return DAV_PROP_INSERT_NOTDEF;
/* Do something according to what */
if( what == DAV_PROP_INSERT_VALUE )
{
s = apr_psprintf(pool, "<D:%s>%s</D:%s>" DEBUG_CR, name, value, name);
}
else if( what == DAV_PROP_INSERT_NAME )
{
s = apr_psprintf(pool, "<D:%s/>" DEBUG_CR, name);
}
else
{
/* assert: what == DAV_PROP_INSERT_SUPPORTED */
s = apr_psprintf(pool,
"<D:supported-live-property D:name=\"%s\" "
"D:namespace=\"%s\"/>" DEBUG_CR,
name, dav_repos_namespace_uris[0]);
}
apr_text_append(pool, phdr, s);
/* we inserted what was asked for */
return what;
}
static int dav_repos_is_writable(const dav_resource * resource, int propid)
{
int i;
// 2013.08.29 dadamin
// 디렉토리에 대해서 hidden, getlastmodified 수정 허용함
// 2013.08.22 dadamin
// 디렉토리 경우 유일한 값을 추출할 수 없어서 속성 변경 불가능 하도록함
//if(resource && resource->info)
//{
// dav_resource_private *ctx = resource->info;
// if(ctx->db_r)
// {
// if(ctx->db_r->m_resource_type)
// return 0;
// }
//}
/* Try to find is_writable */
for( i = 0; dav_repos_props[i].name; i++ )
{
if( propid == dav_repos_props[i].propid )
{
return dav_repos_props[i].is_writable;
}
}
/* It is writable */
return 1;
}
// 2013.08.21 dadamin
// PROPPATCH hidden 유효성 체크
static dav_error *dav_repos_patch_validate_hidden(const dav_resource * resource, const apr_xml_elem * elem, int operation, void **context)
{
const apr_text *cdata;
const apr_text *f_cdata;
char value;
if (operation == DAV_PROP_OP_DELETE)
{
return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
"The 'hidden' property cannot be removed.");
}
cdata = elem->first_cdata.first;
/* ### hmm. this isn't actually looking at all the possible text items */
f_cdata = elem->first_child == NULL
? NULL
: elem->first_child->following_cdata.first;
if (cdata == NULL) {
if (f_cdata == NULL) {
return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
"The 'hidden' property expects a single "
"character, valued 'T' or 'F'. There was no "
"value submitted.");
}
cdata = f_cdata;
}
else if (f_cdata != NULL)
goto too_long;
if (cdata->next != NULL || strlen(cdata->text) != 1)
goto too_long;
value = cdata->text[0];
if (value != 'T' && value != 'F') {
return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
"The 'hidden' property expects a single "
"character, valued 'T' or 'F'. The value "
"submitted is invalid.");
}
*context = (void *)((long)(value == 'T'));
return NULL;
too_long:
return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
"The 'hidden' property expects a single "
"character, valued 'T' or 'F'. The value submitted "
"has too many characters.");
}
// PROPPATCH getlastmodified 유효성 체크
static dav_error *dav_repos_patch_validate_getlastmodified(const dav_resource * resource, const apr_xml_elem * elem, int operation, void **context)
{
const apr_text *cdata;
const apr_text *f_cdata;
if (operation == DAV_PROP_OP_DELETE)
{
return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
"The 'getlastmodified' property cannot be removed.");
}
cdata = elem->first_cdata.first;
/* ### hmm. this isn't actually looking at all the possible text items */
f_cdata = elem->first_child == NULL
? NULL
: elem->first_child->following_cdata.first;
if (cdata == NULL) {
if (f_cdata == NULL) {
return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
"The 'getlastmodified' property expects a UTC "
"timestamp. There was no value submitted.");
}
cdata = f_cdata;
}
else if (f_cdata != NULL)
goto empty_data;
if (cdata->next != NULL || strlen(cdata->text) <= 0)
goto empty_data;
//value = cdata->text[0];
if (atoll(cdata->text) <= 0) {
return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
"The 'getlastmodified' property expects a UTC timestamp."
"The value submitted is invalid.");
}
*context = (void *)(cdata->text);
return NULL;
empty_data :
return solbox_dav_new_error(resource->info->pool, HTTP_CONFLICT, 0,
"The 'getlastmodified' property expects a UTC "
"timestamp. There is no data.");
}
static dav_error *dav_repos_patch_validate( const dav_resource * resource, const apr_xml_elem * elem, int operation, void **context, int *defer_to_dead )
{
// 2013.08.21 dadamin
// PROPPATCH 오퍼레이션 및 설정값 유효성 체크
dav_elem_private *priv = elem->priv;
DBG2("dav_repos_patch_validate: propid[%d], operation [%d]", priv->propid, operation);
switch(priv->propid)
{
case DAV_PROPID_hidden:
return dav_repos_patch_validate_hidden(resource, elem, operation, context);
break;
case DAV_PROPID_getlastmodified:
return dav_repos_patch_validate_getlastmodified(resource, elem, operation, context);
break;
default:
*defer_to_dead = 1;
break;
}
return NULL;
}
static dav_error *dav_repos_patch_exec( const dav_resource * resource, const apr_xml_elem * elem, int operation, void *context, dav_liveprop_rollback ** rollback_ctx )
{
// 2013.08.21 dadamin
// PROPPATCH 에 해당 되는 오퍼레이션 동작
dav_resource_private *ctx = resource->info;
dav_elem_private *priv = elem->priv;
bool is_uricaseignore = false;
DBG2("dav_repos_patch_exec: propid[%d], operation [%d]", priv->propid, operation);
int res = dav_shared_is_uri_ignore_case( resource );
if( res == 1 ) // URI 대소문자 무시 서비스인 경우
{
is_uricaseignore = true;
}
else // 양방향 기본 ( URI 대소문자 구분 )
{
// 만약 dav_shared_is_uri_ignore_case_by_tranid 호출시 오류가 발생한 경우.. 양방향 기본은 대소문자 구분으로 처리한다.
if( res == -1 )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"dav_repos_patch_exec: dav_shared_is_uri_ignore_case func fail. tranID[%" APR_INT64_T_FMT "]"
, resource->info->user_id);
}
is_uricaseignore = false;
}
switch(priv->propid)
{
case DAV_PROPID_hidden:
{
long value = context != NULL;
// don't do anything if there is no change. no rollback info either.
if( ctx->db_r->m_source == value )
break;
if( dbms_update_hidden(ctx->db_r, value, is_uricaseignore) == -1)
{
return solbox_dav_new_error(resource->info->pool,
HTTP_INTERNAL_SERVER_ERROR, 0,
"Could not set the hidden flag of the "
"target resource.");
}
}
break;
case DAV_PROPID_getlastmodified:
{
apr_int64_t value = atoll((char * )context);
// don't do anything if there is no change. no rollback info either.
if(value == ctx->db_r->m_get_lastmodified)
break;
if( dbms_update_file_lastmodified(ctx->db_r, value, is_uricaseignore) == -1)
{
return solbox_dav_new_error(resource->info->pool,
HTTP_INTERNAL_SERVER_ERROR, 0,
"Could not set the getlastmodified value of the "
"target resource.");
}
}
break;
default:
break;
}
return NULL;
}
static void dav_repos_patch_commit( const dav_resource * resource, int operation, void *context, dav_liveprop_rollback * rollback_ctx )
{
/* nothing to do */
// 2013.08.21 dadamin
// dav_repos_patch_exec() 함수에서 모두 처리되기 때문에 후 처리 필요 없음
}
static dav_error *dav_repos_patch_rollback( const dav_resource * resource, int operation, void *context, dav_liveprop_rollback * rollback_ctx )
{
/* nothing to do */
// 2013.08.21 dadamin
// dav_repos_patch_exec() 함수에서 모두 처리되기 때문에 후 처리 필요 없음
return NULL;
}
static const dav_hooks_liveprop dav_repos_hooks_liveprop = {
dav_repos_insert_prop,
dav_repos_is_writable,
dav_repos_namespace_uris,
dav_repos_patch_validate,
dav_repos_patch_exec,
dav_repos_patch_commit,
dav_repos_patch_rollback
};