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

1470 lines
36 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: util.c,v 1.5 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/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <iconv.h>
#include <httpd.h>
#include <http_config.h>
#include <http_protocol.h>
#include <http_log.h>
#include <http_request.h>
#include <http_core.h> /* for ap_construct_url */
#include <apr.h>
#include <apr_md5.h>
#include <apr_strings.h>
#include <apr_hash.h>
#include <apr_tables.h>
#include <apr_file_io.h>
#include <apu_version.h>
#include <apr_dbd.h>
#include <mod_dbd.h>
#include "dav_repos.h"
#include "dbms.h"
#include "util.h"
/* Note: the "dav_repos" prefix is mandatory */
extern module AP_MODULE_DECLARE_DATA dav_repos_module;
/*****************************/
/* common functions */
// FreeBSD 6.2 설치된 iconv 경우 iconv에 srcbuf를 const char* 로 받음
#ifdef __FreeBSD__
static char *iconv_string(apr_pool_t *p, iconv_t cd, const char *srcbuf, size_t srclen)
#else // !__FreeBSD__
static char *iconv_string(apr_pool_t *p, iconv_t cd, char *srcbuf, size_t srclen)
#endif // __FreeBSD__
{
char *outbuf, *marker;
size_t outlen;
if (srclen == 0) {
#ifdef __FreeBSD__
return (char *)srcbuf;
#else // !__FreeBSD__
return srcbuf;
#endif // __FreeBSD__
}
/* Allocate space for conversion. Note max bloat factor is 4 of UCS-4 */
marker = outbuf = (char *)apr_palloc(p, outlen = srclen * 4 + 1);
if (outbuf == NULL) {
return NULL;
}
/* Convert every character within input string. */
while (srclen > 0) {
#ifdef __FreeBSD__
if (iconv(cd, (const char **)&srcbuf, &srclen, &outbuf, &outlen) == (size_t)(-1))
#else // !__FreeBSD__
if (iconv(cd, &srcbuf, &srclen, &outbuf, &outlen) == (size_t)(-1))
#endif // __FreeBSD__
{
return NULL;
}
}
*outbuf = '\0';
return marker;
}
// NEW 2019-09-18 huibong 인코딩 처리 관련 신규 추가 (#32584)
// - URL 인코딩에 대한 판단 오류 상황을 보완하기 위해 인터넷에 유통 중인 소스 중 신뢰성 있는 소스의 기능을 추가
// - 본 소스는 전달받은 문자열이 UTF-8 인코딩인지를 판단하는 함수로서
// - android base 소스에 등록된 함수임
// - 참고자료
// -- https://android.googlesource.com/platform/frameworks/base/+/master/media/jni/android_media_MediaScanner.cpp
static bool isValidUtf8( const char * bytes )
{
while( *bytes != '\0' )
{
unsigned char utf8 = *( bytes++ );
//ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "isValidUtf8: [%c][%02X][%02X]", utf8, utf8, utf8>>4);
// Switch on the high four bits.
switch( utf8 >> 4 )
{
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
// Bit pattern 0xxx. No need for any extra bytes.
break;
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0f:
/*
* Bit pattern 10xx or 1111, which are illegal start bytes.
* Note: 1111 is valid for normal UTF-8, but not the
* modified UTF-8 used here.
*/
return false;
case 0x0e:
// Bit pattern 1110, so there are two additional bytes.
utf8 = *( bytes++ );
if( ( utf8 & 0xc0 ) != 0x80 ) {
return false;
}
// Fall through to take care of the final byte.
//FALLTHROUGH_INTENDED;
case 0x0c:
case 0x0d:
// Bit pattern 110x, so there is one additional byte.
utf8 = *( bytes++ );
//ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "isValidUtf8: [%c][%02X][%02X]", utf8, utf8, utf8 & 0xc0 );
if( ( utf8 & 0xc0 ) != 0x80 ) {
return false;
}
// ADD 2019-09-18 huibong 연관일감 #21096 관련 회피 기능 추가 ((#32584)
// - "책" 문자열이 utf-8 로 인식되는 거를 막기 위해 회피하도록 조건 추가
if( utf8 == 0xa5 ) {
return false;
}
break;
}
}
return true;
}
// CHG 2019-09-18 huibong URL의 정확한 인코딩 탐지 실패 관련 기능 보완 (#32584)
// - #32584 에서 UTF-8 기반 url 을 UHC 로 인식하는 문제 발생
// - UTF-8, UHC 혼합사용으로 인해 정확한 탐지를 실패할 가능성이 존재하지만.....
// - 이를 조금이라도 보완하기 위해 인코딩 check 로직을 보완함.
static int is_valid_uhc( char * buf )
{
// CHG 2019-09-18 huibong UTF-8 enconding 인지 check 기능 추가 (#32584)
// - 우선 기본으로 UTF-8 인 요청을 거른다.
// - 인코딩 특성상 UHC 인데.. UTF-8 로 인식될 가능성이 있다.
// - 이런 경우에는 isValidUtf8() 함수에 해당 요청에 대한 예외를 추가하는 방법 밖에 없다.
if( isValidUtf8( buf ) == true )
{
//ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "is_valid_uhc: isValidUtf8() return UTF-8 [%s]", buf );
return 0;
}
// 일단 UTF-8 인코딩이 아니라고 판단된 경우....
// - 대부분 UHC 이겠지만....
// - UTF-8 인데.. 예외 처리 등으로 UHC 로 잘못 판정될 가능성 존재하므로.....
// - iconv 를 이용하여 다시 UHC 인지 다시 확인한다.
char outBuf[4096] = { 0 };
iconv_t cd = iconv_open( "UTF-8", "UHC" );
if( cd == (iconv_t)( -1 ) )
{
ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "is_valid_uhc: iconv open failed [%s]", buf );
return 0;
}
size_t readBytes = strlen( buf );
size_t writeBytes = sizeof( outBuf );
char * in = buf;
char * out = outBuf;
if( iconv( cd, (const char **)& in, &readBytes, &out, &writeBytes ) == (size_t)-1 )
{
int errorNum = errno;
iconv_close( cd );
// UHC -> UTF-8 로 변환 실패시...
// - 변환 성공시 UHC 이지만.. 변환 실패시에는 UTF-8 로 판정한다.
ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "is_valid_uhc: iconv UHC->UTF-8 error. reuturn UTF-8 [%d][%s]", errorNum, buf );
return 0;
}
iconv_close( cd );
// 최종 UHC 로 판정한다.
//ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "is_valid_uhc: return UHC [%s]", buf );
return 1;
}
// UTF-8 -> Database Character Set
char* transformDBCharacter(request_rec *r, char* srcbuf, char *prefixmsg )
{
char *ret = NULL;
char *srccharset = NULL, *destcharset = NULL;
dav_repos_server_conf *conf = NULL;
conf = dav_repos_get_server_conf(r->server);
iconv_t cd;
if( conf == NULL )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"%s: dav_repos_get_server_conf NULL. return srcbuf", prefixmsg);
return srcbuf;
}
if( conf->db_charset == NULL )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"%s: config DavProviderDBCharset set NULL. return srcbuf", prefixmsg);
return srcbuf;
}
destcharset = (char *) conf->db_charset;
// Database Character UTF-8 지원 시 해당 로직은 수행하지 않음
if (strcmp( destcharset, "UTF-8") == 0)
{
return srcbuf;
}
// check UHC Character
if ( is_valid_uhc(srcbuf) == 1)
{
ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "transformDBCharacter: url encoding is UHC [%s][%s]", prefixmsg, srcbuf);
srccharset = "UHC";
}
else
{
//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "transformDBCharacter: url encoding is UTF-8 [%s][%s]", prefixmsg, srcbuf);
srccharset = "UTF-8";
}
// unknown Character set and return srcbuf
if( srccharset == NULL )
{
return srcbuf;
}
// compare character set
if( strcmp(srccharset, destcharset) == 0 )
{
//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s: same Character [%s]", prefixmsg, srcbuf);
return srcbuf;
}
// transform src character set -> DB character set
cd = iconv_open(destcharset, srccharset);
if( cd == (iconv_t)(-1))
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s: iconv open failed [%s]", prefixmsg, srcbuf);
ret = apr_pstrdup(r->pool, srcbuf);
}
else
{
ret = iconv_string(r->pool, cd, srcbuf, strlen(srcbuf));
if (ret == NULL)
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s: iconv error [%s]",prefixmsg, srcbuf);
ret = apr_pstrdup(r->pool, srcbuf);
}
iconv_close(cd);
}
return ret;
}
// Database Character Set -> UTF-8
char* transformUTF8(request_rec *r, char* srcbuf, char *prefixmsg )
{
char *ret = NULL;
char *srccharset = NULL, *destcharset = NULL;
dav_repos_server_conf *conf = NULL;
conf = dav_repos_get_server_conf(r->server);
iconv_t cd;
destcharset = "UTF-8";
if( conf == NULL )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"%s: dav_repos_get_server_conf NULL. return srcbuf", prefixmsg);
return srcbuf;
}
if( conf->db_charset == NULL )
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
"%s: config DavProviderDBCharset set NULL. return srcbuf", prefixmsg);
return srcbuf;
}
// check UHC Character
if (is_valid_uhc(srcbuf) == 1)
{
srccharset = "UHC";
}
else
{
srccharset = "UTF-8";
}
// compare character set
if( strcmp(srccharset, destcharset) == 0 )
{
return srcbuf;
}
// transform DB character set -> UTF-8
cd = iconv_open(destcharset, srccharset);
if( cd == (iconv_t)(-1))
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s: iconv open failed [%s]", prefixmsg, srcbuf);
ret = apr_pstrdup(r->pool, srcbuf);
}
else
{
ret = iconv_string(r->pool, cd, srcbuf, strlen(srcbuf));
if (ret == NULL)
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "%s: iconv error[%s]", prefixmsg, srcbuf);
ret = apr_pstrdup(r->pool, srcbuf);
}
iconv_close(cd);
}
return ret;
}
int dav_repos_remove_base_uri(const char *base_uri, const char *req_uri, char *result)
{
int nCount = 0;
int req_uri_length = 0;
if((base_uri == NULL) || (req_uri == NULL) || (result == NULL))
{
return -1;
}
req_uri_length = strlen(req_uri);
if(req_uri_length == 1)
{
if((*req_uri == '/') || (*req_uri == '\0') || (*req_uri == ' '))
{
result[0] = '/';
result[1] = '\0';
}
else
{
return -2;
}
return 1;
}
else if(req_uri_length == 0)
{
return -3;
}
while((*req_uri != '\0') && (*base_uri != '\0'))
{
if(*base_uri == *req_uri)
{
base_uri++;
req_uri++;
}
else
{
break;
}
}
while(*req_uri != '\0')
{
result[nCount++] = *req_uri++;
}
result[nCount] = '\0';
return 1;
}
int remove_user_root_dir_from_fake_uri(const char *pcszFakeURI, char *pURI)
{
int nCount = 0;
int sCount = 0;
char sztmpBuffer[MaxSizeOfPathLength];
if(pcszFakeURI == NULL)
{
return -1;
}
if(strlen(pcszFakeURI) > MaxSizeOfPathLength)
{
return -2;
}
while(*pcszFakeURI != '\0')
{
if(*pcszFakeURI == '/')
{
sCount++;
if(sCount == 2)
{
nCount = 0;
memset(sztmpBuffer, 0, MaxSizeOfPathLength);
sztmpBuffer[nCount++] = *pcszFakeURI++;
}
}
sztmpBuffer[nCount++] = *pcszFakeURI++;
}
sztmpBuffer[nCount] = '\0';
snprintf(pURI, MaxSizeOfPathLength, "%s", sztmpBuffer);
return 1;
}
int get_filename_in_path(const char *pcszPath, char *pszPathName, char *pszFileName)
{
int nCount = 0;
char sztmpBuffer[MaxSizeOfPathLength];
if((pcszPath == NULL) || (pszFileName == NULL) || (pszPathName == NULL))
{
return -1;
}
if(strlen(pcszPath) > MaxSizeOfPathLength)
{
return -2;
}
memset(sztmpBuffer, 0, MaxSizeOfPathLength);
while(*pcszPath != '\0')
{
if(*pcszPath == '/')
{
sztmpBuffer[nCount++] = *pcszPath++;
if(*pcszPath == '\0')
{
break;
}
sztmpBuffer[nCount] = '\0';
//DBG1("get_filename_in_path::sztmpBuffer = %s", sztmpBuffer);
strncat(pszPathName, sztmpBuffer, MaxSizeOfPathLength - strlen(pszPathName));
nCount = 0;
memset(sztmpBuffer, 0, MaxSizeOfPathLength);
}
sztmpBuffer[nCount++] = *pcszPath++;
}
sztmpBuffer[nCount] = '\0';
snprintf(pszFileName, MaxSizeOfPathLength, "%s", sztmpBuffer);
return 1;
}
// CHG 2010-08-10 huibong
// Hash 정보 생성 처리시 생성 결과를 static 변수에 저장하고
// 해당 변수를 반환처리 하는 경우 ..
// 동시간 접근시 Lock 을 사용하지 않게 되면 문제가 발생하게 됨. ( File Name에 대한 Hash 정보가 동일하게 생성)
// 하지만 Lock 을 처리속도에 영향을 미칠수 있으므로
// 결과 값을 저장할 출력변수를 매개변수로 전달받도록 수정처리 한다.
//char *get_hashed_filepath(const char *plain_filepath)
bool get_hashed_filepath( const char *plain_filepath, char * result, int result_length)
{
// 매개변수에 대한 검증 처리
if( plain_filepath == NULL || result == NULL || result_length < (APR_MD5_DIGESTSIZE * 2 + 1))
return false;
else
{
// 출력값을 저장할 변수가 적합한 경우.
int i;
unsigned char digest[APR_MD5_DIGESTSIZE];
apr_md5(digest, (const unsigned char *) plain_filepath, strlen(plain_filepath));
// 화면에 표시 가능하도록 Hex 처리.
for (i = 0; i < APR_MD5_DIGESTSIZE; i++)
apr_snprintf((char *) &result[i + i], 3, "%02x", digest[i]);
result[APR_MD5_DIGESTSIZE * 2] = '\0';
return true;
}
}
/* split user_login_id@partner_login_id into */
/* user_login_id, partner_login_id */
/* 2007. 07. 30: Elenoa Lazyfake
* r->user format changed: for classified traffic manage
* (in Makefile, __CLASSIFIED_TRAFFIC_MANAGE__)
* from: tranid@userid@sessionkey
* to: tranid.classid@userid@sessionkey
*/
int at_split(const char *pszAcctInfo, acct_info *pAcctInfo)
{
int fFound = 0;
int chCount = 0;
char sztmpBuffer[MaxSizeOfAcctSession + 1];
if((pszAcctInfo == NULL) || (pAcctInfo == NULL))
{
return -1;
}
/* 0nd step : clear acct_info buffer */
memset((char *)pAcctInfo, 0, sizeof(acct_info));
/* 1st step : parse user login id */
fFound = 0;
chCount = 0;
memset(sztmpBuffer, 0, MaxSizeOfAcctVolume);
while(*pszAcctInfo != '\0')
{
if(*pszAcctInfo == '@')
{
sztmpBuffer[chCount] = '\0';
snprintf(pAcctInfo->szVolumeID, MaxSizeOfAcctVolume, "%s", sztmpBuffer);
DBG1("at_split::sztmpBuffer #1(end) = %s", sztmpBuffer);
fFound = -1;
break;
}
#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
if(*pszAcctInfo == '.')
{
sztmpBuffer[chCount] = '\0';
snprintf(pAcctInfo->szVolumeID, MaxSizeOfAcctVolume, "%s", sztmpBuffer);
DBG1("at_split::sztmpBuffer #1(cont') = %s", sztmpBuffer);
fFound = 1;
break;
}
#endif
sztmpBuffer[chCount++] = *pszAcctInfo++;
}
#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
if (fFound == -1)
{
sprintf(pAcctInfo->szTCClassID, "0");
goto second_step;
}
#endif
if (fFound == 0)
{
#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
pAcctInfo->szTCClassID[0] = '\0';
#endif
return -2;
}
#ifdef __CLASSIFIED_TRAFFIC_MANAGE__
fFound = 0;
chCount = 0;
pszAcctInfo++;
memset(sztmpBuffer, 0, MaxSizeOfAcctVolume);
while(*pszAcctInfo != '\0')
{
if(*pszAcctInfo == '@')
{
sztmpBuffer[chCount] = '\0';
snprintf(pAcctInfo->szTCClassID, MaxSizeOfAcctVolume, "%s", sztmpBuffer);
DBG1("at_split::sztmpBuffer #1(cid) = %s", sztmpBuffer);
fFound = 1;
break;
}
sztmpBuffer[chCount++] = *pszAcctInfo++;
}
if(fFound != 1)
{
return -3;
}
second_step:
#endif
/* 2nd step : parse partner login id */
fFound = 0;
chCount = 0;
pszAcctInfo++;
memset(sztmpBuffer, 0, MaxSizeOfAcctUserID);
while(*pszAcctInfo != '\0')
{
if(*pszAcctInfo == '@')
{
sztmpBuffer[chCount] = '\0';
snprintf(pAcctInfo->szUserID, MaxSizeOfAcctUserID, "%s", sztmpBuffer);
DBG1("at_split::sztmpBuffer #2 = %s", sztmpBuffer);
fFound = 1;
break;
}
sztmpBuffer[chCount++] = *pszAcctInfo++;
}
if(fFound != 1)
{
sztmpBuffer[chCount] = '\0';
snprintf(pAcctInfo->szUserID, MaxSizeOfAcctUserID, "%s", sztmpBuffer);
DBG1("at_split::sztmpBuffer #2 = %s, #3 none", sztmpBuffer);
pAcctInfo->szSessionID[0] = '\0';
}
else
{
/* 3rd step : parse session id */
chCount = 0;
memset(sztmpBuffer, 0, MaxSizeOfAcctSession);
pszAcctInfo++;
while(*pszAcctInfo != '\0')
{
sztmpBuffer[chCount++] = *pszAcctInfo++;
}
sztmpBuffer[chCount] = '\0';
snprintf(pAcctInfo->szSessionID, MaxSizeOfAcctSession, "%s", sztmpBuffer);
DBG1("at_split::sztmpBuffer #3 = %s", sztmpBuffer);
}
return 1;
}
int check_string(char *to, const char *from, size_t length)
{
int nCount = 0;
char tmpBuff[length];
if((to == NULL) || (from == NULL))
{
return -1;
}
while((*from != '\0') && (length > 0))
{
if(*from != '\'')
{
tmpBuff[nCount++] = *from;
}
from++;
length--;
}
tmpBuff[nCount] = '\0';
snprintf(to, nCount + 1, "%s", tmpBuff);
return 1;
}
/*************************/
/* configuration support */
dav_repos_server_conf *dav_repos_get_server_conf( server_rec *s )
{
if( s == NULL )
return NULL;
else
return ap_get_module_config(s->module_config, &dav_repos_module);
}
inline void dav_spin_wait(int vsleep)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = vsleep;
select (0, NULL, NULL, NULL, &tv);
}
ap_dbd_t *dav_repos_get_db(request_rec *r)
{
ap_dbd_t *db;
/* loop until get acquire db session */
if( (db = ap_dbd_open(r->server->process->pool, r->server)) != NULL )
{
return db;
}
/* can reach here? */
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "OPENDAV:dav_repos_get_db database connection pool error.");
return NULL;
}
void dav_repos_release_db(request_rec *r, ap_dbd_t *db, int o_rv)
{
int retval;
{
ap_dbd_close(r->server, db);
retval = OK;
}
if (retval == !OK)
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "OPENDAV: database not properly released");
}
}
int dav_repos_begin_trans(const char *func, request_rec *r, ap_dbd_t *db, apr_dbd_transaction_t **trans)
{
int rv;
rv = apr_dbd_transaction_start(db->driver, r->pool, db->handle, trans);
if (rv)
{
char errbuf[1024];
sprintf(errbuf, "%s: begin transaction", func);
db_error_message(db, errbuf, rv);
dav_repos_release_db(r, db, rv);
//apr_thread_rwlock_unlock(db_lock);
return -20;
}
return 0;
}
int dav_repos_end_trans(const char *func, request_rec *r, ap_dbd_t *db, apr_dbd_transaction_t *trans)
{
int rv;
rv = apr_dbd_transaction_end(db->driver, r->pool, trans);
if (rv)
{
char errbuf[1024];
sprintf(errbuf, "%s: end transaction", func);
db_error_message(db, errbuf, rv);
dav_repos_release_db(r, db, rv);
//apr_thread_rwlock_unlock(db_lock);
return -21;
}
return 0;
}
/* Note: picked up from ap_gm_timestr_822() */
/* NOTE: buf must be at least DAV_TIMEBUF_SIZE chars in size */
void dav_repos_format_time(int style, apr_time_t sec, char *buf)
{
apr_time_exp_t tms;
DBG1("dav_repos_format_time:sec = %" APR_TIME_T_FMT "", sec);
(void) apr_time_exp_gmt(&tms, sec);
TRACE();
if (style == DAV_STYLE_ISO8601)
{
/* ### should we use "-00:00" instead of "Z" ?? */
/* 20 chars plus null term */
sprintf(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2dZ",
tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday,
tms.tm_hour, tms.tm_min, tms.tm_sec);
}
else
{
/* RFC 822 date format; as strftime '%a, %d %b %Y %T GMT' */
/* 29 chars plus null term */
sprintf(buf,
"%s, %.2d %s %d %.2d:%.2d:%.2d GMT",
apr_day_snames[tms.tm_wday],
tms.tm_mday, apr_month_snames[tms.tm_mon],
tms.tm_year + 1900, tms.tm_hour, tms.tm_min, tms.tm_sec);
}
DBG1("dav_repos_format_time:buf = %s", buf);
}
const char *dav_repos_build_ns_name_key(const char *ns, const char *name, apr_pool_t * pool)
{
TRACE();
/* Woops */
if (ns == NULL || name == NULL || pool == NULL)
return NULL;
if (strlen(ns)==0)
ns=" "; /* No namespace */
return apr_psprintf(pool, "%s\t%s", ns, name);
}
void dav_repos_get_ns_name_key(const char *key, dav_prop_name * pname, apr_pool_t * pool)
{
char *str;
pname->ns = pname->name = NULL;
/* Woops */
if (key == NULL || pool == NULL)
return;
str = apr_pstrdup(pool, key);
pname->ns = strtok(str, "\t");
pname->name = strtok(NULL, "\t");
}
/* Make XML response */
dav_response *dav_repos_mkresponse(dav_repos_resource * db_r)
{
int i;
char *s = NULL;
const char *ns;
apr_text_header hdr = { 0 };
apr_text_header hdr_ns = { 0 };
dav_response *res = apr_pcalloc(db_r->p, sizeof(*res));
dav_repos_property *dead_prop;
char *ns_use = apr_pcalloc(db_r->p, DAV_REPOS_MAX_NAMESPACE); /* Maximul namespaces */
/* Resopnse Example
<?xml version="1.0" encoding="utf-8"?>
<D:multistatus xmlns:D="DAV:">
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://localhost/repos/">
<D:href>/</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype><D:collection/></lp1:resourcetype>
<lp1:creationdate>2001-10-22T02:46:33Z</lp1:creationdate>
<lp1:getlastmodified>Mon, 22 Oct 2001 02:46:33 GMT</lp1:getlastmodified>
<lp1:getetag>"59272-1000-7e1c7440"</lp1:getetag>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:lockdiscovery/>
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
</D:multistatus>
*/
/* Make fake data for test */
res->href = db_r->m_uri;
res->status = 200;
TRACE();
DBG0("dav_repos_mkresponse:");
/* Generate namespace first */
/* Dead properties */
for (dead_prop = db_r->pr; dead_prop; dead_prop = dead_prop->next)
{
ns_use[(int) dead_prop->m_ns_id] = 1;
}
/* Add namespace for dead properties */
for (i = 0; i < DAV_REPOS_MAX_NAMESPACE; i++)
{
if (ns_use[i] == 1)
{
ns = dbms_get_ns(db_r, i);
if (ns == NULL || strlen(ns) == 0)
{
ns_use[i] = -1; /* nullns */
}
else
{
s = apr_psprintf(db_r->p, " xmlns:%s%d=\"%s\"", "ns", i, ns);
apr_text_append(db_r->p, &hdr_ns, s);
}
}
}
/* Make XML here */
apr_text_append(db_r->p, &hdr, "<D:propstat>" DEBUG_CR "<D:prop>" DEBUG_CR);
/* Start prop */
/* Dead properties */
for (dead_prop = db_r->pr; dead_prop; dead_prop = dead_prop->next)
{
/* Null ns need xmlns="" */
if (ns_use[(int) dead_prop->m_ns_id] == -1)
{
s = apr_psprintf(db_r->p, "<%s xmlns=\"\">%s</%s>" DEBUG_CR,
dead_prop->m_propname, dead_prop->m_propvalue,
dead_prop->m_propname);
}
else
{
s = apr_psprintf(db_r->p, "<ns%ld:%s>%s</ns%ld:%s>" DEBUG_CR,
dead_prop->m_ns_id, dead_prop->m_propname,
dead_prop->m_propvalue,
dead_prop->m_ns_id, dead_prop->m_propname);
}
apr_text_append(db_r->p, &hdr, s);
ns_use[(int) dead_prop->m_ns_id] = 1;
}
/* Live properties */
if (db_r->lpr_hash) /* build it before mkresponse */
{
long klen;
const char *stmp;
const char *key, *val;
apr_hash_index_t *hindex;
/* Read live props from hash */
for (hindex = apr_hash_first(db_r->p, db_r->lpr_hash); hindex; hindex = apr_hash_next(hindex))
{
apr_hash_this(hindex, (void *) &key, &klen, (void *) &val);
stmp = apr_psprintf(db_r->p, "<D:%s>%s</D:%s>" DEBUG_CR, key, val, key);
DBG1("dav_repos_mkresponse: %s", stmp);
apr_text_append(db_r->p, &hdr, stmp);
}
}
/* Lock discovery print, if we have */
/* FIXME : Do we really need lock info for search */
#if 0
apr_text_append(db_r->p, &hdr, "<lp1:lockdiscovery>" DEBUG_CR);
if (db_r->lockdiscovery)
apr_text_append(db_r->p, &hdr, db_r->lockdiscovery);
apr_text_append(db_r->p, &hdr, "</lp1:lockdiscovery>" DEBUG_CR);
/* Supported lock */
if (db_r->supportedlock)
apr_text_append(db_r->p, &hdr, db_r->supportedlock);
#endif
/* Closing */
apr_text_append(db_r->p, &hdr,
"</D:prop>" DEBUG_CR
"<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
"</D:propstat>" DEBUG_CR);
/*
** name space set up
** Better way??
*/
s = apr_psprintf(db_r->p, " xmlns:%s%d=\"%s\"", "lp", 0, "DAV:");
apr_text_append(db_r->p, &hdr_ns, s);
s = apr_psprintf(db_r->p, " xmlns:%s%d=\"%s\"", "lp", 1, "DAV:");
apr_text_append(db_r->p, &hdr_ns, s);
res->propresult.propstats = hdr.first;
res->propresult.xmlns = hdr_ns.first;
return res;
}
/* Build dead property hash
* Build the hash before makeresponse
*/
void dav_repos_build_pr_hash(dav_repos_resource * db_r)
{
dav_repos_property *dead_prop;
TRACE();
/* Let's build hash */
db_r->pr_hash = apr_hash_make(db_r->p);
/* Dead properties */
for (dead_prop = db_r->pr; dead_prop; dead_prop = dead_prop->next)
{
const char *ns = dbms_get_ns(db_r, dead_prop->m_ns_id);
const char *key = dav_repos_build_ns_name_key(ns, dead_prop->m_propname, db_r->p);
apr_hash_set(db_r->pr_hash, key, APR_HASH_KEY_STRING, dead_prop);
//DBG2("KEY: [%s]%s", key, dead_prop->m_propname);
}
}
/* Build live property */
void dav_repos_build_lpr_hash(dav_repos_resource *db_r)
{
const char *s;
dav_repos_server_conf *dsc = NULL;
/* an HTTP-date can be 29 chars plus a null term */
/* a 64-bit size can be 20 chars plus a null term */
char date[DAV_TIMEBUF_SIZE];;
TRACE();
dsc = dav_repos_get_server_conf(db_r->r->server);
if (!dsc)
return;
/* Let's build hash */
db_r->lpr_hash = apr_hash_make(db_r->p);
/* is directory */
if (db_r->m_resource_type == dav_repos_COLLECTION)
{
apr_hash_set(db_r->lpr_hash, "resourcetype", APR_HASH_KEY_STRING, "<D:collection/>");
}
else
{
apr_hash_set(db_r->lpr_hash, "resourcetype", APR_HASH_KEY_STRING, "");
}
s = apr_psprintf(db_r->p, "%" APR_INT64_T_FMT "", db_r->m_get_content_length);
apr_hash_set(db_r->lpr_hash, "getcontentlength", APR_HASH_KEY_STRING, s);
DBG1("dav_repos_build_lpr_hash:db_r->m_creation_date= %" APR_INT64_T_FMT "", db_r->m_creation_date);
/* set live properties */
dav_repos_format_time(DAV_STYLE_ISO8601, db_r->m_creation_date, date);
apr_hash_set(db_r->lpr_hash, "creationdate", APR_HASH_KEY_STRING, apr_pstrdup(db_r->p, date));
DBG1("dav_repos_build_lpr_hash:db_r->m_get_lastmodified = %" APR_INT64_T_FMT "", db_r->m_get_lastmodified);
dav_repos_format_time(DAV_STYLE_RFC822, db_r->m_get_lastmodified, date);
apr_hash_set(db_r->lpr_hash, "getlastmodified", APR_HASH_KEY_STRING, apr_pstrdup(db_r->p, date));
apr_hash_set(db_r->lpr_hash, "getetag", APR_HASH_KEY_STRING, dav_repos_getetag_dbr(db_r));
apr_hash_set(db_r->lpr_hash, "getcontenttype", APR_HASH_KEY_STRING, db_r->m_get_content_type ? db_r->m_get_content_type : "");
DBG2("dav_repos_build_lpr_hash:db_r->m_host_name=%s dsc->host_name=%s", db_r->m_host_name, dsc->host_name);
/* CHG 2010-07-02 huibong
* NetCache 상에서 PROFIND 에 대한 결과 요청시 원본 host_name 정보를 무조건 입력해서 보낼도록 수정
* 기존 양방향과 호환을 위해 User-Agent String 상에 NetCache 가 존재하는 경우에만 처리하도록 함.
* 양방향에서 현 장비에 해당 파일이 존재하는 경우 Connection 재사용을 위해 Host Name 정보를 보내지 않음.
*/
/*
apr_hash_set(db_r->lpr_hash, "source", APR_HASH_KEY_STRING,
(db_r->m_host_name && strcmp(db_r->m_host_name, dsc->host_name) ?
apr_pstrdup(db_r->p, db_r->m_host_name) : ""));
*/
if( db_r->m_host_name != NULL ) // 원본 파일에 대한 Host Name 정보가 존재하는 경우
{
// 원본 Host Name 과 현재 장비의 Host Name 이 틀린 경우 => Host Name 정보 전달.
if( strcmp(db_r->m_host_name, dsc->host_name) != 0 )
{
apr_hash_set(db_r->lpr_hash, "source", APR_HASH_KEY_STRING, apr_pstrdup(db_r->p, db_r->m_host_name));
}
else
{
// NetCache Agent 인지를 확인하기 위해 PROFPIND Header 의 User-Agent 정보 검사.
const char * userAgent = NULL;
userAgent = apr_table_get( db_r->r->headers_in, "User-Agent");
if( userAgent != NULL && (strncmp(userAgent, "NetCache", 8)==0) )
{
// User-Agent 정보가 NetCache 로 시작하는 경우 -> 무조건 Host Name 정보를 입력하여 전달.
//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,"dav_repos_build_lpr_hash: User-Agent[%s] source[%s]", userAgent, db_r->m_host_name);
apr_hash_set(db_r->lpr_hash, "source", APR_HASH_KEY_STRING, apr_pstrdup(db_r->p, db_r->m_host_name));
}
else
{
// 기존 양방향 Client 인 경우 Host Name 정보와 현재 장비가 동일한 하면 => 빈 문자열 전달.
apr_hash_set(db_r->lpr_hash, "source", APR_HASH_KEY_STRING, "");
}
}
}
else
{
// 원본 파일에 대한 Host Name 정보가 존재하지 않는 경우 빈값을 전달처리
apr_hash_set(db_r->lpr_hash, "source", APR_HASH_KEY_STRING, "");
}
// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
apr_hash_set(db_r->lpr_hash, "getcontentlanguage", APR_HASH_KEY_STRING, "");
}
/*# FIXME : This is a insert lock info for search and prop.
** Do we really need a lock info when we search ?
** Then we should make params in search semewhere.
**
** Need to move find_live and live props
*/
dav_error *dav_repos_insert_lock_prop(const dav_walk_params * params, dav_repos_resource * db_r)
{
dav_error *err;
dav_resource *resource = NULL;
//dav_walker_ctx *ctx = params->walk_ctx;
TRACE();
/* Initilize */
// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
if (params->lockdb != NULL)
{
dav_lock *locks = NULL;
/*
* FIXME : We don't have a resource here
* Our lock hook will use only uri and exist filed in resource
*/
resource = apr_pcalloc(db_r->p, sizeof(*resource));
resource->exists = 1;
resource->uri = db_r->m_fake_uri;
if ((err = dav_lock_query(params->lockdb, resource, &locks)) != NULL)
{
return dav_push_error(db_r->p, err->status, 0,
"DAV:lockdiscovery could not be "
"determined due to a problem fetching "
"the locks for this resource.", err);
}
/* fast-path the no-locks case */
if (locks)
{
/*
** This may modify the buffer. value may point to
** wb_lock.pbuf or a string constant.
*/
// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
//db_r->m_lock_discovery = dav_lock_get_activelock(ctx->r, locks, NULL);
/* Do we need strdup here */
/* db_r->lockdiscovery = apr_pstrdup(db_r->p, wb_lock.buf); */
}
/*
* Get supported lock info
* Our lock hook will not use resource though.
*/
// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
}
return NULL;
}
/* make sure the pathname does not have a trailing "/" */
void dav_repos_no_trail(char *str)
{
int len = strlen(str);
while (len > 1 && str[len - 1] == '/')
{
str[len - 1] = '\0';
len = strlen(str);
}
TRACE();
}
int dav_repos_remove_unsafe_chars(const char *pOld, char *pNew)
{
int nCount = 0;
if((pOld == NULL) || (pNew == NULL))
{
return -1;
}
while(*pOld != '\0')
{
if((*pOld == '\'') || (*pOld == '\\'))
{
pOld++;
}
else
{
pNew[nCount++] = *pOld++;
}
}
pNew[nCount] = '\0';
return 1;
}
int dav_repos_replace_unsafe_chars(const char *pOld, char *pNew)
{
if ((pOld == NULL) || (pNew == NULL))
{
return -1;
}
char const * const pattern = "'";
char const * const replacement = "''";
size_t const replen = strlen(replacement);
size_t const patlen = strlen(pattern);
const char * oriptr;
const char * patloc;
memset(pNew, 0x00, sizeof(*pNew));
for (oriptr = pOld; (patloc = strstr(oriptr, pattern)); oriptr = patloc + patlen)
{
size_t const skplen = patloc - oriptr;
// copy the section until the occurence of the pattern
strncpy(pNew, oriptr, skplen);
pNew += skplen;
// copy the replacement
strncpy(pNew, replacement, replen);
pNew += replen;
}
// copy the rest of the string.
strcpy(pNew, oriptr);
return 1;
}
int dav_repos_restore_unsafe_chars(const char *pOld, char *pNew)
{
if ((pOld == NULL) || (pNew == NULL))
{
return -1;
}
char const * const pattern = "''";
char const * const replacement = "'";
size_t const replen = strlen(replacement);
size_t const patlen = strlen(pattern);
const char * oriptr;
const char * patloc;
memset(pNew, 0x00, sizeof(*pNew));
for (oriptr = pOld; (patloc = strstr(oriptr, pattern)); oriptr = patloc + patlen)
{
size_t const skplen = patloc - oriptr;
// copy the section until the occurence of the pattern
strncpy(pNew, oriptr, skplen);
pNew += skplen;
// copy the replacement
strncpy(pNew, replacement, replen);
pNew += replen;
}
// copy the rest of the string.
strcpy(pNew, oriptr);
return 1;
}
int dav_repos_replace_multi_slash_chars(const char *pOld, char *pNew)
{
int nCount = 0;
int slash_detect = 0;
if ((pOld == NULL) || (pNew == NULL))
{
return -1;
}
while (*pOld != '\0')
{
if (*pOld == '/')
{
if (slash_detect == 0)
{
slash_detect = 1;
}
else
{
pOld++;
continue;
}
}
else
{
slash_detect = 0;
}
pNew[nCount++] = *pOld++;
}
pNew[nCount] = '\0';
return 1;
}
#ifdef __OPENDISK_SOLUTION__
int dav_repos_change_resource_type(int resource_type)
{
if (resource_type >= dav_repos_RECYCLE)
{
return (resource_type - dav_repos_RECYCLE);
}
else if (resource_type >= dav_repos_SHARE)
{
return (resource_type - dav_repos_SHARE);
}
return resource_type;
}
#endif
#define PRIVATE_KEY "gE\x8bk\xc6\x23{2i\x98<dsH3f"
//static void __tea_encrypt(unsigned int *v, unsigned int *k)
//{
// unsigned int v0 = v[0], v1 = v[1], sum = 0, i; /* set up */
// unsigned int delta = 0x9e3779b9; /* a key schedule constant */
// unsigned int k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
// for (i = 0; i < 32; i++) { /* basic cycle start */
// sum += delta;
// v0 += (v1 << 4) + (k0 ^ v1) + (sum ^ (v1 >> 5)) + k1;
// v1 += (v0 << 4) + (k2 ^ v0) + (sum ^ (v0 >> 5)) + k3; /* end cycle */
// }
// v[0] = v0; v[1] = v1;
//}
static void __tea_decrypt(unsigned int *v, unsigned int *k)
{
unsigned int v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i; /* set up */
unsigned int delta = 0x9e3779b9; /* a key schedule constant */
unsigned int k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
for (i = 0; i < 32; i++) { /* basic cycle start */
v1 -= (v0 << 4) + (k2 ^ v0) + (sum ^ (v0 >> 5)) + k3;
v0 -= (v1 << 4) + (k0 ^ v1) + (sum ^ (v1 >> 5)) + k1;
sum -= delta; /* end cycle */
}
v[0] = v0; v[1] = v1;
}
/*
static void tea_encrypt(char *buf, int len)
{
int __len = (len + 7) >> 3;
int i;
//char *key = PRIVATE_KEY;
for (i = 0; i < __len; i++) {
__tea_encrypt((((unsigned int *)buf) + (i << 1)), (unsigned int *)PRIVATE_KEY);
}
}
*/
void tea_decrypt(char *buf, int len)
{
int __len = (len + 7) >> 3;
int i;
for (i = 0; i < __len; i++) {
__tea_decrypt((((unsigned int *)buf) + (i << 1)), (unsigned int *)PRIVATE_KEY);
}
}
int file_trylock(int fd)
{
struct flock _lock;
memset( &_lock, 0, sizeof(struct flock) );
_lock.l_type = F_WRLCK;
_lock.l_start = 0;
_lock.l_whence = SEEK_SET;
_lock.l_len = 0;
return fcntl(fd, F_SETLK, &_lock);
}
int file_unlock(int fd)
{
struct flock _lock;
memset( &_lock, 0, sizeof(struct flock) );
_lock.l_type = F_UNLCK;
_lock.l_start = 0;
_lock.l_whence = SEEK_SET;
_lock.l_len = 0;
return fcntl(fd, F_SETLK, &_lock);
}
/* apache version 업에 따른 apr_dbd_get_row 처리 방법 변경 */
int solbox_apr_dbd_get_row(const apr_dbd_driver_t *driver, apr_pool_t *pool,
apr_dbd_results_t *res, apr_dbd_row_t **row, int rownum)
{
DBG1("solbox_apr_dbd_get_row #1 = %s", APU_VERSION_STRING);
/* apu vsrsion 1.3.0 부터 rowunm counting 1부터 시작*/
if(rownum >= 0 && strcmp(APU_VERSION_STRING, "1.3.0") >= 0)
{
return apr_dbd_get_row(driver, pool, res, row, rownum+1);
}
else
{
return apr_dbd_get_row(driver, pool, res, row, rownum);
}
return -1;
}
dav_error* solbox_dav_new_error(apr_pool_t *p, int status,
int error_id, const char *desc)
{
#if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER >= 3
return dav_new_error(p, status, error_id, 0, desc);
#else // 2.2.x
return dav_new_error(p, status, error_id, desc);
#endif // 2.3 over
}
const char *dav_http_scheme(const request_rec *r)
{
/*
* The http module shouldn't return anything other than
* "http" (the default) or "https".
*/
if (r->server->server_scheme &&
(strcmp(r->server->server_scheme, "https") == 0))
return "https";
return "http";
}