/**************************************************************************** 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 #include #include #include #include #include #include #include #define CORE_PRIVATE #include /* for ap_construct_url */ #include #include #include #include #include #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, ""); 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, "%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 */ 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, "%s" DEBUG_CR, name, value, name); } else if( what == DAV_PROP_INSERT_NAME ) { s = apr_psprintf(pool, "" DEBUG_CR, name); } else { /* assert: what == DAV_PROP_INSERT_SUPPORTED */ s = apr_psprintf(pool, "" 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 };