// SHCSDK.cpp : DLL 응용 프로그램에 대한 진입점을 정의합니다.
//

#ifdef WIN32

#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <io.h>
#include <math.h>
#include <vector>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <atlstr.h>

typedef __int32             int32_t;
typedef unsigned __int32	uint32_t; 

typedef BOOL (WINAPI *API_GET_FILE_SIZE_EX)(HANDLE hFile,PLARGE_INTEGER lpFileSize);

#else
#include <sys/param.h>	// for HZ
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <ctype.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>

typedef int           BOOL;
typedef void*         LPVOID;
typedef char*         LPSTR;
typedef const char*   LPCSTR;
typedef unsigned int  UINT;
typedef unsigned int  DWORD;
typedef unsigned int* LPDWORD;
typedef unsigned long long UINT64;

typedef int SOCKET;
typedef int __int32;
typedef long long __int64;
typedef struct sockaddr SOCKADDR;

#define closesocket(a) close(a)

#define FALSE 0
#define TRUE  1

#define INVALID_SOCKET  -1
#define SOCKET_ERROR    -1
#define _O_CREAT	O_CREAT
#define _O_TRUNC	O_TRUNC
#define _O_APPEND	O_APPEND
#define _O_RDONLY	O_RDONLY
#define _O_WRONLY	O_WRONLY
#define _O_BINARY	0
#define _O_RANDOM	0

#define _S_IFDIR S_IFDIR

#define _lseeki64 lseek64
#define _stati64 stat64
//tael
#define  lseek64 lseek
#define  stat64 stat
#define  open64 open

#endif

#include <time.h>
#include <string>
#include <vector>
#include <set>

#include "solbox_util.h" // dadamin : Implement a function that does not exist.
#include "SHCSDK.h"
#include "GTS_INFO.h"

#include "ne_request.h"
#include "ne_session.h"
#include "ne_xml.h"
#include "ne_dates.h"

#ifndef off64_t
#define off64_t INT64
#endif

#ifdef WIN32
# pragma comment(lib, "ws2_32.lib")
#endif

#define MAX_TRY 3
#define PRIVATE_KEY			"gE\x8bk\xc6\x23{2i\x98<dsH3f"
#define DEFAULT_SESSIONID	"ANONYMOUS0123456"
#define DEFAULT_EXPIRE_TIME	75

using namespace std;

typedef vector<PSHFILE_STRUCT> VECTOR_SHFILELIST, *PVECTOR_SHFILELIST;

typedef struct SHCSDK_HANDLE {
	ne_session*  pnsess;
	ne_inet_addr** hostlist;
	int			 nhostcnt;
	ne_uri       nuri;
	string       sPath;
	string       sAuth;
	bool         bWorking;
	sh_callback  proc;
	void*        pParam;
	int          nError;
	string       sError;
#ifdef WIN32
	HANDLE		 hFile;
#else
	int          nFileDesc;
#endif
	INT64        iFilePos;
	INT64        nFileSize;
	clock_t      cOldTick;
	int          nTranStop;
	string       sRedirect;
	long		 nSpeedLimit;
	clock_t		 starttick;
	PVECTOR_SHFILELIST      pvecshfl;
	set<PVECTOR_SHFILELIST> setFileList;
	string		 sSessionID;
	int			 nAuthExpire;
} SHCSDK_STRUCT, *PSHCSDK_STRUCT;

enum {
	SHSTOP_NO = 0,
	SHSTOP_BYUSER,
	SHSTOP_BYERROR,
};

typedef struct {
	int                nTag;
	UINT               nRootPathLen;
	PSHFILE_STRUCT     pshf;
	PVECTOR_SHFILELIST pvec;
} SHFILELIST_PARSER_STRUCT, *PSHFILELIST_PARSER_STRUCT;

enum {
	SHFLTAG_UNKNOWN = 0,
	SHFLTAG_PATH,
	SHFLTAG_TYPE,
	SHFLTAG_SIZE,
	SHFLTAG_LASTMODIFIED,
	SHFLTAG_SOURCE,
};

#define URL_SIZE  1024
#define PATH_SIZE 2048

#define TERM_TICKCOUNT (CLOCKS_PER_SEC >> 3)

DWORD	dwTransferd;

static int timeout = 5;

static int _connect_time_out = 5;
static int _read_time_out = 120; // neon library 기본 설정값은 120초
//

#ifdef WIN32
HINSTANCE g_hInst;

#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h> 
#define  SET_CRT_DEBUG_FIELD(a)   _CrtSetDbgFlag((a) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
#define  CLEAR_CRT_DEBUG_FIELD(a) _CrtSetDbgFlag(~(a) & _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
#else
#define  SET_CRT_DEBUG_FIELD(a)   ((void) 0)
#define  CLEAR_CRT_DEBUG_FIELD(a) ((void) 0)
#endif

BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
#ifdef  DEBUG
	SET_CRT_DEBUG_FIELD( _CRTDBG_LEAK_CHECK_DF | _CRTDBG_DELAY_FREE_MEM_DF );
	//_CrtSetBreakAlloc(76);
#endif
	g_hInst = (HINSTANCE)hModule;

	return TRUE;
}

static int __nonblock_win32(SOCKET s)
{
	int ul = 1;
	int result;
	
	result = ioctlsocket( s, FIONBIO, (u_long FAR*)&ul );

	return result;
}
#else
int GetLastError()
{
	return errno;
}

void SetLastError(int nError)
{
	errno = nError;
}

#include <sys/time.h>
clock_t GetTickCount(void)
{
	struct timeval tv;

	if (gettimeofday(&tv, NULL) < 0) return 0;
	else {
		/* It's micro second. 1/1000000 sec.
		 * window api's GetTickCount is mili second. 1/1000 sec.
		 * conv it!
		 */
		return (clock_t)((clock_t)(tv.tv_sec * 1000) + tv.tv_usec / 1000);
	}
}

static int __nonblock(int fd)
{
	int result;
	int flags;

	flags = fcntl(fd, F_GETFL, 0);
	result = fcntl(fd, F_SETFL, flags | O_NONBLOCK);

	return result;
}
#endif

inline int WaitForBandCtl(double dWrite, long double ldDesiredBand)
{
	struct timeval tv;
	int iWaitTimeInt = 0;
	double dWaitTime = 0.00;
	unsigned long ulWaitTimeDec = 0;
	fd_set fakeFD;

	FD_ZERO(&fakeFD);
	if((dWrite <= 0) || (ldDesiredBand <= 0)) {
		return -1;
	}

	memset((char *)&tv, 0, sizeof(tv));
	dWaitTime = (double)( 1 / (double)(ldDesiredBand/dWrite));
	iWaitTimeInt = (int)(dWaitTime);
	ulWaitTimeDec = (long)((dWaitTime - iWaitTimeInt) * 1000);

	tv.tv_sec = iWaitTimeInt;
	tv.tv_usec = ulWaitTimeDec;
#ifdef WIN32
	Sleep((DWORD)(dWaitTime*1000));
#else
	usleep((DWORD)(dWaitTime*1000));
#endif
	return 1;
}

static void ReplaceAll(string& sValue, LPCSTR pszOld, LPCSTR pszNew)
{
#ifndef WIN32
	if (strlen(pszOld) != strlen(pszNew)) {
#endif
		string sOld = pszOld;
		string sNew = pszNew;

		size_t s;
		while ((s = sValue.find(sOld)) != string::npos)
			sValue.replace(s, sOld.length(), sNew);
#ifndef WIN32
	} else {
		char *pszValue, *p, *r;

		pszValue = (char *)malloc(sValue.length() + 1);
		strcpy(pszValue, sValue.c_str());

		r = pszValue;
		while ((p = strstr(r, pszOld))) {
			memcpy(p, pszNew, strlen(pszNew));
			r = p;
		}

		sValue.clear();
		sValue = pszValue;
		free(pszValue);
	}
#endif
}

// 세션 상태를 콜백으로 받는 함수
static void status_cb(void *userdata, ne_session_status status,
	const ne_session_status_info *info)
{
	switch (status) {
	case ne_status_connected:
		{
			ne_socket *sock = get_session_socket((ne_session *)userdata);
			// 타임 아웃 값 지정함(30초)
			// 마지막 데이터 전송 완료(실제 전송 완료되지 않고 커널 응답) 후 
			// close 호출 된 경우  해당 옵션 미지정된 경우 네트워크 상 전송할 데이터 남아
			// 있더라도 해당 데이터 모두 삭제됨
			struct linger l = {1, 30};
#ifdef WIN32
			setsockopt(ne_sock_fd(sock), SOL_SOCKET, SO_LINGER,(char*) &l, sizeof l);
#else // WIN32
			setsockopt(ne_sock_fd(sock), SOL_SOCKET, SO_LINGER, &l, sizeof l);
#endif // WIN32
		}
		break;
	}
}

// 세션 생성 및 추가 설정
static bool solbox_session_create(PSHCSDK_STRUCT psdk)
{
	psdk->pnsess = ne_session_create(psdk->nuri.scheme, psdk->nuri.host, psdk->nuri.port);

	// 세션의 상태 notifier 함수 등록
	ne_set_notifier(psdk->pnsess, status_cb, psdk->pnsess);

	return true;
}

//세션의 파괴 및 추가 작업
static void solbox_session_destroy(PSHCSDK_STRUCT psdk)
{
	ne_session_destroy(psdk->pnsess);
}


// dns cache 기능을 사용하기 위한 함수이다.
// 
//static bool solbox_session_create(PSHCSDK_STRUCT psdk)
//{
//	const ne_inet_addr** result;
//	int nResultCnt;
//
//	result = solbox_resolve(psdk->nuri.host, &nResultCnt);
//	ne_free(result);
//	result = solbox_resolve("bd-04-fhs001.ktsh.co.kr", &nResultCnt);
//	ne_free(result);
//	result = solbox_resolve("bd-04-fhs001.ktsh.co.kr", &nResultCnt);
//	ne_free(result);
//	result = solbox_resolve("bd-04-fhs001.ktsh.co.kr", &nResultCnt);
//	ne_free(result);
//	result = solbox_resolve(psdk->nuri.host, &nResultCnt);
//	if( result == NULL )
//	{
//		// host 에러입니다.
//		return false;
//	}
//
//	psdk->pnsess = ne_session_create(psdk->nuri.scheme, psdk->nuri.host, psdk->nuri.port);
//
//	ne_set_addrlist(psdk->pnsess, result, nResultCnt);
//	ne_free(result);
//	return true;
//}

static string GetErrorMessage(int nError)
{
	string sError;

	if (nError >= SHCERRNO_UNDEFINED && nError < SHCERRNO_MAX) {
		switch (nError) {
		case SHCERRNO_UNDEFINED:
			sError = "알 수 없는 오류입니다.";
			break;
		case SHCERRNO_INVALIDHDL:
			sError = "잘못된 SDK 핸들입니다.";
			break;
		case SHCERRNO_INVALIDFLHDL:
			sError = "잘못된 파일리스트 핸들입니다.";
			break;
		case SHCERRNO_SERVICE:
			sError = "서버 응답이 정상적이지 않습니다..";
			break;
		case SHCERRNO_AUTHORIZATION:
			sError = "인증에 실패 했습니다.";
			break;
		case SHCERRNO_ISBUSY:
			sError = "다른 작업을 처리하고 있는 중 입니다.";
			break;
		case SHCERRNO_EXISTSFILE:
			sError = "지정한 이름을 가진 파일 또는 디렉토리가 이미 존재합니다.";
			break;
		case SHCERRNO_NOEXISTSFILE:
			sError = "파일 또는 디렉토리가 존재하지 않습니다.";
			break;
		case SHCERRNO_NOAVAILQUOTA:
			sError = "사용 가능한 용량이 부족합니다.";
			break;
		case SHCERRNO_ALREADYEXIST:
			sError = "폴더가 이미 존재 합니다.";
			break;
		case SHCERRNO_RESP_TIMEOUT:
			sError = "소켓 읽기 실패입니다.";
			break;
		case SHCERRNO_NE_LOOKUP:
			sError = "호스트 정보에대해 룩업에 실패했습니다.";
			break;
		case SHCERRNO_NE_CONNECT:
			sError = "서버에 연결을 하지 못했습니다.";
			break;
		case SHCERRNO_TRAGETBIGGER:
			sError = "대상 데이터의 크기가 전송할 데이터 크기와 동일하거나 큽니다.";
			break;
		case SHCERRNO_DNS_TIMEOUT:
			sError = "DNS 쿼리 시간초과입니다.";
			break;
		case SHCERRNO_EXT_OVERFLOW:
			sError = "download 시 확장 값이 512Bytes를 초과하였습니다.";
			break;

		}
	}
	else {
#ifdef WIN32
		LPVOID lpError;

		if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL, nError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpError, 0, NULL))
			sError = "알 수 없는 오류입니다.";
		else
			sError = (LPCSTR)lpError;

		LocalFree(lpError);
#else
		sError = strerror(nError);
#endif
	}

	return sError;
}

int StrTrim(char* dst, const char* src, int len)
{
	register int  dlen, i;
	register char *sp;
	char *buff;
    
	if(!len) return 0;

	buff = (char *)calloc(1, len + 1);
    strncpy(buff, src, len);
    
	/* R-Trim */
    sp = buff + len -1; dlen = len -1;
	while(1){
            if(dlen <= 0) break;
            else if(*sp != ' ' && *sp != 0x00) break;
            else *sp = 0x00;
            sp --; dlen --;
    }
    dlen = strlen(buff); sp = buff; i = 0;

	/* L-Trim */
	while(i < dlen){ if(*sp != ' ' && *sp != 0x00) break; sp ++; i++; }
    dlen = strlen(sp);
    strcpy(dst, sp);
    free(buff);
    return dlen;
}
static INT64 sh_atoi(LPCSTR pszSize)
{
#ifdef WIN32
	return _atoi64(pszSize);
#else
	return strtoll(pszSize, NULL, 10);
#endif
}

static BOOL DeleteDirectory(LPCSTR pszDirectory)
{
#ifdef WIN32
	string sDirectory = pszDirectory;
	sDirectory += "\\*";

	WIN32_FIND_DATA w32fd;

	HANDLE hFindFile = FindFirstFile(sDirectory.c_str(), &w32fd);
	while (hFindFile != INVALID_HANDLE_VALUE) {
		if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
			if (w32fd.cFileName[0] != '.') {
				if (!DeleteDirectory(w32fd.cFileName))
					return false;
			}
		}
		else {
			if (!DeleteFile(w32fd.cFileName))
				return false;
		}

		if (!FindNextFile(hFindFile, &w32fd))
			break;
	}

	FindClose(hFindFile);

	return RemoveDirectory(pszDirectory);
#else
#define PATH_MAX	4096
	char buf[PATH_MAX + 5];
	int ret;

	snprintf(buf, PATH_MAX, "/bin/rm -rf %s", pszDirectory);
	ret = system(buf);

	return (ret == 0);
#endif
}

static int _sh_send(SOCKET s, char *pszBuf, int nLen)
{
	int iSent = 0, nSentLen;
	fd_set wset;
	struct timeval tv;

	FD_ZERO(&wset);
	FD_SET(s, &wset);
	tv.tv_sec = timeout;
	tv.tv_usec = 0;
	do {
		if (select(s + 1, NULL, &wset, NULL, &tv) < 0) break;
		if (!FD_ISSET(s, &wset)) break;

		nSentLen = send(s, pszBuf + iSent, nLen, 0);

		iSent += nSentLen;
		nLen  -= nSentLen;
	} while (nLen > 0 && nSentLen > 0);

	return nLen > 0 ? SOCKET_ERROR : iSent;
}

// format : Thu, 15 Dec 2005 05:22:33 GMT
static string FormatLastModified(LPCSTR pszTime)
{
	string sTime; 

	// RFC1123_FORMAT to time_t format & GMT+9(32400 sec)
	// "Thu, 15 Dec 2005 05:22:33 GMT" 이런 형태를 RFC1122_FORMAT 이라한다.
	// RFC1122_FORMAT 포맷을 인자로 넘겨서 time_t를 얻을 수 있었고, 
	// 얻은 값에다가 9시간에 해당하는 sec를 더해서 GMT+9를 구한다.
	time_t t = ne_rfc1123_parse(pszTime) + 32400;

	// time_t to struct tm
	// 위 과정에서 이미 GMT+9가 처리되었으므로,
	// 기존 소스처럼 복잡한 과정을 거치지 않아도 된다. 
	// 단지 time_t 값을 속성을 알고 year에 1900을 mon1을 더해준다.
	struct tm stm;
	stm = *gmtime(&t);
	stm.tm_year = stm.tm_year + 1900;
	stm.tm_mon = stm.tm_mon + 1;

	// 최종적으로 표현하고자 하는 날짜 포맷으로 변경처리한다.
	char szTemp[20];
	sprintf(szTemp, "%04d-%02d-%02d ", stm.tm_year, stm.tm_mon, stm.tm_mday);
	sTime = szTemp;
	sprintf(szTemp, " %02d:%02d:%02d", stm.tm_hour, stm.tm_min, stm.tm_sec);
	sTime += szTemp;

	return sTime;
}

// format : Fri Jan 27 12:12:22 2006 Thu
static bool ParseSntpString(LPCSTR pszTime, struct tm* ptm)
{
	string sTime = pszTime;
	sTime.erase(sTime.find_last_not_of(" \t\n") + 1);
	sTime.erase(0, sTime.find_first_not_of(" \t\n"));

	if (sTime == "")
		return false;

	int iString = 0;
	string::size_type stBegin = 0, stEnd = 0;
	do {
		stEnd = sTime.find(' ', stBegin);

		string sToken = sTime.substr(stBegin, stEnd - stBegin);

		switch (iString) {
		case 1:
			if (sToken == "Jan")		ptm->tm_mon = 1;
			else if (sToken == "Feb")	ptm->tm_mon = 2;
			else if (sToken == "Mar")	ptm->tm_mon = 3;
			else if (sToken == "Apr")	ptm->tm_mon = 4;
			else if (sToken == "May")	ptm->tm_mon = 5;
			else if (sToken == "Jun")	ptm->tm_mon = 6;
			else if (sToken == "Jul")	ptm->tm_mon = 7;
			else if (sToken == "Aug")	ptm->tm_mon = 8;
			else if (sToken == "Sep")	ptm->tm_mon = 9;
			else if (sToken == "Oct")	ptm->tm_mon = 10;
			else if (sToken == "Nov")	ptm->tm_mon = 11;
			else if (sToken == "Dec")	ptm->tm_mon = 12;
			break;
		case 2:
			ptm->tm_mday = atoi(sToken.c_str());
			break;
		case 3:
			ptm->tm_hour = atoi(sToken.substr(0, 2).c_str()) + 9;
			ptm->tm_min  = atoi(sToken.substr(3, 5).c_str());
			ptm->tm_sec  = atoi(sToken.substr(6, 8).c_str());
			break;
		case 4:
			ptm->tm_year = atoi(sToken.c_str());
			break;
		}

		if (iString++ >= 4)
			break;

		stBegin = stEnd + 1;
	} while (stEnd != string::npos);

	return true;
}

static INT64 sh_filelength(int nFd)
{
#ifdef WIN32
	return _filelengthi64(nFd);
#else
	INT64 nLength = lseek64(nFd, 0, SEEK_END);
	lseek64(nFd, 0, SEEK_SET);

	return nLength;
#endif
}

static BOOL	check_suthstring(const char* pszAuthstring)
{
	char* pAuth;
	char* pData;
	char* pSrc;
	char* context = NULL;
	char szDst[128]={0};
	BOOL bResult;

	pSrc = new char[strlen(pszAuthstring)+1];

	memcpy(pSrc,pszAuthstring,strlen(pszAuthstring)+1);

	
#ifdef WIN32 
	pAuth = strtok_s((char*)pSrc,":", &context);
#else //  WIN32 
	pAuth = strtok_r((char*)pSrc,":",&context);
#endif // WIN32 


	if(pAuth == NULL)
		bResult = FALSE;
	else
	{
#ifdef WIN32 
		pData = strtok_s(NULL,":", &context);
#else //  WIN32 
		pAuth = strtok_r(NULL,":",&context);
#endif // WIN32 
		if(pData == NULL)
			bResult = FALSE;
		else
		{
			md5_buffer(pAuth,szDst);

#ifdef WIN32
			if(stricmp(pData,szDst) == 0)
#else
			if(strcasecmp(pData,szDst) == 0)
#endif
			{
				strcpy((char*)pszAuthstring,pAuth);
				bResult = TRUE;
			}
			else
				bResult = FALSE;
		}
	}

	delete []pSrc;

	return bResult;
}

static int _sh_recv(SOCKET s, char *pszBuf, int nLen)
{
	int iRecv = 0, nRecvLen;
	fd_set rset;
	struct timeval tv;

	FD_ZERO(&rset);
	FD_SET(s, &rset);
	tv.tv_sec = timeout;
	tv.tv_usec = 0;

	do {
		if (select(s + 1, &rset, NULL, NULL, &tv) < 0) break;
		if (!FD_ISSET(s, &rset)) break;

		nRecvLen = recv(s, pszBuf + iRecv, nLen, 0);

		iRecv += nRecvLen;
		nLen  -= nRecvLen;
	} while (nLen > 0 && nRecvLen > 0);

	return nLen < 0 ? SOCKET_ERROR : iRecv;
}

typedef struct _s_time_sync_t
{
	int32_t sync_info;
}time_sync_t;

#define AUTHPRIME		137897

static time_t get_time_from_sntp()
{
	char szHost[128];
	int  nRecv;
	time_sync_t	tst;
	int nWrite;
	time_sync_t *pSyncInfo = NULL;
#ifdef WIN32
	tm *ttm=NULL;
#endif

	int32_t ltime = (int32_t)time(NULL);

	if (ne_sock_init() != NE_OK)
	{
#ifdef _DEBUG
		printf("time sever connect error : sock init fail\n");
#endif
		return time(NULL);
	}

	// Get server time
	struct addrinfo *result;
	strcpy(szHost, SNTP_DOMAIN);
	if (isalpha(szHost[0]))
	{
		struct addrinfo hints = {0};
		
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_family = PF_UNSPEC;

		if( getaddrinfo( szHost, NULL, &hints, &result ) != 0 )
		{
#ifdef _DEBUG
			printf("time sever connect error : getaddrinfo fail\n");
#endif
			return time(NULL);
		}

		struct sockaddr_in * addr = (struct sockaddr_in *)result->ai_addr;
		sprintf(szHost, "%s", inet_ntoa(addr->sin_addr)); 
		freeaddrinfo(result);
	}

#ifndef WIN32
#define RETRY 2
	int recv_err = 0;

recv_retry:
#endif

	SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sock == INVALID_SOCKET)
	{
#ifdef WIN32
		int nRet = WSAGetLastError();
#endif
#ifdef _DEBUG
		printf("time sever connect error : socket create fail\n");
#endif

		return time(NULL);
	}

	sockaddr_in sa;
	sa.sin_family      = AF_INET;	
	sa.sin_addr.s_addr = inet_addr(szHost);
	sa.sin_port        = htons(SNTP_PORT);

#ifdef WIN32
	__nonblock_win32(sock);

	if (connect(sock, (SOCKADDR*)&sa, sizeof(sa)) == SOCKET_ERROR)
	{

		if (WSAEWOULDBLOCK == WSAGetLastError())
		{
			fd_set wfd, efd;
			struct timeval tv;
			FD_ZERO(&wfd);
			FD_ZERO(&efd);
			FD_SET(sock, &wfd);
			FD_SET(sock, &efd);
			tv.tv_sec = timeout;
			tv.tv_usec = 0;
			int result = select(0, 0, &wfd, &efd,&tv);
			if (SOCKET_ERROR == result || 0 == result)
			{
				int nRet = WSAGetLastError();
#ifdef _DEBUG
				printf("time sever connect error : select fail\n");
#endif
				return time(NULL);
			}
		}
		else
		{
#ifdef _DEBUG
			printf("time sever connect error : unknown error code\n");
#endif
			return time(NULL);
		}
	}

#else // WIN32
	__nonblock(sock);
	{
		int i, nRet;
		for (i = 0; i < (timeout * 10); i++) {
			errno = 0;
			if (connect(sock, (SOCKADDR*)&sa, sizeof(sa)) != SOCKET_ERROR) {
				;
			} else {
				nRet = errno;
			}

			if (errno == EISCONN) {
				break;
			}

			if (((i + 1) % 10) == 0) {
				shutdown(sock, SHUT_RDWR);
				close(sock);
				sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
				if (sock == INVALID_SOCKET)
				{
					return time(NULL);
				}
				__nonblock(sock);
				/* Elenoa */
			}
			usleep(100000);
		}

		if (errno != EISCONN && errno != 0)
		{
			return time(NULL);
		}
	}
#endif // WIN32

	tst.sync_info = ltime + AUTHPRIME - (ltime % AUTHPRIME);

	tst.sync_info = htonl(tst.sync_info);
	nWrite = _sh_send(sock, (char *)&tst, sizeof(tst));

	if(nWrite < 0)
	{
		closesocket(sock);
#ifndef WIN32
		if (recv_err <= RETRY) {
			recv_err ++;
			goto recv_retry;
		}
#endif
#ifdef _DEBUG
		printf("time sever connect error : send fail\n");
#endif

		return time(NULL);
	}

	nRecv = _sh_recv(sock, szHost, sizeof(szHost));

	if (nRecv < 4)
	{
		closesocket(sock);
#ifndef WIN32
		if (recv_err <= RETRY) {
			recv_err ++;
			goto recv_retry;
		}
#endif
#ifdef _DEBUG
		printf("time sever connect error : recv fail\n");
#endif

		return time(NULL);
	}

	pSyncInfo = (time_sync_t *)&szHost;
	pSyncInfo->sync_info = ntohl(pSyncInfo->sync_info);
	closesocket(sock);

	return pSyncInfo->sync_info;
}

static string URLEncode(LPCSTR pszURL)
{
	char  szEncoded[URL_SIZE];
	char* pszEncoded = szEncoded;

	while (*pszURL != (char)NULL) {
		if ((*pszURL >= 'a' && *pszURL <= 'z') || (*pszURL >= 'A' && *pszURL <= 'Z')
			|| (*pszURL >= '0' && *pszURL <= '9') || *pszURL == '/')
			*pszEncoded = (char)*pszURL;
		else {
			*pszEncoded++ = '%';

			char szBuf[10];
			sprintf(szBuf, "%02X", *pszURL);
			int nBufLen = (int)strlen(szBuf);
			*pszEncoded++ = szBuf[nBufLen - 2];
			*pszEncoded   = szBuf[nBufLen - 1];
		}

		pszEncoded++;
		pszURL++;
	}
	*pszEncoded = (char)NULL;

	string sRet = szEncoded;

	return sRet;
}

static string URLDecode(LPCSTR pszURL)
{
	char szOut[URL_SIZE], szHex[3];

	::memset(szOut, 0, sizeof(szOut));

	for (int i = 0, j = 0; pszURL[i]; i++, j++) {
		if (pszURL[i] == '%') {
			szHex[0] = pszURL[++i];
			szHex[1] = pszURL[++i];
			szHex[2] = 0;
#ifdef WIN32
			sscanf((char*)szHex, "%x", &szOut[j]);
#else
			szOut[j] = (char)strtol(szHex, NULL, 16);
#endif
		} else {
			szOut[j] = pszURL[i];
		}
	}

	string sRet = szOut;

	return sRet;
}

static string PathEncode(LPCSTR pszPath)
{
	if (pszPath[0] == '/')
		pszPath += 1;

	string sRetPath = "";
	string sSrcPath = pszPath;

	string::size_type stBegin = 0, stEnd = 0;
	do {
		stEnd = sSrcPath.find('/', stBegin);

		string sPath = sSrcPath.substr(stBegin, stEnd - stBegin);
		sRetPath += "/" + URLEncode(sPath.c_str());

		stBegin = stEnd + 1;
	} while (stEnd != string::npos);

	return sRetPath;
}

void __tea_encrypt(uint32_t *v, uint32_t *k)
{
	uint32_t v0 = v[0], v1 = v[1], sum = 0, i;   /* set up */
	uint32_t delta = 0x9e3779b9;              /* a key schedule constant */
	uint32_t 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;
}


void __tea_decrypt(uint32_t *v, uint32_t *k)
{
	uint32_t v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i;  /* set up */
	uint32_t delta = 0x9e3779b9;              /* a key schedule constant */
	uint32_t 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;
}


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((((uint32_t *)buf) + (i << 1)), (uint32_t *)PRIVATE_KEY);
	}
}


void tea_decrypt(char *buf, int len)
{
	int __len = (len + 7) >> 3;
	int i;

	for (i = 0; i < __len; i++) {
		__tea_decrypt((((uint32_t *)buf) + (i << 1)), (uint32_t *)PRIVATE_KEY);
	}
}

static int ResponseBodyReader(void* userdata, const char* buf, size_t len)
{
	if (len == 0)
		return 0;

	string* psBody = (string*)userdata;

	char* pszTemp = new char[len + 1];
	memcpy(pszTemp, buf, len);
	pszTemp[len] = (char)NULL;

	(*psBody) += pszTemp;

	delete []pszTemp;

	return NE_OK;
}

char* get_auth_str(PSHCSDK_STRUCT psdk, LPCSTR pszPath, int nFlag)
{
	unsigned char pszAuth[4096];
	unsigned char *bintoken = NULL;
	char szTemp[16];
	//	string sAuth;
	int nRet;

	ne_unbase64(psdk->sAuth.c_str(),&bintoken);

	if (bintoken == NULL)
		return NULL;
	
	char* context	= NULL;
	char* pData =  NULL;
#ifdef WIN32 
	pData = strtok_s((char*)bintoken,":", &context);
#else //  WIN32 
	pData = strtok_r((char*)bintoken,":", &context);
#endif // WIN32 

	if(pData == NULL)
	{
		if(bintoken) ne_free(bintoken);
		return NULL;
	}

	int nAuthSize = 4+16+16+psdk->sAuth.size()+8;


	int32_t ltime = 0;

	switch(nFlag)
	{
	case 0: // 서버에서 시간 값을 가져온다.
		ltime = get_time_from_sntp();
		break;
	case 1:
		ltime = time(NULL);
		break;
	}

	int32_t kkk = ltime + psdk->nAuthExpire;

	memcpy(pszAuth,(unsigned char *)&kkk,sizeof(kkk));

	md5_buffer_bin(psdk->sSessionID.c_str(),szTemp);
	memcpy(pszAuth+4,szTemp,sizeof(szTemp));

	md5_buffer_bin(pszPath,szTemp);
	memcpy(pszAuth+4+16,szTemp,sizeof(szTemp));

	memcpy(pszAuth+4+16+16,psdk->sAuth.c_str(),psdk->sAuth.size());

	int nlength = 4 + 16 + 16 + psdk->sAuth.size();

	nRet = nlength % 8;

	if (nRet > 0)
		memset((char*)pszAuth + 4 + 16 + 16 + psdk->sAuth.size(),0x20,8-nRet);
	else
		nRet = 8;

	tea_encrypt((char*)pszAuth,nlength + 8 - nRet);
	char* pszTemp = new char[nlength + 8 -nRet + strlen(pData) + 1];

	memcpy(pszTemp,pData,strlen(pData));
	memcpy(pszTemp+strlen(pData),":",1);
	memcpy(pszTemp+strlen(pData)+1,pszAuth,nlength + 8 -nRet);

	char* pResult = ne_base64((const unsigned char*)pszTemp,nlength + 8 -nRet + strlen(pData) + 1);
	string sAuth;
	sAuth = "Basic ";
	sAuth += pResult;

	if(bintoken) ne_free(bintoken);
	if(pResult) ne_free(pResult);
	if(pszTemp) delete [] pszTemp;

	return strdup(sAuth.c_str());
}

static ssize_t UploadProvider(void *userdata, char *buffer, size_t buflen)
{
	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)userdata;

	if (buflen == 0)
		return 0;

	if (clock() > psdk->cOldTick + TERM_TICKCOUNT || psdk->iFilePos >= psdk->nFileSize)
	{
		if (psdk->proc)
		{
			if (!psdk->proc(psdk->pParam, psdk->iFilePos)) {
				psdk->nTranStop = SHSTOP_BYUSER;
				return -1;
			}
		}	

		if(psdk->nSpeedLimit > 0)
		{
			double t = (double)(GetTickCount() - psdk->starttick);
			double q = (double)((double)dwTransferd/ t*1000);

			DWORD m_dbLimit = psdk->nSpeedLimit * 1024;

			if (q > m_dbLimit)
			{
				DWORD dwret = (DWORD)((((q*t)/m_dbLimit)-t));
#ifdef WIN32
				Sleep(dwret);
#else
				usleep(dwret);
#endif
			}
		}

		psdk->cOldTick = clock();
	}

#ifdef WIN32
	DWORD nRead;
	ReadFile(psdk->hFile,buffer,(DWORD)buflen,&nRead,NULL);
#else
	int nRead = read(psdk->nFileDesc, buffer, (UINT)buflen);
#endif
	if (nRead < 0) {
		psdk->nTranStop = SHSTOP_BYERROR;

		psdk->nError = GetLastError();
		psdk->sError = GetErrorMessage(psdk->nError);
		return -1;
	}

	dwTransferd += nRead;
	psdk->iFilePos += nRead;

	return nRead;
}

static int DownloadProvider(void* userdata, const char* buf, size_t len)
{
	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)userdata;

	if (len == 0)
		return 0;

	DWORD nWritten, nTotal = 0;
	do {
#ifdef WIN32
		WriteFile(psdk->hFile,buf,(DWORD)len,&nWritten,NULL);			
#else
		nWritten = write(psdk->nFileDesc, buf + nTotal, (UINT)len - nTotal);
#endif
		if (nWritten < 0) {
			psdk->nTranStop = SHSTOP_BYERROR;

			psdk->nError = GetLastError();
			psdk->sError = GetErrorMessage(psdk->nError);
			return 1;
		}

		nTotal += nWritten;
	} while (nTotal < (DWORD)len);

	dwTransferd += nTotal;
	psdk->iFilePos += nTotal;

	if (clock() > psdk->cOldTick + TERM_TICKCOUNT || psdk->iFilePos >= psdk->nFileSize)
	{
		if (psdk->proc)
		{
			if (!psdk->proc(psdk->pParam, psdk->iFilePos))
			{
				psdk->nTranStop = SHSTOP_BYUSER;
				return 1;
			}
		}

		if(psdk->nSpeedLimit > 0)
		{
			double t = (double)(GetTickCount() - psdk->starttick);
			double q = (double)((double)dwTransferd/ t*1000);
			DWORD m_dbLimit = psdk->nSpeedLimit * 1024;

			if (q > m_dbLimit)
			{
				DWORD dwret = (DWORD)((((q*t)/m_dbLimit)-t));
#ifdef WIN32
				Sleep(dwret);
#else
				usleep(dwret);
#endif
			}
		}

		psdk->cOldTick = clock();
	}

	return NE_OK;
}

static int FileListSTagParser(void *userdata, int parent, const char *nspace, const char *name, const char **atts)
{
	PSHFILELIST_PARSER_STRUCT pshflp = (PSHFILELIST_PARSER_STRUCT)userdata;

	if (strcmp(name, "response") == 0) {
		pshflp->pshf = new SHFILE_STRUCT;
		::memset(pshflp->pshf, 0, sizeof(SHFILE_STRUCT));
	}
	else if (strcmp(name, "href") == 0) {
		pshflp->nTag = SHFLTAG_PATH;
	}
	else if (strcmp(name, "collection") == 0) {
		pshflp->pshf->attr = SHFILEATTR_FOLDER;
	}
	else if (strcmp(name, "getcontentlength") == 0) {
		pshflp->nTag = SHFLTAG_SIZE;
	}
	else if (strcmp(name, "getlastmodified") == 0) {
		pshflp->nTag = SHFLTAG_LASTMODIFIED;
	}
	else if (strcmp(name, "source") == 0) {
		pshflp->nTag = SHFLTAG_SOURCE;
	}
	else {
		pshflp->nTag = SHFLTAG_UNKNOWN;
	}

	return 1;
}

static string PathDecode(LPCSTR pszPath)
{
	if (pszPath[0] == '/')
		pszPath += 1;

	string sRetPath = "";
	string sSrcPath = pszPath;

	string::size_type stBegin = 0, stEnd = 0;
	do {
		stEnd = sSrcPath.find('/', stBegin);

		string sPath = URLDecode(sSrcPath.substr(stBegin, stEnd - stBegin).c_str());
		sRetPath += "/" + sPath;

		stBegin = stEnd + 1;
	} while (stEnd != string::npos);

	ReplaceAll(sRetPath, "*amp;", "&");

	return sRetPath;
}

static int FileListDataParser(void *userdata, int state, const char *data, size_t len)
{
	PSHFILELIST_PARSER_STRUCT pshflp = (PSHFILELIST_PARSER_STRUCT)userdata;


	switch (pshflp->nTag) {
	case SHFLTAG_PATH:
		if (!pshflp->pshf->path) {
			if (len >= pshflp->nRootPathLen) {
				int  nPathLen = (int)len - pshflp->nRootPathLen;
				char szPath[PATH_SIZE];
				memcpy(szPath, data + pshflp->nRootPathLen, nPathLen);
				szPath[nPathLen - (nPathLen > 1 && szPath[nPathLen - 1] == '/' ? 1 : 0)] = (char)NULL;

				string sPath = PathDecode(szPath).c_str();
				pshflp->pshf->path = new char[sPath.length() + 1];
				strcpy(pshflp->pshf->path, sPath.c_str());
			}
			else {
				pshflp->pshf->path = new char[PATH_SIZE];
				strcpy(pshflp->pshf->path, "*myself*");
			}
		}
		break;
	case SHFLTAG_TYPE:
		break;
	case SHFLTAG_SIZE:
		if (isdigit(data[0])) {
			char szSize[20];
			memcpy(szSize, data, len);
			szSize[len] = (char)NULL;

			pshflp->pshf->size = sh_atoi(szSize);
		}
		break;
	case SHFLTAG_LASTMODIFIED:
		if (!pshflp->pshf->lastModified) {
			char szTime[30];
			memcpy(szTime, data, len);
			szTime[len] = (char)NULL;

			string sTime = FormatLastModified(szTime);
			pshflp->pshf->lastModified = new char[sTime.length() + 1];
			pshflp->pshf->lastModified_gmt = new char[sizeof(szTime)];
			strcpy(pshflp->pshf->lastModified_gmt,szTime);
			strcpy(pshflp->pshf->lastModified, sTime.c_str());
		}
		break;
	case SHFLTAG_SOURCE:
		if (!pshflp->pshf->source) {
			char szSource[PATH_SIZE];
			memcpy(szSource, data, len);
			szSource[len] = (char)NULL;

			pshflp->pshf->source = new char[strlen(szSource) + 1];
			strcpy(pshflp->pshf->source, szSource);
		}
		break;
	default:
		break;
	}

	return 0;
}

static int FileListETagParser(void *userdata, int state, const char *nspace, const char *name)
{
	PSHFILELIST_PARSER_STRUCT pshflp = (PSHFILELIST_PARSER_STRUCT)userdata;

	if (strcmp(name, "response") == 0) {
		if (pshflp->pshf) {
			pshflp->pvec->push_back(pshflp->pshf);
		}

		pshflp->pshf = NULL;
	}

	return 0;
}


static void FreeFileListVector(PVECTOR_SHFILELIST pvec)
{
	for (VECTOR_SHFILELIST::iterator iter = pvec->begin(); iter != pvec->end(); iter++) {
		PSHFILE_STRUCT pshf = (*iter);
		delete []pshf->path;
		delete []pshf->lastModified;
		delete []pshf->lastModified_gmt;
		delete []pshf->source;
		delete pshf;
	}
	
	pvec->clear();
	delete pvec;
}

static bool DoCommand(PSHCSDK_STRUCT psdk, LPCSTR pszMethod, LPCSTR pszPath, LPCSTR pszHeaders, LPCSTR pszBody, 
					  bool bParseBody, LPVOID pSTagParser, LPVOID pDataParser, LPVOID pETagParser, LPVOID pParam,int nFrom)
{
	bool   bRet = false;
	int    nRet;
	string sBody;
	ne_status* pnstat;
	string sAuth;

#ifdef _NEWAUTH
	char* pszAuth = NULL;
#endif

	ne_request *pnreq = ne_request_create(psdk->pnsess, pszMethod, PathEncode(pszPath).c_str());
	if (!pnreq) {
		psdk->nError = SHCERRNO_UNDEFINED;
		psdk->sError = ne_get_error(psdk->pnsess);
		goto EXIT_DOCOMMAND;
	}

#ifdef _NEWAUTH
	pszAuth = get_auth_str(psdk,pszPath, nFrom);

	ne_add_request_header(pnreq, "Authorization",pszAuth);
#else // _SP_SDK_
	ne_add_request_header(pnreq, "Authorization", psdk->sAuth.c_str());
#endif

	ne_add_request_header(pnreq, "Cache-Control", "no-cache");

	if (pszHeaders && strlen(pszHeaders) > 0) {
		string sHeaders = pszHeaders;

		string::size_type stBegin = 0, stEnd = 0;
		do {
			stEnd = sHeaders.find(',', stBegin);

			string sHeader = sHeaders.substr(stBegin, stEnd - stBegin);

			string::size_type stDivider = sHeader.find('|');
			ne_add_request_header(pnreq, sHeader.substr(0, stDivider).c_str(), sHeader.substr(stDivider + 1, sHeader.length()).c_str());

			stBegin = stEnd + 1;
		} while (stEnd != string::npos);
	}

	if (pszBody && strlen(pszBody) > 0)
		ne_set_request_body_buffer(pnreq, pszBody, strlen(pszBody));

	if (bParseBody)
		ne_add_response_body_reader(pnreq, ne_accept_2xx, ResponseBodyReader, &sBody);

	nRet = ne_request_dispatch(pnreq);

	if (nRet != NE_OK) {
		psdk->nError = nRet;
		psdk->sError = ne_get_error(psdk->pnsess);
		goto EXIT_DOCOMMAND;
	}

	pnstat = (ne_status*)ne_get_status(pnreq);
	if (pnstat->code != 0 && (pnstat->code < 200 || pnstat->code >= 300)) {
		psdk->nError = pnstat->code;
		psdk->sError = ne_get_error(psdk->pnsess);
		goto EXIT_DOCOMMAND;
	}

	if (bParseBody) {
		ReplaceAll(sBody, "&amp;", "*amp;");

		ne_xml_parser* pnxparser = ne_xml_create();
		ne_xml_push_handler(pnxparser, (ne_xml_startelm_cb*)pSTagParser, (ne_xml_cdata_cb*)pDataParser, (ne_xml_endelm_cb*)pETagParser, pParam);
		ne_xml_parse(pnxparser, sBody.c_str(), sBody.length());
		ne_xml_destroy(pnxparser);
	}

	bRet = true;

EXIT_DOCOMMAND:
	switch (psdk->nError) {
	case 400:
		psdk->nError = SHCERRNO_SERVICE;
		break;
	case 401:
		psdk->nError = SHCERRNO_AUTHORIZATION;
		break;
	case 403:
		psdk->nError = SHCERRNO_SERVICE;
		break;
	case 404:
		psdk->sRedirect = ne_get_response_header(pnreq, "Redirect") != NULL ? ne_get_response_header(pnreq, "Redirect") : "";
		psdk->nError = SHCERRNO_NOEXISTSFILE;
		break;
	case 500:
		psdk->nError = SHCERRNO_SERVICE;
		break;
	case NE_TIMEOUT:
		psdk->nError = SHCERRNO_RESP_TIMEOUT;
		break;
	case NE_LOOKUP:
		psdk->nError = SHCERRNO_NE_LOOKUP;
		break;
	case NE_CONNECT:
		psdk->nError = SHCERRNO_NE_CONNECT;
		break;
	}

	if (pnreq)
		ne_request_destroy(pnreq);

#ifdef _NEWAUTH
	//pizon
	//fprintf(stderr , "pszAuth:%s:%p:\n" , pszAuth , pszAuth);
	if(pszAuth)
		free(pszAuth);
#endif

	sBody.clear();
	return bRet;
}

static bool DoUploaduffer(PSHCSDK_STRUCT psdk, LPCSTR dstPath, int nWritePolicy, 
							 //up_stream data_func,						
							 ssize_t (*data_func)(
							 void* userval,
							 char* buf,
							 size_t len
							 ),
							 INT64 beginset,			// point to write
							 INT64 nbytes,			// amount to write
							 void*	   userval,	// User-supplied value for callback
							 int nFrom)
{
	bool ret = false;

	// 현재 URI 보관
	bool   bFindSource    = true;
	bool   bSessionChange = false;
	ne_uri nuri = psdk->nuri;
	string strAuth;

	// 업로드 코드 시작
	string sPath = psdk->sPath + dstPath;
	string sHeaders;
	int nRet;

	ne_request *pnreq = NULL;
	
	psdk->nError = 0; //setting NOERROR
#ifdef _NEWAUTH
	char* pszAuth = get_auth_str(psdk,sPath.c_str(),nFrom);
#endif

	sPath = PathEncode(sPath.c_str());

	if(data_func == NULL) {
		goto EXIT_DOUPLOADUFFER;
	}

	// write total size
	psdk->nFileSize = nbytes;

	// check policy
	if (nWritePolicy == SHWRITEPOLICY_OVERWRITE) {
		PVECTOR_SHFILELIST pvec = (PVECTOR_SHFILELIST)sh_get_filelist((HSHSDK)psdk, dstPath, 0);
		if (pvec && pvec->size() > 0) {
			sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);
		
			if (!sh_delete((HSHSDK)psdk, dstPath)) {
				// delete fail
				goto EXIT_DOUPLOADUFFER;
			}
		} else {
			if(psdk->nError != 0 && psdk->nError != SHCERRNO_NOEXISTSFILE)
					goto EXIT_DOUPLOADUFFER;
		}

		if (pvec)
			sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);

	} else if (nWritePolicy == SHWRITEPOLICY_APPEND)  {
		PVECTOR_SHFILELIST pvec = (PVECTOR_SHFILELIST)sh_get_filelist((HSHSDK)psdk, dstPath, 0);
		if (pvec && pvec->size() > 0) {
			PSHFILE_STRUCT pshf = sh_get_file((HSHSDK)psdk, (HSHFILELIST)pvec, 0);
	
			char szRange[100];
			INT64 startpos, endpos;
			startpos = (beginset >= 0 ? beginset : pshf->size);
			endpos = startpos + psdk->nFileSize;
#ifdef WIN32
			sprintf(szRange, "bytes %I64d-%I64d/%I64d", startpos, endpos - 1, endpos);
#else	// !WIN32
			sprintf(szRange, "bytes %lld-%lld/%lld", startpos, endpos - 1, endpos);
#endif // WIN32
			sHeaders  = "Content-Range|";
			sHeaders += szRange;

			if (pshf->source && strlen(pshf->source) > 7) {
				bSessionChange = true;
				solbox_session_destroy(psdk);

				solbox_uri_parse(pshf->source, &psdk->nuri);

				if (psdk->nuri.path)
					free(psdk->nuri.path);
				psdk->nuri.path = strdup("/dav");

				if (!psdk->nuri.scheme)
					psdk->nuri.scheme = strdup("http");

				if (psdk->nuri.port == 0)
					psdk->nuri.port = 80;

				if( solbox_session_create(psdk) == false )
				{
					psdk->nError = SHCERRNO_DNS_TIMEOUT;
					psdk->sError = GetErrorMessage(psdk->nError);
					goto EXIT_DOUPLOADUFFER;
				}
				psdk->sPath  = psdk->nuri.path;
			}

			bFindSource = false;
		}

		if (pvec)
			sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);
	} else {
		PVECTOR_SHFILELIST pvec = (PVECTOR_SHFILELIST)sh_get_filelist((HSHSDK)psdk, dstPath, 0);
		if (pvec && pvec->size() > 0) {
			sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);

			psdk->nError = SHCERRNO_EXISTSFILE;
			psdk->sError = GetErrorMessage(psdk->nError);
			goto EXIT_DOUPLOADUFFER;
		}

		if (pvec)
			sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);
	}

	// change session
	if (bFindSource) {
		for (int i = 0; !bSessionChange && i < MAX_TRY; i++) {
			PVECTOR_SHFILELIST pvec = (PVECTOR_SHFILELIST)sh_get_filelist((HSHSDK)psdk, dstPath, 0);
			if (pvec && pvec->size() > 0) {
				sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);

				psdk->nError = SHCERRNO_EXISTSFILE;
				psdk->sError = GetErrorMessage(psdk->nError);
				goto EXIT_DOUPLOADUFFER;
			}

			if (pvec)
				sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);

			if (psdk->sRedirect == "")
				break;

			if (i > 0)
				ne_uri_free(&psdk->nuri);

			bSessionChange = true;
			solbox_session_destroy(psdk);

			solbox_uri_parse(psdk->sRedirect.c_str(), &psdk->nuri);

			if (!psdk->nuri.scheme)
				psdk->nuri.scheme = strdup("http");

			if (psdk->nuri.port == 0)
				psdk->nuri.port = 80;

			if( solbox_session_create(psdk) == false )
			{
				psdk->nError = SHCERRNO_DNS_TIMEOUT;
				psdk->sError = GetErrorMessage(psdk->nError);
				goto EXIT_DOUPLOADUFFER;
			}
			psdk->sPath  = psdk->nuri.path;

			if (i == (MAX_TRY - 1)) {
				sHeaders = "Force|YES";
				break;
			}
		}
	}

	// put request 
	pnreq = ne_request_create(psdk->pnsess, "PUT", sPath.c_str());
	if (!pnreq) {
		psdk->nError = SHCERRNO_UNDEFINED;
		psdk->sError = ne_get_error(psdk->pnsess);
		goto EXIT_DOUPLOADUFFER;
	}

#ifdef _NEWAUTH
	strAuth = "Basic ";
	strAuth += psdk->sAuth;

	ne_add_request_header(pnreq, "Authorization", strAuth.c_str());	
#else // _SP_SDK_
	ne_add_request_header(pnreq, "Authorization", psdk->sAuth.c_str());
#endif

	if (sHeaders.length() > 0) {
		string::size_type stBegin = 0, stEnd = 0;
		do {
			stEnd = sHeaders.find(',', stBegin);

			string sHeader = sHeaders.substr(stBegin, stEnd - stBegin);

			string::size_type stDivider = sHeader.find('|');
			ne_add_request_header(pnreq, sHeader.substr(0, stDivider).c_str(), sHeader.substr(stDivider + 1, sHeader.length()).c_str());

			stBegin = stEnd + 1;
		} while (stEnd != string::npos);
	}

	INT64 body_size;
	if (beginset > 0) {
		body_size = psdk->nFileSize - beginset;
	} else {
		body_size = psdk->nFileSize;
	}
	
	ne_set_request_body_provider(pnreq, psdk->nFileSize - psdk->iFilePos, data_func, userval);

	nRet = ne_request_dispatch(pnreq);

	if (psdk->nTranStop == SHSTOP_NO) {
		if (nRet != NE_OK) {
			psdk->nError = nRet;
			psdk->sError = ne_get_error(psdk->pnsess);
			goto EXIT_DOUPLOADUFFER;
		}

		ne_status* pnstat = (ne_status*)ne_get_status(pnreq);
		if (pnstat->code != 0 && (pnstat->code < 200 || pnstat->code >= 300)) {
			psdk->nError = pnstat->code;
			psdk->sError = ne_get_error(psdk->pnsess);
			goto EXIT_DOUPLOADUFFER;
		}
	}
	ne_end_request(pnreq);

	ret = true;

EXIT_DOUPLOADUFFER:

	switch (psdk->nError) {
	case 404:
		psdk->nError = SHCERRNO_NOEXISTSFILE;
		break;
	}

#ifdef _NEWAUTH
	if(pszAuth) {
		free(pszAuth);
		pszAuth =NULL;
	}
#endif // _NEWAUTH
	
	if (pnreq)
		ne_request_destroy(pnreq);

	if (bSessionChange) {
		// CHG : dadamin / 2010.12.16 - Redirection 실패 시 최초에 받은 uri 정보로 환원함.
		//		이처럼 변경하는 이유는 신규(2010.12.16) 개발될 SDK에서는 대표 URI를 사용하지 않고
		//		dns resolve해서 나온 IP 하나만 사용하기 때문에 301로 연결 성공한 정보를 그대로 사용함.
		if( ret == false )
		{
			solbox_session_destroy(psdk);
			ne_uri_free(&psdk->nuri);
			psdk->nuri   = nuri;
			if( solbox_session_create(psdk) == false )
			{
				// 바로 리턴하기때문에 후처리는 하지 않아도 된다.
				psdk->nError = SHCERRNO_DNS_TIMEOUT;
				psdk->sError = GetErrorMessage(psdk->nError);
			}
			psdk->sPath  = "/dav";
		}
		else
		{
			psdk->sPath  = "/dav";
			ne_uri_free(&nuri);
		}
	}

#ifdef WIN32
	CString strTemp;
	strTemp.Format("UploadBuffer %s : %s : %d", ret ? "SUCCESS" : "ERROR", dstPath, psdk->nError);
	OutputDebugString(strTemp);
#endif // WIN32

	return ret;
}

static bool DoUpload(PSHCSDK_STRUCT psdk, LPCSTR pszSrcFile, LPCSTR pszDstFile, int nWritePolicy, INT64 nAvailQuota,int nFrom, LPCSTR extSession)
{
	// 현재 URI 보관
	bool   bFindSource    = true;
	bool   bSessionChange = false;
	ne_uri nuri = psdk->nuri;
	string strAuth;

	// 업로드 코드 시작
	string sPath = psdk->sPath + pszDstFile;
	string sHeaders;
	int nRet;

	ne_request *pnreq = NULL;

	bool bResult = false;
	
	psdk->nError = 0; // setting NOERROR
#ifdef _NEWAUTH
	char* pszAuth = get_auth_str(psdk,sPath.c_str(),nFrom);
#endif

	sPath = PathEncode(sPath.c_str());

#ifdef WIN32
	psdk->hFile = CreateFile(pszSrcFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
#else
	psdk->nFileDesc = open64(pszSrcFile, _O_RDONLY | _O_BINARY | _O_RANDOM);
#endif

#ifdef WIN32
	if (psdk->hFile == INVALID_HANDLE_VALUE){
#else
	if (psdk->nFileDesc == -1) {
#endif
		psdk->nError = GetLastError();
		psdk->sError = GetErrorMessage(psdk->nError);
		goto EXIT_DOUPLOAD;		
	}
	
#ifdef WIN32
	LARGE_INTEGER filesize;
	filesize.QuadPart = 0;
	filesize.LowPart = SetFilePointer(psdk->hFile, filesize.LowPart, &filesize.HighPart, FILE_END);
	psdk->nFileSize = filesize.QuadPart;
	SetFilePointer(psdk->hFile,0, 0, FILE_BEGIN);
#else
	psdk->nFileSize = sh_filelength(psdk->nFileDesc);
#endif

	if (psdk->nFileSize == -1L) {
		psdk->nError = GetLastError();
		psdk->sError = GetErrorMessage(psdk->nError);
		goto EXIT_DOUPLOAD;
	}

	if (nWritePolicy == SHWRITEPOLICY_OVERWRITE) {
		PVECTOR_SHFILELIST pvec = (PVECTOR_SHFILELIST)sh_get_filelist((HSHSDK)psdk, pszDstFile, 0);
		if (pvec && pvec->size() > 0) {
			sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);

			if (!sh_delete((HSHSDK)psdk, pszDstFile))
			{
				goto EXIT_DOUPLOAD;
			}
		} else {
			if( psdk->nError != 0 && psdk->nError != SHCERRNO_NOEXISTSFILE )
				goto EXIT_DOUPLOAD;
		}
		/* Elenoa ADD START */
		if (pvec)
			sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);
		/* Elenoa ADD END */
	}
	else if (nWritePolicy == SHWRITEPOLICY_APPEND)
	{
		PVECTOR_SHFILELIST pvec = (PVECTOR_SHFILELIST)sh_get_filelist((HSHSDK)psdk, pszDstFile, 0);
		if (pvec && pvec->size() > 0)
		{
			PSHFILE_STRUCT pshf = sh_get_file((HSHSDK)psdk, (HSHFILELIST)pvec, 0);

			// CHG : dadamin / 2011.01.05 - 대상 파일이 동일하거나 더 큰 경우 에러 처리함
			if (psdk->nFileSize <= pshf->size) 
			{
				sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);
				bResult = false;
				psdk->nError = SHCERRNO_TRAGETBIGGER;
				psdk->sError = GetErrorMessage(psdk->nError);
				goto EXIT_DOUPLOAD;
			}

#ifdef WIN32
			// Windows Large file 
			LARGE_INTEGER li;
			li.QuadPart = pshf->size;
			
			li.LowPart = SetFilePointer(psdk->hFile,li.LowPart, &li.HighPart, FILE_BEGIN);
			psdk->iFilePos = li.QuadPart;
#else
			psdk->iFilePos = _lseeki64(psdk->nFileDesc, pshf->size, SEEK_SET);
#endif
			if (psdk->iFilePos == -1L) {
				sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);
				goto EXIT_DOUPLOAD;
			}

			char szRange[100];
#ifdef WIN32
			sprintf(szRange, "bytes %I64d-%I64d/%I64d", psdk->iFilePos, psdk->nFileSize - 1, psdk->nFileSize);
#else
			sprintf(szRange, "bytes %lld-%lld/%lld", psdk->iFilePos, psdk->nFileSize - 1, psdk->nFileSize);
#endif

			sHeaders  = "Content-Range|";
			sHeaders += szRange;

			if (pshf->source && strlen(pshf->source) > 7) {
				bSessionChange = true;
				solbox_session_destroy(psdk);

				// CHG : dadamin / 2010.12.15 버전UP(0.29.5)에 따른 ne_uri_parse 하위 호환성 결로 발생한 문제 수정 
				solbox_uri_parse(pshf->source, &psdk->nuri);

				if (psdk->nuri.path)
					free(psdk->nuri.path);
				psdk->nuri.path = strdup("/dav");

				if (!psdk->nuri.scheme)
					psdk->nuri.scheme = strdup("http");

				if (psdk->nuri.port == 0)
					psdk->nuri.port = 80;

				if( solbox_session_create(psdk) == false )
				{
					psdk->nError = SHCERRNO_DNS_TIMEOUT;
					psdk->sError = GetErrorMessage(psdk->nError);
					goto EXIT_DOUPLOAD;
				}
				psdk->sPath  = psdk->nuri.path;
			}

			bFindSource = false;
		}
		sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);
	}
	else {
		PVECTOR_SHFILELIST pvec = (PVECTOR_SHFILELIST)sh_get_filelist((HSHSDK)psdk, pszDstFile, 0);
		if (pvec && pvec->size() > 0) {
			sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);

			psdk->nError = SHCERRNO_EXISTSFILE;
			psdk->sError = GetErrorMessage(psdk->nError);
			goto EXIT_DOUPLOAD;
		}
		/* Elenoa ADD START */
		if (pvec)
			sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);
		/* Elenoa ADD END */
	}

	if ((UINT64)nAvailQuota < (UINT64)(psdk->nFileSize - psdk->iFilePos)) {
		psdk->nError = SHCERRNO_NOAVAILQUOTA;
		psdk->sError = GetErrorMessage(psdk->nError);
		goto EXIT_DOUPLOAD;
	}

	if (bFindSource) {
		for (int i = 0; !bSessionChange && i < MAX_TRY; i++) {
			PVECTOR_SHFILELIST pvec = (PVECTOR_SHFILELIST)sh_get_filelist((HSHSDK)psdk, pszDstFile, 0);
			if (pvec && pvec->size() > 0) {
				sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);

				psdk->nError = SHCERRNO_EXISTSFILE;
				psdk->sError = GetErrorMessage(psdk->nError);
				goto EXIT_DOUPLOAD;
			}
			/* Elenoa ADD START */
			if (pvec)
				sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);
			/* Elenoa ADD END */

			if (psdk->sRedirect == "")
				break;

			if (i > 0)
				ne_uri_free(&psdk->nuri);

			bSessionChange = true;
			solbox_session_destroy(psdk);

			// CHG : dadamin / 2010.12.15 버전UP(0.29.5)에 따른 ne_uri_parse 하위 호환성 결여로 발생한 문제 수정 
			solbox_uri_parse(psdk->sRedirect.c_str(), &psdk->nuri);

			if (!psdk->nuri.scheme)
				psdk->nuri.scheme = strdup("http");

			if (psdk->nuri.port == 0)
				psdk->nuri.port = 80;

			if( solbox_session_create(psdk) == false )
			{
				psdk->nError = SHCERRNO_DNS_TIMEOUT;
				psdk->sError = GetErrorMessage(psdk->nError);
				goto EXIT_DOUPLOAD;
			}
			psdk->sPath  = psdk->nuri.path;

			if (i == (MAX_TRY - 1)) {
				sHeaders = "Force|YES";
				break;
			}
		}
	}

	pnreq = ne_request_create(psdk->pnsess, "PUT", sPath.c_str());
	if (!pnreq) {
		psdk->nError = SHCERRNO_UNDEFINED;
		psdk->sError = ne_get_error(psdk->pnsess);
		goto EXIT_DOUPLOAD;
	}

	#ifdef _NEWAUTH
		// 2011.06.02 : 업로드 시 신인증(auth timeout 체크) 체계로 수행할 경우
	    //				body까지 전송 후에 관련 에러 받을 수 있으므로
		//				관련 에러(401) 받기까지 시간 소요가 긴다.
		//				또한, 재시도 로직으로 인해서 업로드를 이중으되어지는 문제도 있다.
		//				그러므로, 업로드 관련 인증을 수행하지 않게 이전 인증체제를 사용한다.
		strAuth = "Basic ";
		strAuth += psdk->sAuth;

		ne_add_request_header(pnreq, "Authorization", strAuth.c_str());
	#else // _SP_SDK_
		ne_add_request_header(pnreq, "Authorization", psdk->sAuth.c_str());
	#endif
	// toto 
	if ( extSession != NULL )
	{
		if ( strlen( extSession ) > 512)
		{
			psdk->nError = SHCERRNO_EXT_OVERFLOW;
			psdk->sError = GetErrorMessage(psdk->nError);
			goto EXIT_DOUPLOAD;
		}
		ne_add_request_header(pnreq, "SB_ExtSession", extSession);
	}

	if (sHeaders.length() > 0) {
		string::size_type stBegin = 0, stEnd = 0;
		do {
			stEnd = sHeaders.find(',', stBegin);

			string sHeader = sHeaders.substr(stBegin, stEnd - stBegin);

			string::size_type stDivider = sHeader.find('|');
			ne_add_request_header(pnreq, sHeader.substr(0, stDivider).c_str(), sHeader.substr(stDivider + 1, sHeader.length()).c_str());

			stBegin = stEnd + 1;
		} while (stEnd != string::npos);
	}

	//dadamin : 32bit Large File Support / Simplify LFS support, drop *64 transition functions
	ne_set_request_body_provider(pnreq, psdk->nFileSize - psdk->iFilePos, UploadProvider, psdk);

	nRet = ne_request_dispatch(pnreq);

	if (psdk->nTranStop == SHSTOP_NO) {
		if (nRet != NE_OK) {
			psdk->nError = nRet;
			psdk->sError = ne_get_error(psdk->pnsess);
			goto EXIT_DOUPLOAD;
		}

		ne_status* pnstat = (ne_status*)ne_get_status(pnreq);
		if (pnstat->code != 0 && (pnstat->code < 200 || pnstat->code >= 300)) {
			psdk->nError = pnstat->code;
			psdk->sError = ne_get_error(psdk->pnsess);
			goto EXIT_DOUPLOAD;
		}
	}
	ne_end_request(pnreq);

	bResult = true;

EXIT_DOUPLOAD:
	switch (psdk->nError) {
	case 404:
		psdk->nError = SHCERRNO_NOEXISTSFILE;
		break;
	}

#ifdef _NEWAUTH
	if(pszAuth)
		free(pszAuth);
#endif

#ifdef WIN32
	if(psdk->hFile != NULL)
	{
		CloseHandle(psdk->hFile);
		psdk->hFile = NULL;
	}
#else
	if (psdk->nFileDesc != -1) 
	{
		close(psdk->nFileDesc);
		psdk->nFileDesc = -1;
	}
#endif

	if (pnreq)
		ne_request_destroy(pnreq);

	if (bSessionChange) {
		// CHG : dadamin / 2010.12.16 - Redirection 실패 시 최초에 받은 uri 정보로 환원함.
		//		이처럼 변경하는 이유는 신규(2010.12.16) 개발될 SDK에서는 대표 URI를 사용하지 않고
		//		dns resolve해서 나온 IP 하나만 사용하기 때문에 301로 연결 성공한 정보를 그대로 사용함.
		if( bResult == false )
		{
			solbox_session_destroy(psdk);
			ne_uri_free(&psdk->nuri);
			psdk->nuri   = nuri;
			if( solbox_session_create(psdk) == false )
			{
				// 바로 리턴하기때문에 후처리는 하지 않아도 된다.
				psdk->nError = SHCERRNO_DNS_TIMEOUT;
				psdk->sError = GetErrorMessage(psdk->nError);
			}
			psdk->sPath  = "/dav";
		}
		else
		{			
			psdk->sPath  = "/dav";
			ne_uri_free(&nuri);
		}
	}
#ifdef WIN32
	CString strTemp;
	strTemp.Format("Upload %s : %s : %d", bResult ? "SUCCESS" : "ERROR", pszDstFile, psdk->nError);
	OutputDebugString(strTemp);
#endif

	return bResult;
}

static bool DoDownloadBuffer(PSHCSDK_STRUCT psdk, LPCSTR pszSrcFile,
							 int (*data_func)(
							 void* userval,
							 const char* buf,
							 size_t len),		// If len is zero the transfer is restarted from the begining
							 INT64 beginset,			// point to read
							 INT64 nbytes,			// amount to read	
							 void*	   userval,	// User-supplied value for callback
							 int nFrom)
{
	string sPath = psdk->sPath + pszSrcFile;
	string sHeaders;
	PSHFILE_STRUCT pshf;
	int nRet;

	ne_request *pnreq = NULL;
	char* pszAuth;
	bool bResult = false;

	ne_uri prvenuri = psdk->nuri;
	char szrange[100] = {0};
REPEAT_FUNC:

#ifdef _NEWAUTH
	pszAuth = get_auth_str(psdk,sPath.c_str(),nFrom);
#endif

	sPath = PathEncode(sPath.c_str());

	PVECTOR_SHFILELIST pvec = (PVECTOR_SHFILELIST)sh_get_filelist((HSHSDK)psdk, pszSrcFile, 0);
	if (!pvec || pvec->size() == 0)
		goto EXIT_DODOWNLOADBUFFER;		

	if(data_func == NULL)
		goto EXIT_DODOWNLOADBUFFER;	

	pshf = sh_get_file((HSHSDK)psdk, (HSHFILELIST)pvec, 0);

	pnreq = ne_request_create(psdk->pnsess, "GET", sPath.c_str());
	if (!pnreq) {
		psdk->nError = SHCERRNO_UNDEFINED;
		psdk->sError = ne_get_error(psdk->pnsess);
		goto EXIT_DODOWNLOADBUFFER;
	}

#ifdef _NEWAUTH
	ne_add_request_header(pnreq, "Authorization", pszAuth);
#else // _SP_SDK_
	ne_add_request_header(pnreq, "Authorization", psdk->sAuth.c_str());
#endif

	// make range get header	
	if(beginset > 0) 
	{
		if(nbytes > 0)
		{
			sprintf(szrange,
#ifdef WIN32
				"bytes=%I64d-%I64d",
#else
				"bytes=%lld-%lld",
#endif
				beginset, beginset + nbytes - 1);
		}
		else
		{
			sprintf(szrange,
#ifdef WIN32
				"bytes=%I64d-%I64d",
#else
				"bytes=%lld-%lld",
#endif
				beginset, pshf->size);
		}
	}
	else
	{
		sprintf(szrange,
#ifdef WIN32
			"bytes=%I64d-%I64d",
#else
			"bytes=%lld-%lld",
#endif
			psdk->iFilePos, pshf->size);
	}

	if (szrange[0] != '\0') 
	{
		sHeaders += "Range|";
		sHeaders += szrange;
		sHeaders += ",Accept-Ranges|bytes";
	}

	if (sHeaders.length() > 0) {
		string::size_type stBegin = 0, stEnd = 0;
		do {
			stEnd = sHeaders.find(',', stBegin);

			string sHeader = sHeaders.substr(stBegin, stEnd - stBegin);

			string::size_type stDivider = sHeader.find('|');
			ne_add_request_header(pnreq, sHeader.substr(0, stDivider).c_str(), sHeader.substr(stDivider + 1, sHeader.length()).c_str());

			stBegin = stEnd + 1;
		} while (stEnd != string::npos);
	}

	ne_add_response_body_reader(pnreq, ne_accept_2xx, data_func, userval);
	//psdk->nFileSize = pshf->size;

	nRet = ne_request_dispatch(pnreq);

	if (psdk->nTranStop == SHSTOP_NO) {
		if (nRet != NE_OK) {
			psdk->nError = nRet;
			psdk->sError = ne_get_error(psdk->pnsess);
			goto EXIT_DODOWNLOADBUFFER;
		}

		ne_status* pnstat = (ne_status*)ne_get_status(pnreq);
		if (pnstat->code != 0 && (pnstat->code < 200 || pnstat->code >= 300)) {
			psdk->nError = pnstat->code;
			psdk->sError = ne_get_error(psdk->pnsess);
			goto EXIT_DODOWNLOADBUFFER;
		}
	}
	ne_end_request(pnreq);

	bResult = true;

EXIT_DODOWNLOADBUFFER:
	sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);

#ifdef _NEWAUTH
	if(pszAuth)
		free(pszAuth);
#endif

	string sRedirection;
	if (pnreq) {
		const char *pszLocation = ne_get_response_header(pnreq, "Location");
		if (pszLocation)
			sRedirection = pszLocation;
		ne_request_destroy(pnreq);
	}
	
	// Redirection
	if (psdk->nError == 301 && sRedirection != "") {
		solbox_session_destroy(psdk);

		// 지정된 곳으로 접속
		ne_uri nuri;
		// CHG : dadamin / 2010.12.15 버전UP(0.29.5)에 따른 ne_uri_parse 하위 호환성 결여로 발생한 문제 수정 
		solbox_uri_parse(sRedirection.c_str(), &nuri);

		if (!nuri.scheme)
			nuri.scheme = strdup("http");

		if (nuri.port == 0)
			nuri.port = 80;

		psdk->sPath = nuri.path;
		psdk->sPath.erase(psdk->sPath.find('/', 1), string::npos);

		psdk->nuri = nuri;

		if( solbox_session_create(psdk) == false )
		{
			ne_uri_free(&prvenuri);
			psdk->nError = SHCERRNO_DNS_TIMEOUT;
			psdk->sError = GetErrorMessage(psdk->nError);
			return false;
		}

		psdk->iFilePos  = 0;
		psdk->cOldTick  = clock();
		psdk->nTranStop = SHSTOP_NO;
		bResult = DoDownloadBuffer(psdk, pszSrcFile, data_func, beginset, nbytes, userval, nFrom);

		// CHG : dadamin / 2010.12.16 - Redirection 실패 시 최초에 받은 uri 정보로 환원함.
		//		이처럼 변경하는 이유는 신규(2010.12.16) 개발될 SDK에서는 대표 URI를 사용하지 않고
		//		dns resolve해서 나온 IP 하나만 사용하기 때문에 301로 연결 성공한 정보를 그대로 사용함.
		if(!bResult)
		{
			// 원래 서버로 재접속
			solbox_session_destroy(psdk);
			ne_uri_free(&psdk->nuri);
			psdk->nuri = prvenuri;
			if( solbox_session_create(psdk) == false )
			{
				psdk->nError = SHCERRNO_DNS_TIMEOUT;
				psdk->sError = GetErrorMessage(psdk->nError);
			}
			psdk->sPath  = "/dav";
		}
		else 
		{
			psdk->sPath  = "/dav";
			ne_uri_free(&prvenuri);
		}
	}

	return bResult;
}

static bool DoDownload(PSHCSDK_STRUCT psdk, LPCSTR pszSrcFile, LPCSTR pszDstFile, int nWritePolicy, INT64 nAvailQuota,int nFrom, LPCSTR extSession)
{
	string sPath = psdk->sPath + pszSrcFile;
	string sHeaders;
	PSHFILE_STRUCT pshf;
	char* pszAuth;

	ne_request *pnreq = NULL;
	bool bResult = false; 
	int nFlag = 0;
#ifndef  WIN32
	mode_t mode;
#endif // !WIN32
#ifdef _NEWAUTH
	pszAuth = get_auth_str(psdk,sPath.c_str(),nFrom);
#endif

	sPath = PathEncode(sPath.c_str());

	PVECTOR_SHFILELIST pvec = NULL;
	struct _stati64 stDstFile;
	int nRet = _stati64(pszDstFile, &stDstFile);
	if (nWritePolicy == SHWRITEPOLICY_NONE) {
		if (nRet != -1) {
			psdk->nError = SHCERRNO_EXISTSFILE;
			psdk->sError = GetErrorMessage(psdk->nError);
			goto EXIT_DODOWNLOAD;
		}
	}
	else {
		if (nRet != -1 && (stDstFile.st_mode & _S_IFDIR)) {
			if (nWritePolicy == SHWRITEPOLICY_OVERWRITE) {
				if (!DeleteDirectory(pszDstFile)) {
					psdk->nError = GetLastError();
					psdk->sError = GetErrorMessage(psdk->nError);
					goto EXIT_DODOWNLOAD;					
				}
			}
			else {
				psdk->nError = SHCERRNO_EXISTSFILE;
				psdk->sError = GetErrorMessage(psdk->nError);
			}
		}
	}

	pvec = (PVECTOR_SHFILELIST)sh_get_filelist((HSHSDK)psdk, pszSrcFile, 0);
	if (!pvec || pvec->size() == 0)
	{
		goto EXIT_DODOWNLOAD;
	}
	
#ifdef WIN32
	nFlag=nWritePolicy;
	switch (nWritePolicy) 
	{
	case SHWRITEPOLICY_OVERWRITE:
	case SHWRITEPOLICY_NONE :
		nFlag = CREATE_ALWAYS;  
		break;
	case SHWRITEPOLICY_APPEND:    
		nFlag = OPEN_ALWAYS; 
		break;
	}
#else
	nFlag = _O_CREAT | _O_WRONLY | _O_BINARY | _O_RANDOM;
	switch (nWritePolicy) 
	{
	case SHWRITEPOLICY_OVERWRITE: nFlag |= _O_TRUNC;  break;
	case SHWRITEPOLICY_APPEND:    nFlag |= _O_APPEND; break;
	}
#endif


#ifdef WIN32
	psdk->hFile = CreateFile(pszDstFile,GENERIC_WRITE,FILE_SHARE_READ,NULL,nFlag,FILE_ATTRIBUTE_NORMAL,NULL);
	if (psdk->hFile == INVALID_HANDLE_VALUE) {
#else
	// 일부 linux 계열(Ubuntu) mode 인자를 넣지 않고 O_CREAT를 수행할 수 없게 되어 있음.
	mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 파일의 권한 0644

	psdk->nFileDesc = open64(pszDstFile, nFlag, mode);
	if (psdk->nFileDesc == -1L) {
#endif
		psdk->nError = GetLastError();
		psdk->sError = GetErrorMessage(psdk->nError);
		goto EXIT_DODOWNLOAD;
	}

	pshf = sh_get_file((HSHSDK)psdk, (HSHFILELIST)pvec, 0);

#ifdef WIN32
	LARGE_INTEGER li;
	li.QuadPart = 0;

    li.LowPart = SetFilePointer(psdk->hFile, li.LowPart, &li.HighPart, FILE_END);
	psdk->iFilePos = li.QuadPart;	
#else
	psdk->iFilePos = _lseeki64(psdk->nFileDesc, 0, SEEK_END);
#endif
	if (psdk->iFilePos == -1L)
	{
		psdk->nError = GetLastError();
		psdk->sError = GetErrorMessage(psdk->nError);
		goto EXIT_DODOWNLOAD;
	}

	if (pshf->size > 0 && psdk->iFilePos >= pshf->size)
	{
		bResult = false;
		psdk->nError = SHCERRNO_TRAGETBIGGER;
		psdk->sError = GetErrorMessage(psdk->nError);
		goto EXIT_DODOWNLOAD;
	}

	if (nWritePolicy == SHWRITEPOLICY_APPEND) 
	{
		char szRange[100];

#ifdef WIN32
		sprintf(szRange, "bytes=%I64d-%I64d", psdk->iFilePos, pshf->size);
#else
		sprintf(szRange, "bytes=%lld-%lld", psdk->iFilePos, pshf->size);
#endif
		sHeaders += "Range|";
		sHeaders += szRange;
		sHeaders += ",Accept-Ranges|bytes";
	}

	if ((UINT64)nAvailQuota < (UINT64)(pshf->size - psdk->iFilePos)) {
		psdk->nError = SHCERRNO_NOAVAILQUOTA;
		psdk->sError = GetErrorMessage(psdk->nError);
		goto EXIT_DODOWNLOAD;
	}

	pnreq = ne_request_create(psdk->pnsess, "GET", sPath.c_str());
	if (!pnreq) {
		psdk->nError = SHCERRNO_UNDEFINED;
		psdk->sError = ne_get_error(psdk->pnsess);
		goto EXIT_DODOWNLOAD;
	}

#ifdef _NEWAUTH
	ne_add_request_header(pnreq, "Authorization", pszAuth);
#else // _SP_SDK_
	ne_add_request_header(pnreq, "Authorization", psdk->sAuth.c_str());
#endif

	// toto 
	if ( extSession != NULL )
	{
		if ( strlen( extSession ) > 512)
		{
			psdk->nError = SHCERRNO_EXT_OVERFLOW;
			psdk->sError = GetErrorMessage(psdk->nError);
			goto EXIT_DODOWNLOAD;
		}
		ne_add_request_header(pnreq, "SB_ExtSession", extSession);
	}

	if (sHeaders.length() > 0) {
		string::size_type stBegin = 0, stEnd = 0;
		do {
			stEnd = sHeaders.find(',', stBegin);

			string sHeader = sHeaders.substr(stBegin, stEnd - stBegin);

			string::size_type stDivider = sHeader.find('|');
			ne_add_request_header(pnreq, sHeader.substr(0, stDivider).c_str(), sHeader.substr(stDivider + 1, sHeader.length()).c_str());

			stBegin = stEnd + 1;
		} while (stEnd != string::npos);
	}

	ne_add_response_body_reader(pnreq, ne_accept_2xx, DownloadProvider, psdk);
	psdk->nFileSize = pshf->size;

	nRet = ne_request_dispatch(pnreq);

	if (psdk->nTranStop == SHSTOP_NO) {
		if (nRet != NE_OK) {
			psdk->nError = nRet;
			psdk->sError = ne_get_error(psdk->pnsess);
			goto EXIT_DODOWNLOAD;
		}

		ne_status* pnstat = (ne_status*)ne_get_status(pnreq);
		if (pnstat->code != 0 && (pnstat->code < 200 || pnstat->code >= 300)) {
			psdk->nError = pnstat->code;
			psdk->sError = ne_get_error(psdk->pnsess);
			goto EXIT_DODOWNLOAD;
		}
	}
	ne_end_request(pnreq);

	bResult = true;

EXIT_DODOWNLOAD:
	sh_free_filelist((HSHSDK)psdk, (HSHFILELIST)pvec);

#ifdef _NEWAUTH
	if(pszAuth)
		free(pszAuth);
#endif

#ifdef WIN32
	if (psdk->hFile != NULL) {
		CloseHandle(psdk->hFile);
		psdk->hFile = NULL;
	}
#else
	if (psdk->nFileDesc) {
		close(psdk->nFileDesc);
		psdk->nFileDesc = -1;
	}
#endif

	string sRedirection;
	if (pnreq) {
		const char *pszLocation = ne_get_response_header(pnreq, "Location");
		if (pszLocation)
			sRedirection = pszLocation;
		ne_request_destroy(pnreq);
	}

	ne_uri prvenuri;
	// Redirection
	if (psdk->nError == 301 && sRedirection != "") {
		solbox_session_destroy(psdk);

		// 지정된 곳으로 접속
		ne_uri nuri;
		// CHG : dadamin / 2010.12.15 버전UP(0.29.5)에 따른 ne_uri_parse 하위 호환성 결로 발생한 문제 수정 
		solbox_uri_parse(sRedirection.c_str(), &nuri);

		if (!nuri.scheme)
			nuri.scheme = strdup("http");

		if (nuri.port == 0)
			nuri.port = 80;

		prvenuri = psdk->nuri;

		psdk->sPath = nuri.path;
		psdk->sPath.erase(psdk->sPath.find('/', 1), string::npos);
			
		psdk->nuri = nuri;

		if( solbox_session_create(psdk) == false )
		{
			ne_uri_free(&prvenuri);
			psdk->nError = SHCERRNO_DNS_TIMEOUT;
			psdk->sError = GetErrorMessage(psdk->nError);
			return false;
		}

#ifdef WIN32
		psdk->hFile = NULL;
#else
		psdk->nFileDesc = -1;
#endif
		psdk->iFilePos  = 0;
		psdk->cOldTick  = clock();
		psdk->nTranStop = SHSTOP_NO;
		bResult = DoDownload(psdk, pszSrcFile, pszDstFile, nWritePolicy == SHWRITEPOLICY_NONE ? SHWRITEPOLICY_OVERWRITE : nWritePolicy, nAvailQuota,nFrom, extSession);

		// 원래 서버로 재접속
		// CHG : dadamin / 2010.12.16 - Redirection 실패 시 최초에 받은 uri 정보로 환원함.
		//		이처럼 변경하는 이유는 신규(2010.12.16) 개발될 SDK에서는 대표 URI를 사용하지 않고
		//		dns resolve해서 나온 IP 하나만 사용하기 때문에 301로 연결 성공한 정보를 그대로 사용함.
		if( bResult == false )
		{
			solbox_session_destroy(psdk);
			ne_uri_free(&psdk->nuri);
			psdk->nuri = prvenuri;
			if( solbox_session_create(psdk) == false )
			{
				psdk->nError = SHCERRNO_DNS_TIMEOUT;
				psdk->sError = GetErrorMessage(psdk->nError);
			}
			psdk->sPath  = "/dav";
		}
		else 
		{
			psdk->sPath  = "/dav";
			ne_uri_free(&prvenuri);
		}
	}

	return bResult;
}
// interfal function

// output interface
WIN32DLL
HSHSDK sh_init(const char *url, const char *authString, sh_callback proc, void *param)
{
	PSHCSDK_STRUCT psdk = new SHCSDK_STRUCT;
	const ne_inet_addr** result;
	int nResultCnt;

	if (!psdk)
		return NULL;

	psdk->pnsess   = NULL;
	psdk->pvecshfl = NULL;

	// neon.lib init...
	if (ne_sock_init() != NE_OK)
		return NULL;

	// CHG : dadamin / 2010.12.15 버전UP(0.29.5)에 따른 ne_uri_parse 하위 호환성 결로 발생한 문제 수정 
	int nNeonRet = solbox_uri_parse(url, &psdk->nuri);
	if (nNeonRet != NE_OK)
		return NULL;

	if (!psdk->nuri.scheme)
		psdk->nuri.scheme = strdup("http");

	if (psdk->nuri.port == 0)
		psdk->nuri.port = 80;

	
	if( solbox_session_create(psdk) == false )
	{
		psdk->nError = SHCERRNO_DNS_TIMEOUT;
		psdk->sError = GetErrorMessage(psdk->nError);
		goto EXIT_SH_INIT;
	}

	if (!psdk->pnsess)
		goto EXIT_SH_INIT;


	// time out setting
	ne_set_read_timeout(psdk->pnsess, _read_time_out);
	ne_set_connect_timeout(psdk->pnsess, _connect_time_out);
	// time out setting

	psdk->sPath = psdk->nuri.path;

#ifdef _NEWAUTH
	psdk->sAuth  = authString;
#else // _SP_SDK_
	psdk->sAuth  = "Basic ";
	psdk->sAuth  += authString;
#endif

	psdk->nError = 0;
	psdk->sError = "";

	psdk->proc   = proc;
	psdk->pParam = param;

	psdk->bWorking = false;

	psdk->sSessionID = DEFAULT_SESSIONID;

	psdk->nAuthExpire = DEFAULT_EXPIRE_TIME;

#ifdef WIN32
	psdk->hFile = NULL;
#else
	psdk->nFileDesc = -1;
#endif

	psdk->nTranStop = SHSTOP_NO;

	psdk->starttick = GetTickCount();
	
	psdk->nSpeedLimit = 0;

	dwTransferd = 0;
	return psdk;

EXIT_SH_INIT:
	int nError = GetLastError();

	ne_uri_free(&psdk->nuri);

	if (psdk->pnsess)
		solbox_session_destroy(psdk);

	delete psdk;

	SetLastError(nError);

	return NULL;
}

WIN32DLL int sh_set_sessionID(HSHSDK hshsdk,const char *pszID)
{
	if(!hshsdk)
		return FALSE;
	
	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;

	psdk->sSessionID = pszID;
	
	return TRUE;
}

WIN32DLL const char* sh_get_sessionID(HSHSDK hshsdk)
{
	if(!hshsdk)
		return FALSE;
	
	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;

	return psdk->sSessionID.c_str();
}

WIN32DLL
void sh_free(HSHSDK hshsdk)
{
	if (!hshsdk)
		return;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;

	for (set<PVECTOR_SHFILELIST>::iterator iter = psdk->setFileList.begin(); iter != psdk->setFileList.end(); iter++)
		FreeFileListVector(*iter);

	ne_uri_free(&psdk->nuri);

	if (psdk->pnsess)
		solbox_session_destroy(psdk);

	dwTransferd = 0;

	delete psdk;
}

WIN32DLL
int sh_get_error_number(HSHSDK hshsdk)
{
	if (!hshsdk)
		return SHCERRNO_INVALIDHDL;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;

	return psdk->nError;
}

WIN32DLL
const char* sh_get_error_message(HSHSDK hshsdk)
{
	if (!hshsdk)
		return GetErrorMessage(SHCERRNO_INVALIDHDL).c_str();

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;

	return psdk->sError.c_str();
}

WIN32DLL
HSHFILELIST sh_get_filelist(HSHSDK hshsdk, const char* path, unsigned int depth)
{
	if (!hshsdk)
		return NULL;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;	
	psdk->nError = SHCERRNO_NOERROR;

	string sPath = psdk->sPath;
	if (path[0] != '/')
		sPath += "/";
	sPath += path;

	char szDepth[10];
	if (depth <= 1)
		sprintf(szDepth, "%d", depth);
	else
		strcpy(szDepth, "infinity");

	string sHeaders = "Depth|";
	sHeaders += szDepth;

	string sBody = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
	sBody += "<D:propfind xmlns:D=\"DAV:\">";
	sBody += "<D:prop>";
	sBody += "<D:resourcetype/>";
	sBody += "<D:getcontentlength/>";
	sBody += "<D:getlastmodified/>";
	sBody += "<D:source/>";
	sBody += "</D:prop>";
	sBody += "</D:propfind>";

	SHFILELIST_PARSER_STRUCT shflp;
	::memset(&shflp, 0, sizeof(SHFILELIST_PARSER_STRUCT));

	shflp.nRootPathLen = (UINT)psdk->sPath.length();

	shflp.pvec = new VECTOR_SHFILELIST;
	if (shflp.pvec) {
		if (DoCommand(psdk, "PROPFIND", sPath.c_str(), sHeaders.c_str(), sBody.c_str(), true,
					(LPVOID)FileListSTagParser, (LPVOID)FileListDataParser, (LPVOID)FileListETagParser, &shflp,1)) 
		{
			psdk->setFileList.insert(shflp.pvec);
			psdk->pvecshfl = shflp.pvec;

			VECTOR_SHFILELIST::iterator iter = shflp.pvec->begin();
			if (strcmp((*iter)->path, "*myself*") == 0)
				strcpy((*iter)->path, sPath.substr(psdk->sPath.length(), string::npos).c_str());
		}
		else 
		{
			if (psdk->nError == SHCERRNO_AUTHORIZATION)
			{
				if (DoCommand(psdk, "PROPFIND", sPath.c_str(), sHeaders.c_str(), sBody.c_str(), true,
					(LPVOID)FileListSTagParser, (LPVOID)FileListDataParser, (LPVOID)FileListETagParser, &shflp,0))
				{
					psdk->setFileList.insert(shflp.pvec);
					psdk->pvecshfl = shflp.pvec;

					VECTOR_SHFILELIST::iterator iter = shflp.pvec->begin();
					if (strcmp((*iter)->path, "*myself*") == 0)
						strcpy((*iter)->path, sPath.substr(psdk->sPath.length(), string::npos).c_str());
				}
				else
				{
					FreeFileListVector(shflp.pvec);
					shflp.pvec = NULL;
				}
			}
			else
			{
				FreeFileListVector(shflp.pvec);
				shflp.pvec = NULL;
			}
		}
	}
	else {
		psdk->nError = GetLastError();
		psdk->sError = GetErrorMessage(psdk->nError);
	}

	sPath.clear();
	sBody.clear();
	return shflp.pvec;
}

WIN32DLL
long sh_get_filelist_count(HSHSDK hshsdk, HSHFILELIST hshfl)
{
	if (!hshsdk)
		return -1;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = SHCERRNO_NOERROR;

	if (!hshfl) {
		psdk->nError = SHCERRNO_INVALIDFLHDL;
		psdk->sError = GetErrorMessage(psdk->nError);
		return -1;
	}

	if (psdk->pvecshfl != hshfl) {
		set<PVECTOR_SHFILELIST>::iterator iter = psdk->setFileList.find((PVECTOR_SHFILELIST)hshfl);
		if (iter == psdk->setFileList.end()) {
			psdk->nError = SHCERRNO_INVALIDFLHDL;
			psdk->sError = GetErrorMessage(psdk->nError);
			return -1;
		}

		psdk->pvecshfl = (*iter);
	}

	return (long)psdk->pvecshfl->size();
}

WIN32DLL
PSHFILE_STRUCT sh_get_file(HSHSDK hshsdk, HSHFILELIST hshfl, long index)
{
	if (!hshsdk)
		return NULL;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = SHCERRNO_NOERROR;

	if (!hshfl) {
		psdk->nError = SHCERRNO_INVALIDFLHDL;
		psdk->sError = GetErrorMessage(psdk->nError);
		return NULL;
	}

	if (psdk->pvecshfl != hshfl) {
		set<PVECTOR_SHFILELIST>::iterator iter = psdk->setFileList.find((PVECTOR_SHFILELIST)hshfl);
		if (iter == psdk->setFileList.end()) {
			psdk->nError = SHCERRNO_INVALIDFLHDL;
			psdk->sError = GetErrorMessage(psdk->nError);
			return NULL;
		}

		psdk->pvecshfl = (*iter);
	}

	return psdk->pvecshfl->at(index);
}

WIN32DLL
void sh_free_filelist(HSHSDK hshsdk, HSHFILELIST hshfl)
{
	if (!hshsdk || !hshfl)
		return;

	PSHCSDK_STRUCT     psdk = (PSHCSDK_STRUCT)hshsdk;
	PVECTOR_SHFILELIST pvec = (PVECTOR_SHFILELIST)hshfl;

	set<PVECTOR_SHFILELIST>::iterator iter = psdk->setFileList.find(pvec);
	if (iter == psdk->setFileList.end())
		return;

	FreeFileListVector(pvec);
	psdk->setFileList.erase(pvec);

	psdk->pvecshfl = NULL;
}

WIN32DLL
int sh_make_directory(HSHSDK hshsdk, const char* directory)
{
	if (!hshsdk)
		return FALSE;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = 0;
	
	if (sh_file_exist(hshsdk,directory))
	{
		psdk->nError = SHCERRNO_ALREADYEXIST;
		psdk->sError = GetErrorMessage(psdk->nError);
		return false;
	}

	string sDirectory = psdk->sPath;
	if (directory[0] != '/')
		sDirectory += "/";
	sDirectory += directory;
	
	int nRet = DoCommand(psdk, "MKCOL", sDirectory.c_str(), "", "", false, NULL, NULL, NULL, NULL, 1);

	if (!nRet)
	{
		if (sh_get_error_number(psdk) == SHCERRNO_AUTHORIZATION)
			nRet = DoCommand(psdk, "MKCOL", sDirectory.c_str(),"","",false,NULL,NULL,NULL,NULL,0);
	}

	return nRet;
}

WIN32DLL
int sh_copy(HSHSDK hshsdk, const char* srcPath, const char* dstPath, int overwrite)
{
	if (!hshsdk)
		return FALSE;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = SHCERRNO_NOERROR;

	string sSrcPath = psdk->sPath;
	if (srcPath[0] != '/')
		sSrcPath += "/";
	sSrcPath += srcPath;

	string sDstPath = psdk->sPath;
	if (dstPath[0] != '/')
		sDstPath += "/";
	sDstPath += dstPath;

	string sHeader = "New-uri|" + PathEncode(sDstPath.c_str()) + ",Overwrite|";
	sHeader += (overwrite ? "T" : "F");

	int nRet = DoCommand(psdk, "COPY", sSrcPath.c_str(), sHeader.c_str(),"", false, NULL, NULL, NULL, NULL, 1);

	if (!nRet)
	{
		if (sh_get_error_number(psdk) == SHCERRNO_AUTHORIZATION)
			nRet = DoCommand(psdk, "COPY", sSrcPath.c_str(), sHeader.c_str(),"",false,NULL,NULL,NULL,NULL,0);
	}

	return nRet;
}

WIN32DLL
int sh_move(HSHSDK hshsdk, const char* srcPath, const char* dstPath, int overwrite)
{
	if (!hshsdk)
		return FALSE;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = SHCERRNO_NOERROR;

	string sSrcPath = psdk->sPath;
	if (srcPath[0] != '/')
		sSrcPath += "/";
	sSrcPath += srcPath;

	string sDstPath = psdk->sPath;
	if (dstPath[0] != '/')
		sDstPath += "/";
	sDstPath += dstPath;

	string sHeader = "New-uri|" + PathEncode(sDstPath.c_str()) + ",Overwrite|";
	sHeader += (overwrite ? "T" : "F");

	int nRet = DoCommand(psdk, "MOVE", sSrcPath.c_str(), sHeader.c_str(), "", false, NULL, NULL, NULL, NULL, 1);

	if(!nRet)
	{
		if (sh_get_error_number(psdk) == SHCERRNO_AUTHORIZATION)
			nRet = DoCommand(psdk, "MOVE", sSrcPath.c_str(), sHeader.c_str(),"",false,NULL,NULL,NULL,NULL,0);
	}
	return nRet;
}

WIN32DLL
int sh_delete(HSHSDK hshsdk, const char* path)
{
	if (!hshsdk)
		return FALSE;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = SHCERRNO_NOERROR;
	
	int ncount=0;

	// 경로가 '////////' 형식인경우 
	for(int i=0;i<strlen(path);i++)
	{
		if(path[i] == '/')
			ncount++;
	}

	if (path == NULL || strlen(path) == 0 || (strlen(path) == 1 && path[0] == '/') || (ncount == strlen(path))) {
		psdk->nError = SHCERRNO_SERVICE;
		psdk->sError = "루트는 삭제할 수 없습니다.";
		return FALSE;
	}

	string sPath = psdk->sPath;
	if (path[0] != '/')
		sPath += "/";
	sPath += path;

	int nRet = DoCommand(psdk, "DELETE", sPath.c_str(), "", "" , false, NULL, NULL, NULL, NULL, 1);

	if (!nRet)
	{
		if (sh_get_error_number(psdk) == SHCERRNO_AUTHORIZATION)
			nRet = DoCommand(psdk, "DELETE", sPath.c_str(),"","",false,NULL,NULL,NULL,NULL,0);
	}
	return nRet;
}

WIN32DLL
int sh_upload_buffer(
	HSHSDK     hshsdk,      // SDK handle
	const char *dstPath,    // Destination file
	int writePolicy,		// Write policy flag
	up_stream proc,			// user defined function
	INT64 nbytes,			// write size
	void* userval			// User-supplied value for callback
	) {
	int ret = 0;

	if (!hshsdk)
	{
		return ret;
	}

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = SHCERRNO_NOERROR;

	if (psdk->bWorking) {
		psdk->nError = SHCERRNO_ISBUSY;
		psdk->sError = GetErrorMessage(psdk->nError);
		return ret;
	}

#ifdef WIN32
	psdk->hFile = NULL;
#else
	psdk->nFileDesc = -1;
#endif
	psdk->iFilePos  = 0;
	psdk->cOldTick  = clock();
	psdk->nTranStop = SHSTOP_NO;

	ret = DoUploaduffer(psdk, dstPath, writePolicy, proc, 0, nbytes, userval, 1); 

	if (!ret)
	{
		switch( sh_get_error_number(psdk) )
		{
		case 401:
			DoUploaduffer(psdk, dstPath, writePolicy, proc, 0, nbytes, userval, 0); 
			break;			
		case NE_LOOKUP:
			// 2011.06.20 : DNS lookup 실패 시 1회 재시도
			DoUploaduffer(psdk, dstPath, writePolicy, proc, 0, nbytes, userval, 1); 
			break;
		}
	}
	
	return ret;
}

WIN32DLL
int sh_upload_buffer_r(
	HSHSDK     hshsdk,      // SDK handle
	const char *dstPath,    // Destination file
	int writePolicy,		// Write policy flag
	up_stream proc,			// user defined function
	INT64 beginpos,			// write start position
	INT64 nbytes,			// write size
	void* userval			// User-supplied value for callback
	)
{
	int ret = 0;

	if (!hshsdk)
	{
		return ret;
	}

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = SHCERRNO_NOERROR;

	if (psdk->bWorking) {
		psdk->nError = SHCERRNO_ISBUSY;
		psdk->sError = GetErrorMessage(psdk->nError);
		return ret;
	}

#ifdef WIN32
	psdk->hFile = NULL;
#else
	psdk->nFileDesc = -1;
#endif
	psdk->iFilePos  = 0;
	psdk->cOldTick  = clock();
	psdk->nTranStop = SHSTOP_NO;

	ret = DoUploaduffer(psdk, dstPath, writePolicy, proc, beginpos, nbytes, userval, 1); 

	if (!ret)
	{
		switch( sh_get_error_number(psdk) )
		{
		case 401:
			DoUploaduffer(psdk, dstPath, writePolicy, proc, beginpos, nbytes, userval, 0); 
			break;			
		case NE_LOOKUP:
			// 2011.06.20 : DNS lookup 실패 시 1회 재시도
			DoUploaduffer(psdk, dstPath, writePolicy, proc, beginpos, nbytes, userval, 1); 
			break;
		}
	}

	return ret;
}

WIN32DLL
int sh_upload(HSHSDK hshsdk, const char *srcPath, const char *dstPath, int writePolicy, INT64 availQuota, const char *extSession)
{
	if (!hshsdk)
	{
		return FALSE;
	}
	
	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = SHCERRNO_NOERROR;

	if (psdk->bWorking) {
		psdk->nError = SHCERRNO_ISBUSY;
		psdk->sError = GetErrorMessage(psdk->nError);
		return FALSE;
	}
#ifdef WIN32
	psdk->hFile = NULL;
#else
	psdk->nFileDesc = -1;
#endif
	psdk->iFilePos  = 0;
	psdk->cOldTick  = clock();
	psdk->nTranStop = SHSTOP_NO;

	int nRet = DoUpload(psdk, srcPath, dstPath, writePolicy, availQuota,1, extSession); 
	
	if (!nRet)
	{
		switch( sh_get_error_number(psdk) )
		{
		case 401:
			// 2011.06.02 : 401 에러 발생 경우 body를 모두 전송 후 리턴 되기 때문에 iFilePos를 초기화하여 시도하여함.
			psdk->iFilePos = 0;
			nRet = DoUpload(psdk, srcPath, dstPath, writePolicy, availQuota,0, extSession); 
			break;			
		case NE_LOOKUP:
			// 2011.06.20 : DNS lookup 실패 시 1회 재시도
			psdk->iFilePos = 0;
			nRet = DoUpload(psdk, srcPath, dstPath, writePolicy, availQuota,1, extSession); 
			break;
		}
	}

	return nRet;
}

WIN32DLL int sh_download_buffer(
	HSHSDK     hshsdk,      // SDK handle
	const char *srcPath,    // Source file
	down_stream proc,
	INT64 beginpos,
	void*	   userval		// User-supplied value for callback
	)
{
	if (!hshsdk)
		return FALSE;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = SHCERRNO_NOERROR;

	if (psdk->bWorking) {
		psdk->nError = SHCERRNO_ISBUSY;
		psdk->sError = GetErrorMessage(psdk->nError);
		return FALSE;
	}
	
	psdk->iFilePos  = 0;
	psdk->cOldTick  = clock();
	psdk->nTranStop = SHSTOP_NO;

	int nRet = DoDownloadBuffer(psdk, srcPath, proc, beginpos, -1, userval, 1);

	if(!nRet)
	{
		switch( sh_get_error_number(psdk) )
		{
		case 401:
			nRet = DoDownloadBuffer(psdk, srcPath, proc, beginpos, -1, userval,0);
			break;
		case NE_LOOKUP:
			// 2011.06.20 : DNS lookup 실패 시 1회 재시도
			nRet = DoDownloadBuffer(psdk, srcPath, proc, beginpos, -1, userval,1);
			break;
		}
	}
	return nRet;
}

// sh_download_buffer range version for GAMPLE
//
WIN32DLL 
int sh_download_buffer_r(
	HSHSDK     hshsdk,      // SDK handle
	const char *srcPath,    // Source file
	down_stream proc,		// user defined function
	INT64 beginpos,			// point to read
	INT64 nbytes,			// amount to read	
	void*	   userval		// User-supplied value for callback
	)
{
	
	if (!hshsdk)
		return FALSE;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = SHCERRNO_NOERROR;

	if (psdk->bWorking) {
		psdk->nError = SHCERRNO_ISBUSY;
		psdk->sError = GetErrorMessage(psdk->nError);
		return FALSE;
	}

	psdk->iFilePos  = 0;
	psdk->cOldTick  = clock();
	psdk->nTranStop = SHSTOP_NO;

	int nRet = DoDownloadBuffer(psdk, srcPath, proc, beginpos, nbytes, userval, 1);

	if(!nRet)
	{
		switch ( sh_get_error_number(psdk) )
		{
		case 401:
			nRet = DoDownloadBuffer(psdk, srcPath, proc, beginpos, nbytes, userval,0);
			break;
		case NE_LOOKUP:
			// 2011.06.20 : DNS lookup 실패 시 1회 재시도
			nRet = DoDownloadBuffer(psdk, srcPath, proc, beginpos, nbytes, userval, 1);
			break;
		}
	}
	return nRet;
}


WIN32DLL
int sh_download(HSHSDK hshsdk, const char *srcPath, const char *dstPath, int writePolicy, INT64 availQuota, const char *extSession)
{
	if (!hshsdk)
	{
		return FALSE;
	}

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = SHCERRNO_NOERROR;

	if (psdk->bWorking) {
		psdk->nError = SHCERRNO_ISBUSY;
		psdk->sError = GetErrorMessage(psdk->nError);
		return FALSE;
	}
#ifdef WIN32
	psdk->hFile = NULL;
#else
	psdk->nFileDesc = -1;
#endif	
	
	psdk->iFilePos  = 0;
	psdk->cOldTick  = clock();
	psdk->nTranStop = SHSTOP_NO;

//pizon
	int nRet = DoDownload(psdk, srcPath, dstPath, writePolicy, availQuota, 1, extSession);

	if(!nRet)
	{
		switch( sh_get_error_number(psdk) )
		{
		case 401:
			nRet = DoDownload(psdk, srcPath, dstPath, writePolicy, availQuota,0, extSession);
			break;
		case  NE_LOOKUP:
			// 2011.06.20 : DNS lookup 실패 시 1회 재시도
			nRet = DoDownload(psdk, srcPath, dstPath, writePolicy, availQuota, 1, extSession);
			break;
		}
	}
	return nRet;
}

WIN32DLL 
int sh_set_limit(HSHSDK hshsdk,int nLimit)
{
	if (!hshsdk)
		return FALSE;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;

	psdk->nSpeedLimit = nLimit;
	return TRUE;
}

WIN32DLL 
int sh_get_limit(HSHSDK hshsdk)
{
	if (!hshsdk)
		return FALSE;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;

	return psdk->nSpeedLimit;
}

WIN32DLL 
INT64 sh_get_filepos(HSHSDK hshsdk)
{
	if(!hshsdk)
		return FALSE;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;

	return psdk->iFilePos;
}

WIN32DLL 
bool sh_file_exist(
	HSHSDK		hshsdk,		// SDK Handle
	const char	*path)		// file/folder Path

{
	// 파일 목록 가져오기
	HSHFILELIST hshfl = sh_get_filelist(hshsdk, path, 0);
	
	if (!hshfl)
		return false;
	else
	{
		// 메모리 정리
		PVECTOR_SHFILELIST pvec = (PVECTOR_SHFILELIST)hshfl;
		if (pvec && pvec->size() > 0) {
			sh_free_filelist(hshsdk, (HSHFILELIST)pvec);
		}
		return true;
	}

}


#include "mcrypt.h"
#define ENCRYPT_KEY		"I'm your father"
#define INIT_VECTOR		"1234567890123456"
#define ENCRYPT_BIT		24

WIN32DLL
int sh_encrypt(const char *text, char *encrypted)
{
	MCRYPT mcrypt = NULL;
	BOOL bResult = FALSE;
	struct tm *stm;
	char szKey[ENCRYPT_BIT];
	char szIV[ENCRYPT_BIT];
	char szBuf[1024];
	time_t tnow;

	tnow = get_time_from_sntp();
	if(tnow == 0)
		goto EXIT_SH_ENCRYPT;

	stm = gmtime((time_t*)&tnow);

	// encrypt
	mcrypt = mcrypt_module_open(MCRYPT_BLOWFISH, NULL, MCRYPT_CBC, NULL);
	if (mcrypt == MCRYPT_FAILED)
		return FALSE;

	::memset(szKey, 0, ENCRYPT_BIT);
	strcpy(szKey, ENCRYPT_KEY);

	memcpy(szIV, INIT_VECTOR, ENCRYPT_BIT);

	if (mcrypt_generic_init(mcrypt, szKey, ENCRYPT_BIT, szIV) < 0)
		goto EXIT_SH_ENCRYPT;

	sprintf(szBuf, "%s%04d%02d%02d%02d%02d%02d", text, stm->tm_year, stm->tm_mon, stm->tm_mday, stm->tm_hour, stm->tm_min, stm->tm_sec);

#ifndef WIN32
	{
#endif
	int nBufSize = (int)strlen(szBuf);
	mcrypt_generic(mcrypt, szBuf, nBufSize);

	mcrypt_generic_deinit(mcrypt);

	char  szEncrypted[URL_SIZE];
	char* pszEncrypted   = ne_base64((const unsigned char *)szBuf, nBufSize);
	int   nEncryptedSize = (int)strlen(pszEncrypted);

	memcpy(szEncrypted, pszEncrypted, nEncryptedSize);
	free(pszEncrypted);

	memcpy(&szEncrypted[nEncryptedSize], szIV, ENCRYPT_BIT);
	szEncrypted[nEncryptedSize + ENCRYPT_BIT] = (char)NULL;

	strcpy(encrypted, URLEncode(szEncrypted).c_str());

	bResult = TRUE;
#ifndef WIN32
	}
#endif

EXIT_SH_ENCRYPT:
	mcrypt_module_close(mcrypt);

	return bResult;
}

void sh_set_sntp_timeout(int second)
{
	timeout = second;
}

void sh_set_C_timeout(int second)
{
	sh_set_sntp_timeout(second);
	sh_set_connect_timeout(second);
}

int sh_set_auth_expire(HSHSDK hshsdk,int ntime)
{
	if(!hshsdk)
		return FALSE;
	
	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;

	psdk->nAuthExpire = ntime;
	
	return TRUE;
}

int sh_get_auth_expire(HSHSDK hshsdk)
{
	if(!hshsdk)
		return FALSE;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;

	return psdk->nAuthExpire;
}

void sh_set_connect_timeout(int second)
{
	if( 0 < second )	second = 0;

	_connect_time_out = second;
}

void sh_set_response_timeout(int second)
{
	if( 0 < second )	second = 0;
	_read_time_out = second;
}

// NEW 2010-03-29
// 특정 시간(tStart) 이후에 업로드된 리스트를 얻어온다.
// 고객의 요청에 의해 해당 인터페이스 추가.
WIN32DLL
HSHFILELIST sh_get_filelist_certain_time(HSHSDK hshsdk, const char* path, time_t tStart, unsigned int depth)
{
	if (!hshsdk)
		return NULL;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;
	psdk->nError = 0;

	string sPath = psdk->sPath;
	if (path[0] != '/')
		sPath += "/";
	sPath += path;

	char szDepth[10];
	if (depth <= 1)
		sprintf(szDepth, "%d", depth);
	else
		strcpy(szDepth, "infinity");

	string sHeaders = "Depth|";
	sHeaders += szDepth;

	// sh_get_filelist_certain_time() 함수를 추가로 만들게 된 코드다.
	char szTime[20];
	memset(szTime, 0x00, 20);
	if (tStart > 0 )
	{
		// time * 1000000 을 하는 이유는 
		// 아파치에서 filetime을 구하는 식이 이미 정해져 있어서.
		// 아파치의 룰에 맞추기 위해서 적용한다.
		sprintf(szTime, "%lld", tStart * 1000000);
		sHeaders += ",";
		sHeaders += "StartTime|";
		sHeaders += szTime;
	}

	string sBody = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
	sBody += "<D:propfind xmlns:D=\"DAV:\">";
	sBody += "<D:prop>";
	sBody += "<D:resourcetype/>";
	sBody += "<D:getcontentlength/>";
	sBody += "<D:getlastmodified/>";
	sBody += "<D:source/>";
	sBody += "</D:prop>";
	sBody += "</D:propfind>";

	SHFILELIST_PARSER_STRUCT shflp;
	::memset(&shflp, 0, sizeof(SHFILELIST_PARSER_STRUCT));

	shflp.nRootPathLen = (UINT)psdk->sPath.length();

	shflp.pvec = new VECTOR_SHFILELIST;
	if (shflp.pvec) {
		if (DoCommand(psdk, "PROPFIND", sPath.c_str(), sHeaders.c_str(), sBody.c_str(), true,
			(LPVOID)FileListSTagParser, (LPVOID)FileListDataParser, (LPVOID)FileListETagParser, &shflp, 1)) 
		{
			psdk->setFileList.insert(shflp.pvec);
			psdk->pvecshfl = shflp.pvec;

			VECTOR_SHFILELIST::iterator iter = shflp.pvec->begin();
			if (strcmp((*iter)->path, "*myself*") == 0)
				strcpy((*iter)->path, sPath.substr(psdk->sPath.length(), string::npos).c_str());
		}
		else 
		{
			if (psdk->nError == SHCERRNO_AUTHORIZATION)
			{
				if (DoCommand(psdk, "PROPFIND", sPath.c_str(), sHeaders.c_str(), sBody.c_str(), true,
					(LPVOID)FileListSTagParser, (LPVOID)FileListDataParser, (LPVOID)FileListETagParser, &shflp,0))
				{
					psdk->setFileList.insert(shflp.pvec);
					psdk->pvecshfl = shflp.pvec;

					VECTOR_SHFILELIST::iterator iter = shflp.pvec->begin();
					if (strcmp((*iter)->path, "*myself*") == 0)
						strcpy((*iter)->path, sPath.substr(psdk->sPath.length(), string::npos).c_str());
				}
				else
				{
					FreeFileListVector(shflp.pvec);
					shflp.pvec = NULL;
				}
			}
			else
			{
				FreeFileListVector(shflp.pvec);
				shflp.pvec = NULL;
			}
		}
	}
	else {
		psdk->nError = GetLastError();
		psdk->sError = GetErrorMessage(psdk->nError);
	}

	sPath.clear();
	sBody.clear();
	return shflp.pvec;
}
// 전송 관련 callback 함수 등록
WIN32DLL 
int sh_set_transfer_callback (HSHSDK hshsdk, sh_callback proc, void  *param)
{
	if (!hshsdk)
		return 1;

	PSHCSDK_STRUCT psdk = (PSHCSDK_STRUCT)hshsdk;	
	psdk->proc   = proc;
	psdk->pParam = param;

	return 0;
}

WIN32DLL 
int sh_open (
	HSHSDK			hshsdk, // SDK handle 
	const char		*path  // Path
	)
{
	char ppath[2048] = {0};
	char *find = strrchr((char*)path, '/');
	strncpy(ppath, path, strlen(path) - strlen(find));

	int r =  sh_make_directory(hshsdk, ppath) ;
	if (r == 0)
	{
		if ( sh_get_error_number(hshsdk) == SHCERRNO_ALREADYEXIST )
			return true;
	}

	return r;
}

WIN32DLL 
int sh_send_append (
	HSHSDK			hshsdk, // SDK handle 
	const char *dstPath,    // Destination file
	up_stream proc,			// user defined function
	INT64 nbytes,			// write size
	void* userval			// User-supplied value for callback
	)
{
	return sh_upload_buffer_r(hshsdk, dstPath, SHWRITEPOLICY_APPEND, proc, -1, nbytes, userval);
}

WIN32DLL 
int sh_send_block (
	HSHSDK			hshsdk, // SDK handle 
	const char *dstPath,    // Destination file
	up_stream proc,			// user defined function
	INT64 beingpos,			// write start position
	INT64 nbytes,			// write size
	void* userval			// User-supplied value for callback
	)
{
	return sh_upload_buffer_r(hshsdk, dstPath, SHWRITEPOLICY_APPEND, proc, beingpos, nbytes, userval);
}
