2701 lines
92 KiB
C
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
|
|
};
|