#include "stdafx.h"
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WIN32
#include <io.h>
#endif // WIN32

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#ifdef WIN32
#pragma comment(lib, "wsock32.lib")
#ifdef USE_OVRIO
#pragma comment(lib, "ws2_32.lib")
#endif // USE_OVRIO
#define SEND_RECV_TYPE char
#define socklen_t int
#else // !WIN32
#ifdef SOLARIS
#define SEND_RECV_TYPE char
#else // !SOLARIS
#define SEND_RECV_TYPE void
#endif // SOLARIS
#ifdef SOLARIS
#define socklen_t int
#endif // SOLARIS

#define SD_RECEIVE SHUT_RD
#define SD_SEND SHUT_WR
#define SD_BOTH SHUT_RDWR

#define SOCKET_ERROR (-1)

#define _open open
#define _close close
#define _stat stat
#define _fstat fstat
#define _O_RDONLY O_RDONLY
#define _O_WRONLY O_WRONLY
#define _O_BINARY 0
#define _O_CREAT O_CREAT
#define _O_TRUNC O_TRUNC

#define WSAENOTSOCK ENOTSOCK // EBADF
#define WSAEWOULDBLOCK EWOULDBLOCK
#define WSAEMSGSIZE EMSGSIZE // send
#define WSAENOTCONN ENOTCONN
#define WSAEFAULT EFAULT
#define WSAEINPROGRESS EINPROGRESS
#define WSAENETDOWN ENETDOWN
#define WSAENETRESET ENETRESET
#define WSAECONNRESET ECONNRESET
#define WSAECONNABORTED ECONNABORTED
#define WSAEADDRINUSE EADDRINUSE
#define WSAETIMEDOUT ETIMEDOUT
#define WSAEHOSTUNREACH EHOSTUNREACH
#define WSAEACCES EACCES
#define WSAEOPNOTSUPP EOPNOTSUPP
#define WSAEINVAL EINVAL
#define WSAESHUTDOWN ESHUTDOWN

#define WSANOTINITIALISED (-10011)
#define WSANO_RECOVERY (-1003)
#define WSAEDISCON (-1020)

#define GetACP() 949
#define SO_MAX_MSG_SIZE 100
typedef hostent*    LPHOSTENT;
typedef in_addr*    LPIN_ADDR;
#endif // WIN32

//getpeername,getsockname,gethostbyaddr,gethostbyname

BEGIN_PACKET_MAP_RAW(CSOC, (_SPacketMap*)NULL)
    PACKET_MAP_ENTRY(CSOC, X_KEEPALIVE, OnKeepAlive)
END_PACKET_MAP(CSOC)

BEGIN_PACKET_MAP(CSOCConnect, CSOC)
    PACKET_MAP_ENTRY(CSOCConnect, X_TEXT, OnText)
    PACKET_MAP_ENTRY(CSOCConnect, X_TEXTFMT, OnTextFmt)
    PACKET_MAP_ENTRY(CSOCConnect, C_FTP, OnFTP)
    PACKET_MAP_ENTRY(CSOCConnect, S_FTP, OnFTP)
END_PACKET_MAP(CSOCConnect)

BEGIN_PACKET_MAP(CSOCClient, CSOCConnect)
END_PACKET_MAP(CSOCClient)

BEGIN_PACKET_MAP(CSOCServer, CSOCConnect)
END_PACKET_MAP(CSOCServer)

//#ifdef SR_TRACE
#ifdef TRACE
#undef TRACE
#endif // TRACE
#if 0 // def _DEBUG
static void __cdecl __DbgTrace(const char* pcszFormat, ...)
{/*
    va_list ap;

    va_start(ap, pcszFormat);
    char sz[256];

    vsprintf(sz, pcszFormat, ap);

    va_end(ap);

    ::OutputDebugString(sz);*/
}
#define TRACE __DbgTrace
#else // !_DEBUG
static inline void __cdecl __RelTraceIgnore(const char* pcszFormat, ...) {}
#define TRACE              1 ? (void)0 : __RelTraceIgnore
#endif // _DEBUG
//#endif // SR_TRACE

static const struct timeval g_tSelectTimeOut = {0L, 0L}; // t.tv_sec, t.tv_usec
static struct linger g_opt_ling = { 0, 0 }; // ling.l_onoff, ling.l_linger

char SERVERID[] = "";

#ifdef _DEBUG
static const char* EEXCP2MSG(EException eException);
#else // !_DEBUG
#define EEXCP2MSG(x) ""
#endif // _DEBUG
static int __cdecl compare_Timer(const void *elem1, const void *elem2);
static long timer_ConnectCheck(CTimerEvent& Timer);
static int __cdecl qsort_TEXTFmt(const void* elem1, const void* elem2);
static int __cdecl bsearch_TEXTFmt(const void* pcvKey, const void* elem);

#ifdef _DEBUG
static const char* EEXCP2MSG(EException eException)
{
    const char* s_pcszaMSG[] = {
        "EEXCP_NoError",

        // local side...
        "EEXCP_TooLowVersion",
        "EEXCP_Unknown",
        "EEXCP_ConnectionTimeOut",
        "EEXCP_ConnectionFailure",
        "EEXCP_SendBufferOverflow",
        "EEXCP_VersionError",

        // fatal socket error...
        "EEXCP_WSAENETDOWN",
        "EEXCP_WSANO_RECOVERY",
        "EEXCP_WSAENETRESET",
        "EEXCP_WSAECONNRESET",
        "EEXCP_WSAECONNABORTED",
        "EEXCP_WSAEADDRINUSE",
        "EEXCP_WSAETIMEDOUT",
        "EEXCP_WSAEHOSTUNREACH",
        "EEXCP_WSAEACCES",

        // internal error
        "EEXCP_WSANOTINITIALISED",
        "EEXCP_WSAEFAULT",
        "EEXCP_WSAENOTSOCK",
        "EEXCP_WSAENOTCONN",
        "EEXCP_WSAEOPNOTSUPP",
        "EEXCP_WSAEINVAL",
        "EEXCP_DupCreation",
    };

    return s_pcszaMSG[eException];
}
#endif // _DEBUG

#if !defined(SOLARIS)
inline WORD GetWORD(const BYTE* pbPacket) { return ntohs(UPKV(pbPacket)->m_w); }
inline DWORD GetDWORD(const BYTE* pbPacket) { return ntohl(UPKV(pbPacket)->m_dw); }
inline float GetFLOAT(const BYTE* pbPacket) { return UPKV(pbPacket)->m_f; }
inline double GetDOUBLE(const BYTE* pbPacket) { return UPKV(pbPacket)->m_dbl; }
inline SINT64 GetI64(const BYTE* pbPacket) { return UPKV(pbPacket)->m_i64; }
inline void PutWORD(BYTE* pbPacket, WORD w) { UPKV(pbPacket)->m_w = htons(w); }
inline void PutDWORD(BYTE* pbPacket, DWORD dw) { UPKV(pbPacket)->m_dw = htonl(dw); }
inline void PutFLOAT(BYTE* pbPacket, float f) { UPKV(pbPacket)->m_f = f; }
inline void PutDOUBLE(BYTE* pbPacket, double dbl) { UPKV(pbPacket)->m_dbl = dbl; }
inline void PutI64(BYTE* pbPacket, SINT64 i64) { UPKV(pbPacket)->m_i64 = i64; }
#else // defined(SOLARIS)
inline WORD GetWORD(const BYTE* pbPacket) { WORD w; memcpy(&w, &UPKV(pbPacket)->m_w, sizeof(w)); return ntohs(w); }
inline DWORD GetDWORD(const BYTE* pbPacket) { DWORD dw; memcpy(&dw, &UPKV(pbPacket)->m_dw, sizeof(dw)); return ntohl(dw); }
inline float GetFLOAT(const BYTE* pbPacket) { float f; memcpy(&f, &UPKV(pbPacket)->m_f, sizeof(f)); return f; }
inline double GetDOUBLE(const BYTE* pbPacket) { double dbl; memcpy(&dbl, &UPKV(pbPacket)->m_dbl, sizeof(dbl)); return dbl; }
inline SINT64 GetI64(const BYTE* pbPacket) { SINT64 i64; memcpy(&i64, &UPKV(pbPacket)->m_i64, sizeof(i64)); return i64; }
inline void PutWORD(BYTE* pbPacket, WORD w) { w = htons(w); memcpy(&UPKV(pbPacket)->m_w, &w, sizeof(w)); }
inline void PutDWORD(BYTE* pbPacket, DWORD dw) { dw = htonl(dw); memcpy(&UPKV(pbPacket)->m_dw, &dw, sizeof(dw)); }
inline void PutFLOAT(BYTE* pbPacket, float f) { memcpy(&UPKV(pbPacket)->m_f, &f, sizeof(f)); }
inline void PutDOUBLE(BYTE* pbPacket, double dbl) { memcpy(&UPKV(pbPacket)->m_dbl, &dbl, sizeof(dbl)); }
inline void PutI64(BYTE* pbPacket, SINT64 i64) { memcpy(&UPKV(pbPacket)->m_i64, &i64, sizeof(i64)); }
#endif // defined(SOLARIS)

static int __cdecl compare_Timer(const void *elem1, const void *elem2)
{
HEAPCHK();
    DWORD dwTime1 = ((const CTimerEvent*)elem1)->m_dwTime;
    DWORD dwTime2 = ((const CTimerEvent*)elem2)->m_dwTime;
    if (dwTime1 != dwTime2) {
        if (dwTime1 < dwTime2)
            return -1;
        return +1;
    }
HEAPCHK();
    return 0;
}

static long timer_ConnectCheck(CTimerEvent& Timer)
{
    //TRACE("Connect Timeout : %x\n", Timer.m_pVoid);

    ((PSOCServer)Timer.m_pVoid)->CallOnConnectionTimeOut();
	return -1; // already deleted...
}

static int __cdecl qsort_TEXTFmt(const void* elem1, const void* elem2)
{
    WORD wFmt1 = ((const CTEXTFmt*)elem1)->m_DW.m_wFmt, wFmt2 = ((const CTEXTFmt*)elem2)->m_DW.m_wFmt;

    if (wFmt1 != wFmt2) {
        if (wFmt1 > wFmt2) {
            return +1;
        } else {
            return -1;
        }
    }

    return 0;
}

static int __cdecl bsearch_TEXTFmt(const void* pcvKey, const void* elem)
{
    WORD wFmt1 = *(const WORD*)pcvKey;
    WORD wFmt2 = ((const CTEXTFmt*)elem)->m_DW.m_wFmt;

    if (wFmt1 != wFmt2) {
        if (wFmt1 > wFmt2) {
            return +1;
        } else {
            return -1;
        }
    }

    return 0;
}

const char* SkipWord(const char* pcsz)
{
    ASSERT(pcsz && ((*pcsz != '\0') && (*pcsz != ' ') && (*pcsz != '\t')));
    const char* pcszOrg = pcsz;
    char c;

    while((c = *++pcsz) != '\0') {
        if ((c == '\t') || (c == ' '))
            break;
    }

    return pcsz;
}

const char* SkipWhite(const char* pcsz)
{
    ASSERT(pcsz);
    char c;

    while((c = *pcsz) != '\0') {
        if ((c != '\t') && (c != ' '))
            return pcsz;
        pcsz++;
    }

    return NULL;
}

const char* SkipWhiteReverse(const char* pcsz)
{
    ASSERT(pcsz);

    const char* pcszEnd = &pcsz[strlen(pcsz)];
    char c;

    while(pcszEnd > pcsz) {
        if (((c = pcszEnd[-1]) != ' ') && (c != '\t'))
            break;
    }

    return pcszEnd;
}

BOOL IsControlExist(const char* pcsz)
{
    BYTE c;

    while((c = *(const BYTE*)pcsz++) != '\0') {
        if (c < 0x20) {
            return TRUE;
        }
    }

    return FALSE;
}

BOOL IsAllDigit(const char* pcsz)
{
    BYTE c;

    while((c = *(const BYTE*)pcsz++) != '\0') {
        if (!(('0' <= c) && (c <= '9'))) {
            return FALSE;
        }
    }

    return TRUE;
}

BOOL CheckStringOK(const char* pcsz, int nMin, int nMax)
{
    if ((nMin <= strlen(pcsz)) || (strlen(pcsz) <= nMax)) {
        if (!IsControlExist(pcsz))
            return TRUE;
    }

    return FALSE;
}

BOOL CheckNumberOK(const char* pcsz, int nMin, int nMax)
{
    if (CheckStringOK(pcsz, nMin, nMax)) {
        if (IsAllDigit(pcsz))
            return TRUE;
    }
    return FALSE;
}

BOOL IsInvalidCharsExist(const char* pcsz, const char* pcszInvalidChars)
{
    ASSERT(pcsz && pcszInvalidChars);
    char c;

    while((c = *pcsz++) != '\0') {
        if (strchr(pcszInvalidChars, c)) {
            return TRUE;
        }
    }
    return FALSE;
}

char* cmd_GetString(char*& psz, int nQuoteType/* = 0*/)
{
    // nQuoteType: 0 - no quote, 1 - single, 2 - double, 3: single & double...
    ASSERT((0 <= nQuoteType) && (nQuoteType <= 3));

    char* psz2 = psz;

    psz2 = (char*)SkipWhite(psz2);
    if (psz2) {
        char* pszStart = psz2;
        char cStart = *pszStart;

        switch(cStart) {
        case '\'':
            if (!((nQuoteType == 1) || (nQuoteType == 3)))
                nQuoteType = 0;
            break;
        case '"':
            if (!((nQuoteType == 2) || (nQuoteType == 3)))
                nQuoteType = 0;
            break;
        default:
            nQuoteType = 0;
            break;
        }
        if (nQuoteType) {
            pszStart++;
            psz2++;

            char c;
            while((c = *psz2) != '\0') {
                if (c == cStart) {
                    if (((c = psz2[1]) == ' ') || (c == '\t')) {
                        *psz2 = '\0';
                        psz2 += 2;
                        break;
                    }
                }
                psz2++;
            }
        } else {
            psz2 = (char*)SkipWord(psz2);
            if (*psz2) {
                *psz2++ = '\0';
            }
        }
        psz = psz2;

        return pszStart;
    }

    return NULL;
}

int cmd_GetNumber(char*& psz, int& nNumber, int nMin/* = 0*/, int nMax/* = 65536*/)
{
    ASSERT((INT_MIN <= nMin) && (nMax < INT_MAX) && (nMin < nMax));
    BOOL bNegative = FALSE;
    BOOL bSignExist = FALSE;
    char* psz2;
    
    while((psz2 = cmd_GetString(psz)) != NULL) {
        char c;

        while((c = *psz2) != '\0') {
            if ((c == '-') || (c == '+')) {
                if (nMin < 0) {
                    if (c == '-')
                        bNegative = !bNegative;
                    bSignExist = TRUE;
                } else {
                    return -3; // sign exist...
                }
            } else {
                break;
            }
            psz2++;
        }
        if (c == '\0')
            continue;
        if (IsAllDigit(psz2)) {
            while((c = *psz2) == '0')
                psz2++;
            SINT64 ll = 0;
            if (c != '\0') {
                while((c = *psz2) != '\0') {
                    ll *= 10;
                    ll += c - '0';
                    psz2++;
                    if (ll >= INT_MAX) {
                        return -2;
                    }
                }
            }
            if (bNegative)
                ll = -ll;
            if ((nMin <= ll) && (ll < nMax)) {
                nNumber = ll;
                return +1;
            }
            return -2; // range error.
        } else {
            return -1; // non digit char(s) exist...
        }
    }
    if (bSignExist)
        return -3; // sign exist but no number...
    return 0; // no number...
}

CTEXTFmt::CTEXTFmt(const char* pcszFormat/* = NULL*/)
    : CStr(pcszFormat)
{
    m_DW.m_wFmt = 0;
    m_DW.m_wTX = TX_SYSTEM;
}

CTEXTFmtArray::CTEXTFmtArray()
{
}

CTEXTFmtArray::~CTEXTFmtArray()
{
}

int CTEXTFmtArray::LoadFmts(const char* pcszFile, const char* pcszSignature, BOOL bCheckACP/* = FALSE*/)
{
    CStr strFile;

    if (bCheckACP) {
        UINT uACP = GetACP();
        strFile.Format(pcszFile, uACP); // ...cclnt%u.fmt

        if (access(strFile, 0) == -1) {
            if (uACP != 1252) {
                strFile.Format(pcszFile, 1252); // ...cclnt%u.fmt
                if (access(strFile, 0) == -1) {
                    return 0;
                } else {
                    uACP = 1252;
                }
            } else {
                return 0;
            }
        }
        pcszFile = strFile;
    }
    int nSizeOld = GetSize();
    int nSize = nSizeOld;

    FILE* pFile = fopen(pcszFile, "rt");
    if (pFile) {
        BYTE baLine[512];
        BYTE* pbEnd = &baLine[sizeof(baLine) - 1];
        BYTE* pb = &baLine[0];

        while(!feof(pFile)) {
            int c = getc(pFile);
            if ((c == EOF) || (c == '\0')) {
                break;
            }
            if (c < 0x20) {
                if ((c == '\n') || (c == '\r')) {
                    *pb = '\0';
                    pb = &baLine[0];
                    switch(ProcessOneLine(baLine, pcszSignature, nSizeOld, nSize)) {
                    default: //case 0:
                        break;
                    case +1:
                        nSize++;
                        break;
                    case -1:
                        // assume: none TEXTFmt file?...
                        goto out_before_close;
                    }
                }
                pcszSignature = NULL;
            } else {
                if (pb < pbEnd) {
                    *pb++ = (BYTE)c;
                } else {
                    // assume: none TEXTFmt file?...
                    goto out_before_close;
                }
            }
        }
        if (pb != &baLine[0]) {
            if (ProcessOneLine(baLine, pcszSignature, nSizeOld, nSize) == +1) {
                nSize++;
            }
        }
out_before_close:
        fclose(pFile);
    }
    FreeExtra();

    if ((nSize -= nSizeOld) > 0) {
        Sort();
    }

    return nSize;
}

CTEXTFmt& CTEXTFmtArray::FindTEXTFmt(WORD wFmt) const
{
    return *(CTEXTFmt*)bsearch(&wFmt, &GetAt(0), GetSize(), sizeof(CTEXTFmt), bsearch_TEXTFmt);
}

CTEXTFmtArray::CAtoms* CTEXTFmtArray::FindAtoms(const char* pcsz) const
{
    const TARRAYPTR(CAtoms*)& paAtoms = m_paAtoms;
    int i, nSize = paAtoms.GetSize();

    for(i =0; i < nSize; i++) {
        CAtoms* pAtoms = paAtoms.At(i);
        if (strcmp(pAtoms->m_strTitle, pcsz) == 0)
            return pAtoms;
    }

    return NULL;
}

void CTEXTFmtArray::Sort()
{
    qsort(&GetAt(0), GetSize(), sizeof(CTEXTFmt), qsort_TEXTFmt);
}

int CTEXTFmtArray::ProcessOneLine(BYTE baLine[], const char* pcszSignature, int nSizeOld, int nSize)
{
    BYTE* pb = &baLine[0];
    BYTE b;

    pb = (BYTE*)SkipWhite((const char*)pb);
    if (pb && (pb[0] != ';')) { // is comment?
        BYTE* pbEnd = (BYTE*)SkipWhiteReverse((const char*)pb);
        *pbEnd = '\0';
        if (pcszSignature) {
            // check signature
            if (lstrcmpi((const char*)pb, pcszSignature) == 0) {
                return 0;
            } else {
                return -1;
            }
        } else {
            BYTE* p = (BYTE*)strchr((const char*)pb, '=');
            if (p) {
                *p = '\0';
                BYTE* pbStart = &p[1];
                p = (BYTE*)SkipWhiteReverse((const char*)p);

                WORD wFmt = 0;
                BOOL bFmt = FALSE;
                BYTE* pbAtoms = pb;
                while((b = *pb) == '0') {
                    pb++;
                }
                if (CheckNumberOK((const char*)pb, 1, 5)) {
                    int nATOI = atoi((const char*)pb);
                    if ((0 <= nATOI) && (nATOI < 65536)) {
                        bFmt = TRUE;
                        wFmt = (WORD)nATOI;
                    }
                }

                pbStart = (BYTE*)SkipWhite((const char*)pbStart);
                if (pbStart) {
                    if (*pbStart == '"') {
                        pbStart++;
                    }
                    if ((pbStart < pbEnd) && (pbEnd[-1] == '"'))
                        pbEnd[-1] = '\0';
                } else {
                    pbStart = (BYTE*)"";
                }
                if (bFmt) {
                    CTEXTFmt* pTEXTFmt = &FindOnLoad(wFmt, nSizeOld, nSize);
                    if (!pTEXTFmt) {
                        pTEXTFmt = &Add();
                        pTEXTFmt->m_DW.m_wFmt = wFmt;
                        CTEXTFmtArray::ProcessEscape(pbStart);
                        pTEXTFmt->Assign((const char*)pbStart);
                        return +1;
                    } else {
                        // default - don't overwrite duplicated entry...
                        /*
                        if (bOverWriteIfExist) {
                            // same value...: pTEXTFmt->m_DW.m_wFmt = (WORD)n;
                            CTEXTFmtArray::ProcessEscape(pbStart);
                            pTEXTFmt->Assign((const char*)pbStart);
                            return 0;
                        } else {
                            // don't overwrite...
                            return 0;
                        }
                        */
                    }
                } else {
                    // Atom...
                    CAtoms* pAtoms = FindAtoms((const char*)pbAtoms);
                    if (!pAtoms) {
                        pAtoms = new CAtoms((const char*)pbAtoms);
                        m_paAtoms.Add() = pAtoms;
                    }
                    CTEXTFmtArray::ProcessEscape(pbStart);
                    pAtoms->Add() = (const char*)pbStart;
                }
            }
        }
    }

    return 0;
}

CTEXTFmt& CTEXTFmtArray::FindOnLoad(WORD wFmt, int nSizeOld, int nSize) const
{
    CTEXTFmt* pTEXTFmt = (CTEXTFmt*)bsearch(&wFmt, &GetAt(0), nSizeOld, sizeof(CTEXTFmt), bsearch_TEXTFmt);
    if (pTEXTFmt) {
        return *pTEXTFmt;
    }
    pTEXTFmt = &GetAt(nSizeOld);
    while(nSizeOld < nSize) {
        if (pTEXTFmt->m_DW.m_wFmt == wFmt) {
            return *pTEXTFmt;
        }
        pTEXTFmt++;
        nSizeOld++;
    }
    return *(CTEXTFmt*)NULL;
}

void CTEXTFmtArray::ProcessEscape(BYTE* pb)
{
    BYTE b;

    while((b = *pb) != '\0') {
        if (b == '\\') {
            switch(pb[1]) {
            case 'r':
                *pb = '\r';
                strcpy((char*)&pb[1], (const char*)&pb[2]);
                break;
            case 't':
                *pb = '\t';
                strcpy((char*)&pb[1], (const char*)&pb[2]);
                break;
            case 'n':
                *pb = '\n';
                strcpy((char*)&pb[1], (const char*)&pb[2]);
                break;
            case '\0':
                break;
            default:
                pb++;
                break;
            }
        }
        pb++;
    }
}

void Randomize()
{
    srand(time(0));
}

DWORD CDAPacket::Push() const
{
    return m_wLen;
}

void CDAPacket::Pop(DWORD dw)
{
    ASSERT(dw >= m_wLen);
    m_pbPacket -= dw - m_wLen;
    m_wLen = (WORD)dw;
}

BYTE* CDAPacket::GetData(int nSize)
{
    if (nSize <= m_wLen) {
        BYTE* pb = m_pbPacket;
        m_wLen -= nSize;
        m_pbPacket += nSize;

        return pb;
    } else {
        CSOC* pSOC = IsSOC();
        if (pSOC) {
            pSOC->Drop();
        }

        return NULL;
    }
}

BYTE* __cdecl CDAPacket::Disassemble(const char *pcszFormat, ...)
{
    va_list ap;

    va_start(ap, pcszFormat);

    BYTE* pb = vDisassemble(pcszFormat, ap);

    va_end(ap);

    return pb;
}

BYTE* __cdecl CDAPacket::vDisassemble(const char* pcszFormat, va_list ap)
{
#ifdef NO_PDCD
#define PACKET_DUMP_CHECK_DISASSEMBLE(SizeInBYTE) pbNext = &pbPacket[SizeInBYTE];
#else // !NO_PDCD
#define PACKET_DUMP_CHECK_DISASSEMBLE(SizeInBYTE) if ((pbNext = &pbPacket[SizeInBYTE]) > pbPacketEnd) { TRACE("Buffer boundary failure... in vDisssemble() %d\n", __LINE__); goto as_failure; }
#endif // NO_PDCD
    BYTE* pbPacket = m_pbPacket;
    BYTE* pbPacketEnd = &pbPacket[m_wLen];
    BYTE* pbNext;
    BYTE b;

    while((b = *pcszFormat++) != '\0') {
		switch(b) {
		case 'b':
        case 'B':
            PACKET_DUMP_CHECK_DISASSEMBLE(sizeof(BYTE));
			*va_arg(ap, BYTE*) = *pbPacket;
			break;
		case 'w':
        case 'W':
            PACKET_DUMP_CHECK_DISASSEMBLE(sizeof(WORD));
			*va_arg(ap, WORD*) = GetWORD(pbPacket);
			break;
        case 'n':
		case 'd':
        case 'D':
            PACKET_DUMP_CHECK_DISASSEMBLE(sizeof(DWORD));
			*va_arg(ap, DWORD*) = GetDWORD(pbPacket);
			break;
        case 'f':
            PACKET_DUMP_CHECK_DISASSEMBLE(sizeof(float));
			*va_arg(ap, float*) = GetFLOAT(pbPacket);
            break;
        case 'F':
            PACKET_DUMP_CHECK_DISASSEMBLE(sizeof(double));
			*va_arg(ap, double*) = GetDOUBLE(pbPacket);
            break;
        case '6':
            PACKET_DUMP_CHECK_DISASSEMBLE(sizeof(SINT64));
			*va_arg(ap, SINT64*) = GetI64(pbPacket);
            break;
		case 's':
        case '$':
		  {
            WORD wLen = strlen((const char*)pbPacket) + 1;
            PACKET_DUMP_CHECK_DISASSEMBLE(wLen);
            if (b == 's')
			    *va_arg(ap, char**) = (char*)pbPacket;
            else
                *va_arg(ap, CStr*) = (const char*)pbPacket;
			break;
		  }
		case 'S':
	      {
            PACKET_DUMP_CHECK_DISASSEMBLE(sizeof(WORD));
			WORD wLen = GetWORD(pbPacket);

            if (wLen > 0) {
			    pbPacket = pbNext;
                PACKET_DUMP_CHECK_DISASSEMBLE(wLen);
			    *va_arg(ap, char**) = (char*)pbPacket;
            } else {
                *va_arg(ap, char**) = (char*)NULL;
            }
			break;
          }
		case 'x':
	      {
            PACKET_DUMP_CHECK_DISASSEMBLE(sizeof(WORD));
			WORD wLen = GetWORD(pbPacket);

            if (wLen > 0) {
			    pbPacket = pbNext;
                PACKET_DUMP_CHECK_DISASSEMBLE(wLen);
			    *va_arg(ap, const BYTE**) = (BYTE*)pbPacket;
            } else {
                *va_arg(ap, const BYTE**) = (BYTE*)NULL;
            }
            *va_arg(ap, WORD*) = wLen;
			break;
          }
		case 'X':
	      {
            *va_arg(ap, const BYTE**) = (BYTE*)pbPacket;
            WORD wLen = va_arg(ap, int); // WORD wLen = va_arg(ap, WORD);

            PACKET_DUMP_CHECK_DISASSEMBLE(wLen);
			break;
          }
        case '!':
          {
            CPacketDumper* pDumper = va_arg(ap, CPacketDumper*);
            pbNext = pDumper->Disassemble(pbPacket, pbPacketEnd);
            if (!pbNext) {
                goto as_failure;
            }
            ASSERT((m_pbPacket <= pbNext) && (pbNext <= pbPacketEnd));
            break;
          }
		default:
            TRACE("Invalid vDisassemble Char %c\n", (char)b);
            ASSERT(FALSE);
as_failure:
            //fprintf(stderr, "vDisassemble Char %c\n", (char)b);
          {
            CSOC* pSOC = IsSOC();
            if (pSOC) {
                pSOC->Drop();
            }
          }
			return FALSE;
		}
        pbPacket = pbNext;
    }

    m_wLen = pbPacketEnd - pbPacket;

    return m_pbPacket = pbPacket;
#undef PACKET_DUMP_CHECK_DISASSEMBLE
}

CTimerPool::CTimerPool()
{
    m_nReEnter = 0;
    m_bDirty = FALSE;
    m_dwPrecision = 10;
    m_dwLastTicks = 0;
}

void CTimerPool::CheckTimers()
{
    if (m_nReEnter == 0) {
        m_nReEnter++;

        int nSize = GetSize();
        if (nSize > 0) {
            DWORD dwTicks = GetTickCount();
//fprintf(stderr, "1 --- %d <? %d\n", m_dwLastTicks + m_dwPrecision, dwTicks);
            if (m_dwLastTicks + m_dwPrecision < dwTicks) {
                SetLastTicks(dwTicks);
                if (m_bDirty) {
                    HEAPCHK();
                    qsort(&GetAt(0), nSize, sizeof(CTimerEvent), compare_Timer);
                    HEAPCHK();
                    FreeExtra();
                    HEAPCHK();
                    m_bDirty = FALSE;
                }
                DWORD dwTime = GetTickCount();

                HEAPCHK();
                int i = 0;
                while(i < nSize) {
                    CTimerEvent& Timer = GetAt(i);
//fprintf(stderr, "1 --- %d <? %d\n", Timer.m_dwTime, dwTime);
                    if (Timer.m_dwTime < dwTime) {
                        long lRet = (*Timer.m_pfnTimerProc)(Timer);
                        if (lRet >= 0) { // stay...
                            if (lRet == 0) {
                                Timer.m_dwTime += GetTickCount();
                            } else {
                                Timer.m_dwTime = GetTickCount() + lRet;
                            }
                            m_bDirty = TRUE;
                            i++;
                            break;
                        } else {
                            if (lRet == -1) { // already deleted...
                                nSize--;
                            } else { // delete this...
                                HEAPCHK();
                                Del(i);
                                HEAPCHK();
                                nSize--;
                            }
                        }
                    } else {
                        break;
                    }
                }
                HEAPCHK();
            }
        }

        m_nReEnter--;
    }
}

CTimerEvent& CTimerPool::AddTimer(PFN_TimerProc pfnTimerProc, DWORD dwTime, void* pVoid)
{
HEAPCHK();
    CTimerEvent& Timer = Add();
    Timer.m_pfnTimerProc = pfnTimerProc;
    Timer.m_dwTime = dwTime + GetTickCount();
    Timer.m_pVoid = pVoid;
    Timer.m_dwFrame = 0;
    //Timer.m_nParam = 0; // not initialized...
    //Timer.m_dwParam = 0; // not initialized...
    m_bDirty = TRUE;
HEAPCHK();

    return Timer;
}

BOOL CTimerPool::DelTimer(int nIndex)
{
HEAPCHK();
    int nSize = GetSize();
    if ((0 <= nIndex) && (nIndex <nSize)) {
        Del(nIndex);
        return TRUE;
    }
    return FALSE;
}

BOOL CTimerPool::DelTimers(PFN_TimerProc pfnTimerProc, void* pVoid, UINT uFlags, int nFromIndex/* = 0*/)
{
    //CHK_TIMER_NOVOIDCHECK
HEAPCHK();
    int nSize = GetSize();
    if (!pfnTimerProc) {
        if (nSize) {
            Reset();
            return TRUE;
        }
        return FALSE;
    }
HEAPCHK();
    int i;
    if (nFromIndex < 0)
        i = 0;
    else
        i = nFromIndex;

    BOOL bDeleted = FALSE;
    while(i < nSize) {
        CTimerEvent& Timer = GetAt(i);
        if ((Timer.m_pfnTimerProc == pfnTimerProc) && ((uFlags & CHK_TIMER_NOVOIDCHECK) || (Timer.m_pVoid == pVoid))) {
HEAPCHK();
            Del(i);
HEAPCHK();
            nSize--;
            if (uFlags & CHK_TIMER_ONCE)
                return TRUE;
            bDeleted = TRUE;
        } else {
            i++;
        }
    }
HEAPCHK();

    return bDeleted;
}

int CTimerPool::FindTimer(PFN_TimerProc pfnTimerProc, void* pVoid, UINT uFlags, int nFromIndex/* = 0*/)
{
    //CHK_TIMER__NOVOIDCHECK
    //CHK_TIMER_ONCE
HEAPCHK();
    int i;
    if (nFromIndex < 0)
        i = 0;
    else
        i = nFromIndex;

    int nSize = GetSize();
    if (!pfnTimerProc) {
        if (i < nSize) {
            return i;
        }
        return -1;
    }
HEAPCHK();
    while(i < nSize) {
        CTimerEvent& Timer = GetAt(i);
        if ((Timer.m_pfnTimerProc == pfnTimerProc) && ((uFlags & CHK_TIMER_NOVOIDCHECK) || (Timer.m_pVoid == pVoid))) {
            return i;
        } else {
            i++;
        }
    }
HEAPCHK();

    return -1;
}

CPacket::CPacket(CSOC* pSOC)
    : m_pSOC(pSOC)
{
    m_wLen = 0;
    ASSERT(pSOC);
}

CPacket::~CPacket()
{
}

void CPacket::Reset()
{
    SetLength(0);
}

BYTE* __cdecl CPacket::Assemble(const char *pcszFormat, ...)
{
    BYTE* pbEnd;

    va_list ap;

    va_start(ap, pcszFormat);

    pbEnd = vAssemble(pcszFormat, ap);

    va_end(ap);

    return pbEnd;
}

BYTE* __cdecl CPacket::vAssemble(const char *pcszFormat, va_list ap)
{
#ifdef NO_PDCA
#define PACKET_DUMP_CHECK_ASSEMBLE(SizeInBYTE) pbNext = &pbPacket[SizeInBYTE];
#else // !NO_PDCA
#define PACKET_DUMP_CHECK_ASSEMBLE(SizeInBYTE) if ((pbNext = &pbPacket[SizeInBYTE]) > pbPacketEnd) { TRACE("Buffer boundary failure... in vAssemble() %d\n", __LINE__); goto as_failure; }
#endif // NO_PDCA

    BYTE* pbPacket = GetLastPtr();
    BYTE* pbPacketEnd = GetEndPtr();
    BYTE* pbNext;
    BYTE b;

    BYTE* pbPacketOld = pbPacket;

    while((b = *pcszFormat++) != '\0') {
		switch(b) {
		case 'b':
        case 'B':
            PACKET_DUMP_CHECK_ASSEMBLE(sizeof(BYTE));
			*pbPacket = va_arg(ap, int); // *pbPacket = va_arg(ap, BYTE);
			break;
		case 'w':
        case 'W':
            PACKET_DUMP_CHECK_ASSEMBLE(sizeof(WORD));
			PutWORD(pbPacket, va_arg(ap, int)); // PutWORD(pbPacket, va_arg(ap, WORD));
			break;
        case 'n':
		case 'd':
        case 'D':
            PACKET_DUMP_CHECK_ASSEMBLE(sizeof(DWORD));
			PutDWORD(pbPacket, va_arg(ap, DWORD));
			break;
		case 'f':
          {
            PACKET_DUMP_CHECK_ASSEMBLE(sizeof(float));
            DWORD dwFloat;
            dwFloat = va_arg(ap, DWORD);
			PutFLOAT(pbPacket, *(float*)&dwFloat); // PutFLOAT(pbPacket, va_arg(ap, float));
			break;
          }
		case 'F':
            PACKET_DUMP_CHECK_ASSEMBLE(sizeof(double));
			PutDOUBLE(pbPacket, va_arg(ap, double));
			break;
		case '6':
            PACKET_DUMP_CHECK_ASSEMBLE(sizeof(SINT64));
			PutI64(pbPacket, va_arg(ap, SINT64));
			break;
		case 's':
          {
			const char* pcStr = va_arg(ap, const char*);
            ASSERT(pcStr);

			WORD wLen = strlen(pcStr) + 1;
            PACKET_DUMP_CHECK_ASSEMBLE(wLen);
			memcpy(pbPacket, (const char*)pcStr, wLen);
			break;
          }
		case 'S':
          {
            WORD wLen;

			const char* pcStr = va_arg(ap, const char*);
            if (pcStr) {
			    wLen = strlen(pcStr) + 1;
                PACKET_DUMP_CHECK_ASSEMBLE(sizeof(WORD) + wLen);
			    PutWORD(pbPacket, wLen);
			    memcpy(&pbPacket[sizeof(WORD)], (const char*)pcStr, wLen);
            } else {
                PACKET_DUMP_CHECK_ASSEMBLE(sizeof(WORD));
                wLen = 0;
			    PutWORD(pbPacket, wLen);
            }
			break;
		  }
        case 'x': // binary data
          {
            const BYTE* pb = va_arg(ap, const BYTE*);
            ASSERT(pb);
            const WORD wLen = va_arg(ap, int); // va_arg(ap, WORD);

            PACKET_DUMP_CHECK_ASSEMBLE(sizeof(WORD) + wLen);
			PutWORD(pbPacket, wLen);
            if (wLen > 0) {
                memcpy(&pbPacket[sizeof(WORD)], pb, wLen);
            }
            break;
          }
        case 'X': // binary data, sub-packet copy...
          {
            const BYTE* pb = va_arg(ap, const BYTE*);
            const WORD wLen = va_arg(ap, int); // va_arg(ap, WORD);

            PACKET_DUMP_CHECK_ASSEMBLE(wLen);
            if (wLen > 0) {
                ASSERT(pb);
                memcpy(pbPacket, pb, wLen);
            }

            break;
          }
        case '!':
          {
            CPacketDumper* pDumper = va_arg(ap, CPacketDumper*);
            pbNext = pDumper->Assemble(pbPacket, pbPacketEnd);
            if (!pbNext) {
                goto as_failure;
            }
            ASSERT((pbPacketOld <= pbNext) && (pbNext <= pbPacketEnd));
            break;
          }
		default:
            TRACE("Invalid vAssemble Char %c\n", (char)b);
            ASSERT(FALSE);
as_failure:
            DropSOC();
            return NULL;
		}
        pbPacket = pbNext;
    }
    Prepare(pbPacket - pbPacketOld);

    return pbPacket;
#undef PACKET_DUMP_CHECK_ASSEMBLE
}

BYTE* CPacket::DisassembleParamsAndTextFmt(CSOCConnect& SOC, WORD wFmt, const char* pcszParamType, WORD& wTX)
{
    const CTEXTFmt& Fmt = SOC.GetTEXTFmt(wFmt);
    BYTE* pbPacket = GetLastPtr();
    if (&Fmt) {
        wTX = Fmt.m_DW.m_wTX;
        UFmtParam aFPs[STRF_MAX_PARAMS];

#if STRF_MAX_PARAMS==3
        if (SOC.Disassemble(pcszParamType, &aFPs[0], &aFPs[1], &aFPs[2]))
#elif STRF_MAX_PARAMS==7
        if (SOC.Disassemble(pcszParamType, &aFPs[0], &aFPs[1], &aFPs[2], &aFPs[3], &aFPs[4], &aFPs[5], &aFPs[6]))
#else // error
#error STRF_MAX_PARAMS size must be 3 or 7...
#endif
        {
            if (DoTextFmt(Fmt, aFPs, pcszParamType)) {
                return pbPacket;
            }
        }
    } else {
        wTX = TX_UNKNOWN;
        BYTE* pbPacketEnd = GetEndPtr();

        char szBuf[36];
        int nLen = sprintf(szBuf, "TEXTFmt Missing: %d", (int)wFmt) + 1;
        if (nLen <= (pbPacketEnd - pbPacket)) {
            Prepare(nLen);
            return pbPacket;
        } else {
            DropSOC();
        }
    }

    return NULL;
}

void CPacket::DropSOC()
{
    CSOC& SOC = GetSOC();
    if (&SOC) {
        SOC.Drop();
    }
}

BYTE* CPacket::DoTextFmt(const char* pcszFormat, const UFmtParam aFPs[STRF_MAX_PARAMS], const char caParamType[/*STRF_MAX_PARAMS + 1*/])
{
#ifdef NO_PDCA
#define PACKET_DUMP_CHECK_ASSEMBLE(SizeInBYTE) pbNext = &pbPacket[SizeInBYTE];
#else // !NO_PDCA
#define PACKET_DUMP_CHECK_ASSEMBLE(SizeInBYTE) if ((pbNext = &pbPacket[SizeInBYTE]) > pbPacketEnd) { TRACE("Buffer boundary failure... in DoTextFmt() %d\n", __LINE__); goto as_failure; }
#endif // NO_PDCA

    BYTE* pbPacket = GetLastPtr();
    BYTE* pbPacketEnd = GetEndPtr();
    BYTE* pbNext;
    BYTE b;

    BYTE* pbPacketOld = pbPacket;
    WORD wParams = strlen(&caParamType[0]);
    char szBuf[36];
    char* pszEnd;
    int nLen;
    const UFmtParam* pFP = NULL;

    while((b = *pcszFormat++) != '\0') {
        if (b == '%') {
            b = *pcszFormat++;
            if (('0' <= b) && (b < ('0' + wParams))) {
                b -= '0';
                pFP = &aFPs[b];
                switch(caParamType[b]) {
		        case 'b':
                    PACKET_DUMP_CHECK_ASSEMBLE(sizeof(BYTE));
                    *pbPacket = pFP->m_c; // char...
			        break;
                case 'B':
                    nLen = sprintf(szBuf, "%u", (int)pFP->m_b);
check_szBuf:
                    PACKET_DUMP_CHECK_ASSEMBLE(nLen);
                    if (nLen > 0) {
                        memcpy(pbPacket, &szBuf[0], nLen);
                    }
			        break;
		        case 'w':
                    nLen = sprintf(szBuf, "%d", (int)pFP->m_w);
			        goto check_szBuf;
                case 'W':
                    nLen = sprintf(szBuf, "%d", (int)pFP->m_s);
			        goto check_szBuf;
		        case 'n':
                    nLen = sprintf(szBuf, "%d", pFP->m_n);
			        goto check_szBuf;
		        case 'd':
                    nLen = sprintf(szBuf, "%ld", pFP->m_l);
			        goto check_szBuf;
                case 'D':
                    nLen = sprintf(szBuf, "%lu", pFP->m_dw);
			        goto check_szBuf;
                case 'f':
                    nLen = sprintf(szBuf, "%f", pFP->m_f);
			        goto check_szBuf;
                case 'F':
                    nLen = sprintf(szBuf, "%lf", pFP->m_F);
			        goto check_szBuf;
                case '6':
                    nLen = sprintf(szBuf, "%I64", pFP->m_i64);
			        goto check_szBuf;
		        case 's':
		        case 'S':
                    nLen = strlen(pFP->m_psz);
                    PACKET_DUMP_CHECK_ASSEMBLE(nLen);
                    if (nLen > 0) {
                        memcpy(pbPacket, pFP->m_psz, nLen);
                    }
			        break;
                default:
                    TRACE("Invalid DoTextFmt Char %c\n", (char)b);
                    ASSERT(FALSE);
as_failure:
                    DropSOC();
                    return NULL;
                }
            } else {
                if (b != '%') {
                    PACKET_DUMP_CHECK_ASSEMBLE(sizeof(BYTE) * 2);
                    pbPacket[0] = '%';
                    pbPacket[1] = b;
                } else {
                    PACKET_DUMP_CHECK_ASSEMBLE(sizeof(BYTE));
                    pbPacket[0] = '%';
                }
            }
        } else {
            PACKET_DUMP_CHECK_ASSEMBLE(sizeof(BYTE));
            pbPacket[0] = b;
        }
        pbPacket = pbNext;
    }
    PACKET_DUMP_CHECK_ASSEMBLE(sizeof(BYTE));
    *pbPacket++ = '\0';

    Prepare(pbPacket - pbPacketOld);

    return pbPacket;
#undef PACKET_DUMP_CHECK_ASSEMBLE
}

CSockAddr::CSockAddr(int nPort, const char* pcszAddr/* = NULL*/, short sFamily/* = AF_INET*/)
{
    SetTarget(pcszAddr);
	SetPort(nPort);
	sin_family = sFamily;
    memset(&sin_zero[0], 0, sizeof(sin_zero));
}

CSockAddr::CSockAddr(int nPort, u_long ulAddr/* = INADDR_ANY*/, short sFamily/* = AF_INET*/)
{
    SetTarget(ulAddr);
	SetPort(nPort);
	sin_family = sFamily;
    memset(&sin_zero[0], 0, sizeof(sin_zero));
}

const char* CSockAddr::GetTarget() const
{
    return m_strTarget;
}

void CSockAddr::SetTarget(const char* pcszAddr)
{
    if (pcszAddr) {
        sin_addr.s_addr = inet_addr(pcszAddr);
        if (sin_addr.s_addr == INADDR_ANY) { // 0.0.0.0, HOST/Network Addr irrelevant
            hostent* pHN = gethostbyname(pcszAddr);
            if (pHN) {
                sin_addr.s_addr = *(u_long*)pHN->h_addr;
            }
        }
    } else {
        sin_addr.s_addr = INADDR_ANY;
    }
    m_strTarget = pcszAddr;
}

void CSockAddr::SetTarget(u_long ulAddr)
{
#if 1 // def WIN32
	sin_addr.s_addr = htonl(ulAddr);
#else // !WIN32
    sin_addr = inet_makeaddr(ulAddr, 0);
#endif // WIN32
    m_strTarget = inet_ntoa(sin_addr);
}

int CSockAddr::GetPort() const
{
    return ntohs(sin_port);
}

void CSockAddr::SetPort(int nPort)
{
    sin_port = htons((short)nPort);
}

const char* GetHostAddress()
{
	char hostname[256];

    if (gethostname(hostname, sizeof(hostname)) == 0) {
        struct in_addr in;

        in.s_addr = inet_addr(hostname);
        if (in.s_addr == INADDR_NONE) {
	        struct hostent* hp;

            hp = gethostbyname(hostname);
            if (hp) {
                struct in_addr** bptr;

                bptr = (struct in_addr**)hp->h_addr_list;
                if (bptr && *bptr) {
                    in = **bptr;
                }
            }
        }

        if (in.s_addr != INADDR_NONE) {
            return inet_ntoa(in);
        }
    }

    return "127.0.0.1";
}

PF_PacketProcessor _SPacketMap::TraceAndModifyBaseCall(BYTE bCmd, PF_PacketProcessor& refPFPP) const
{
    ASSERT(this);
    ASSERT(&refPFPP);
    ASSERT(refPFPP == PFPP_CallBase);

    register _SPacketMap* pBaseMap = m_pBaseMap;

    if (pBaseMap) {
        ASSERT(pBaseMap->m_pPFPP);
        PF_PacketProcessor& refPFPP2 = pBaseMap->m_pPFPP[bCmd];
        PF_PacketProcessor PFPP = refPFPP2;
        if (PFPP == PFPP_CallBase) {
            PFPP = pBaseMap->TraceAndModifyBaseCall(bCmd, refPFPP2);
            ASSERT(PFPP != PFPP_CallBase);
        }
        refPFPP = PFPP;
        return PFPP;
    } else {
        refPFPP = &PFPP_Ignore;
        return &PFPP_Ignore;
    }
}

CFileTransfer::CFileTransfer(DWORD dwFTID, DWORD dwGUARD, const char* pcszFileNameRemote, const char* pcszFileNameLocal)
    : m_dwFTID(dwFTID), m_dwGUARD(dwGUARD), m_strFileNameRemote(pcszFileNameRemote), m_strFileNameLocal(pcszFileNameLocal)
{
    if (dwFTID == 0) {
        m_dwFTID = (long)this;
        m_bClientSite = FALSE;
    } else {
        m_bClientSite = TRUE;
    }
    m_dwTime = (DWORD)-1;
    m_lSize = (DWORD)-1;

    m_uMinBPS = 0;
    m_uMaxBPS = (DWORD)-1;
    m_nFile = -1;
    m_lPos = 0;
    m_dwStartTime = 0;
}

CFileTransfer::~CFileTransfer()
{
    Close();
}

BOOL CFileTransfer::Setup(DWORD& dwError)
{
    ASSERT(!m_bClientSite);
    ASSERT((m_dwTime == (DWORD)-1) && (m_lSize == (DWORD)-1) && (m_nFile == -1));

    int nFile = _open(m_strFileNameLocal, _O_RDONLY | _O_BINARY);
    if (nFile != -1) {
        struct _stat st;

        if (_fstat(nFile, &st) == 0) {
            m_dwTime = st.st_mtime;
            m_lSize = (long)st.st_size;
            m_nFile = nFile;

            return TRUE;
        } else {
            _close(nFile);
            nFile = -1;
            dwError = (DWORD)-2;
        }
    } else {
        dwError = (DWORD)-1;
    }
    
    return FALSE;
}

BOOL CFileTransfer::Setup(DWORD dwTime, long lSize, DWORD& dwError)
{
    ASSERT(m_bClientSite);
    ASSERT((m_dwTime == (DWORD)-1) && (m_lSize == (DWORD)-1) && (m_nFile == -1));

    m_nFile = _open(m_strFileNameLocal, _O_WRONLY | _O_BINARY | _O_CREAT | _O_TRUNC);
    if (m_nFile != -1) {
        m_dwTime = dwTime;
        m_lSize = lSize;
        return TRUE;
    } else {
        dwError = (DWORD)-1;
        return FALSE;
    }
}

int CFileTransfer::CheckReadPosition(long lPosition) const
{
    ASSERT((lPosition >= 0) && this && !m_bClientSite);
    if (lPosition == m_lPos) {
        if (lPosition == m_lSize) {
            return 0; // end of file...
        } else {
            return +1; // more data to read.
        }
    } else {
        return -1; // error
    }
}

int CFileTransfer::Close()
{
    int nFile = m_nFile;

    if (nFile != -1) {
        m_nFile = -1;
        if (_close(nFile) != 0) {
            nFile = -2;
        }
    }

    return nFile;
}

void CFileTransfer::Start()
{
    if (!m_dwStartTime) {
        m_dwStartTime = GetTickCount();
    }
}

CFTPArray::CFTPArray()
{
    SetGrowBy(20);
}

CFTPArray::~CFTPArray()
{
}

int CFTPArray::Find(CFileTransfer* pFTP) const
{
    int i, Size = GetSize();

    for(i = 0; i < Size; i++) {
        if (At(i) == pFTP)
            return i;
    }

    return -1;
}

CFileTransfer* CFTPArray::CutIfExist(CFileTransfer* pFTP)
{
    int nIndex = Find(pFTP);
    if (nIndex != -1) {
        Del(nIndex);
        return pFTP;
    }

    return NULL;
}
#ifdef USE_OVRIO
COVRIO::COVRIO()
{
    m_pSOC = NULL;
    m_nSize = 0;
    //m_ba[/*m_nSize*/];
}

COVRIO::~COVRIO()
{
}

void CALLBACK OVRIO_Completion(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
    COVRIO* pOVRIO = (COVRIO*)lpOverlapped;

    if (pOVRIO) {
        (pOVRIO->m_pSOC)->OnOVRIO_Completion(dwError, cbTransferred, pOVRIO, dwFlags);
    }
}
#endif // USE_OVRIO
CSOC::CSOC()
    : m_Addr(0, (u_long)0)
{
    m_nLockCount = 0;
    m_hSocket = INVALID_SOCKET;
    m_nState = 0;
	m_bSendLoop = TRUE;
    m_pSOCManager = NULL;
    /*
    m_DW.m_xAdded = FALSE;
    */
    m_dwKASndTicks = m_dwKARcvTicks = 0;
    m_dwKARcvTimeOut = 0;

#ifdef WIN32
    m_hWndOwner = NULL;
#endif // WIN32
    m_ppSOC = NULL;
    m_ID.m_uID = CSOC_SERVER_ID;

    m_dwDroppingTicks = 0;

#ifdef USE_OVRIO
    m_pOVRIOStart = NULL;
#endif // USE_OVRIO
    // ...
    m_dwVarFlags = 0;
    m_DW.m_xAutoDelete = 1;
}

CSOC::~CSOC()
{
    Reset();
}

void CSOC::Reset()
{
    DoClose();

    if (m_ppSOC) {
        ASSERT((*m_ppSOC == this) || (*m_ppSOC == NULL));
        *m_ppSOC = NULL;
    }

#ifdef USE_OVRIO
    ASSERT(!m_pOVRIOStart);
    m_pOVRIOStart = NULL;
#endif // USE_OVRIO

    m_dwKASndTicks = m_dwKARcvTicks = 0;
    m_dwDroppingTicks = 0;

    m_DW.m_xAdded = FALSE;
    m_DW.m_xIsConnected = FALSE;
    m_DW.m_xIsConnecting = FALSE;
    m_DW.m_xDoDelSocket = FALSE;
    m_DW.m_xIsDropping = FALSE;
    m_DW.m_xIsSetupFailure = FALSE;
    m_DW.m_xGracefulLocal = FALSE;
    m_DW.m_xErrSEND = FALSE;
    m_DW.m_xErrRECEIVE = FALSE;
    m_DW.m_xGracefulRemote = FALSE;
    m_DW.m_xAbortyLocal = FALSE;
    m_DW.m_xAbortyRemote = FALSE;
    m_DW.m_xLoginStarted = FALSE;
    m_DW.m_xLoginOK = FALSE;
}

int CSOC::GetSignatureFromPort(int nPort) const
{
    return nPort ^ 0x5a5a5a5a;
}

int CSOC::GetConnectionState() const
{
    if (IsValidSafe()) {
        if (IsDroppingState())
            return -1;
        else if (IsConnecting())
            return -2;
        else if (IsConnected())
            return +1;
        else {
            // Time Out?
            return -3; // intermediate state 
        }
    }

    return 0;
}

void CSOC::Drop()
{
//fprintf(stderr, "CSDOC::Drop()\n");
    m_pSOCManager->OnDrop(this);
}

BOOL CSOC::DoSend(const BYTE* pbBuf, int nSize)
{
    ASSERT(pbBuf && (nSize > 0));
    ASSERT(!m_DW.m_xIsConnecting);
    ASSERT(!IsListenSocket());
    if (nSize <= MAX_PACKET) {
        if (IsSendPossible()) {
#ifdef USE_OVRIO
            int nRet;
            
            /*nRet = send(m_hSocket, (const char*)pbBuf, nSize, 0);
            if (nRet != SOCKET_ERROR) {
                ASSERT(nRet >= 0);
                pbBuf = &pbBuf[nRet];
                nSize -= nRet;
            } else {
                // error
                int nErr = WSAGetLastError();

                if ((nErr != WSAEWOULDBLOCK) && (nErr != WSAEINPROGRESS))
                    DoErrorReport(nErr, TRUE);
            }*/
            if (!IsDroppingState()) {
                if (nSize > 0) {
                    COVRIO* pOVRIO = newCOVRIO(pbBuf, nSize);
                    if (pOVRIO) {
                        DWORD dwSent;
                        WSABUF wsabuf = { nSize, (char*)&pOVRIO->m_ba[0] };
                        do {
                            while((nRet = WSASend(m_hSocket, &wsabuf, 1, &dwSent, 0, pOVRIO, OVRIO_Completion)) == 0) {
                                nSize -= dwSent;
                                wsabuf.buf += dwSent;
                                wsabuf.len = nSize;
                                pOVRIO->m_nSize = nSize;
                                if (nSize <= 0) {
                                    //OnOVRIO_Completion(0, dwSent, pOVRIO, 0);
                                    return TRUE;
                                }
                            }
                            ASSERT(nRet == SOCKET_ERROR);
                        } while(((nRet = WSAGetLastError()) == WSAEWOULDBLOCK) || (nRet == WSAEINPROGRESS));
                        if (nRet == WSA_IO_PENDING) {
                            return TRUE;
                        } else {
                            DoErrorReport(nRet, TRUE);
                        }
                    } else {
                        Drop();
                    }
                } else {
                    ASSERT(nSize == 0);
                    return TRUE;
                }
            }
            return FALSE;
#else // !USE_OVRIO
	        int nMax = m_aSnd.GetMax();
		    BYTE* pDest;

		    if ((pDest = m_aSnd.Prepare(nSize)) == NULL) {
			    int nRetry = 0;
			    DWORD dwTimeBegin = GetTickCount();

			    do {
				    if (m_bSendLoop) {
					    DWORD dwSendTimeOut = GetSendTimeOut();
					    DWORD dwTime = dwTimeBegin + dwSendTimeOut;
					    int nFSOrg = m_aSnd.GetFreeSize();

					    while(nFSOrg < nSize) {
						    int nDS = OnSend();
						    if (IsDroppingState())
							    return FALSE;
						    int nFS = nMax - nDS;
						    DWORD dw = GetTickCount();
						    if (nFSOrg != nFS) {
							    dwTime = dw + dwSendTimeOut;
							    nFSOrg = nFS;
						    }
						    if (dwTime <= dw) {
							    goto noSendLoop;
						    }
					    }
				    } else {
noSendLoop:
					    if (!OnOverflow(nSize, nRetry, dwTimeBegin)) {
						    TRACE("SendBuffer overflow %d > %d\n", nSize, m_aSnd.GetFreeSize());
						    if (!IsDroppingState())
							    DoExceptions(EEXCP_SendBufferOverflow, TRUE);
						    return FALSE;
					    }
				    }
				    nRetry++;
			    } while((pDest = m_aSnd.Prepare(nSize)) == NULL);
		    }
            ASSERT(pDest);
            memcpy(pDest, pbBuf, nSize);

		    return TRUE;
#endif // USE_OVRIO
        }
    } else {
        ASSERT(FALSE);
		TRACE("Too large packet to send %d > MAX_PACKET:%d\n", nSize, MAX_PACKET);
		Drop();
    }

    return FALSE;
}

BOOL CSOC::DoSendCmdWOP(BYTE bCmd)
{
    // WOP means With Out Param(s)
    BYTE ba[sizeof(WORD) + 1/* bCmd */];

    PutWORD(&ba[0], 1);
    ba[sizeof(WORD)] = bCmd;

    return DoSend(&ba[0], sizeof(WORD) + 1/* bCmd */);
}

void CSOC::DoSendLoginOK()
{
    if (!IsDroppingState()) {
        DoSendX("BB", S_LOGN_RSLT, CSCODE_OK);
    }
}

void CSOC::DoSendLoginError()
{
    if (!IsDroppingState()) {
        DoSendX("BB", S_LOGN_RSLT, CSCODE_ERR);
        Drop();
    }
}

void CSOC::DoSendLoginError(WORD wErrFMT)
{
    if (!IsDroppingState()) {
        DoSendX("BBW", S_LOGN_RSLT, CSCODE_ERR, wErrFMT);
        Drop();
    }
}

BOOL CSOC::DoSend(const CPacket& Packet)
{
    ASSERT(&Packet.GetSOC() == this);
    WORD wLen = Packet.GetLength();

    BYTE* pw = (BYTE*)Packet.GetPacketStart();
    PutWORD(pw, wLen);

    BOOL bRet = DoSend(pw, wLen + sizeof(WORD));

    *(WORD*)pw = wLen;

    return bRet;
}

BOOL CSOC::DoSendX(BYTE bCmd, const void* pData, int nLen)
{
	ASSERT(nLen > 0);
    BYTE ba[MAX_PACKET];
    PutWORD(&ba[0], (WORD)(nLen + 1));
    ba[sizeof(WORD)] = bCmd;
    memcpy(&ba[sizeof(WORD) + 1], pData, nLen);

    return DoSend(&ba[0], nLen + 1);
}

BOOL __cdecl CSOC::DoSendX(const char *pcszFormat, ...)
{
    va_list ap;

    va_start(ap, pcszFormat);

    CPacket Packet(this);
    Packet.vAssemble(pcszFormat, ap);

    va_end(ap);

    return DoSend(Packet);
}

BOOL CSOC::DoSendText(BYTE bCmd, const char* pcsz)
{
	return DoSendX(bCmd, pcsz, strlen(pcsz) + 1/* '\0' */);
}

BOOL CSOC::DoSendText(const char* pcszID, const char* pcsz, WORD wTX/* = TX_NORMAL*/)
{
    return DoSendX("BWss", X_TEXT, wTX, pcszID, pcsz);
}

BOOL __cdecl CSOC::DoSendFmt(WORD wFmt, const char* pcszParamType/* = NULL*/, ...)
{
    BOOL bRet;

    va_list ap;

    va_start(ap, pcszParamType);
    bRet = vDoSendFmt(wFmt, pcszParamType, ap);
    va_end(ap);

    return bRet;
}

BOOL CSOC::vDoSendFmt(WORD wFmt, const char* pcszParamType, va_list ap)
{
    if (!pcszParamType)
        pcszParamType = "";
    else {
        ASSERT(strlen(pcszParamType) <= STRF_MAX_PARAMS );
    }
    CPacket Packet(this);
    Packet.Assemble("BWs", X_TEXTFMT, wFmt, pcszParamType);
    Packet.vAssemble(pcszParamType, ap);

    return DoSend(Packet);
}

const CTEXTFmtArray& CSOC::GetTEXTFmtArray()
{
    return *(const CTEXTFmtArray*)NULL;
}

const CTEXTFmt& CSOC::GetTEXTFmt(WORD wFmt)
{
    const CTEXTFmtArray& TEXTFmtArray = GetTEXTFmtArray();

    if (&TEXTFmtArray) {
        return TEXTFmtArray.FindTEXTFmt(wFmt);
    }

    return *(const CTEXTFmt*)NULL;
}

BOOL CSOC::DoProcessPacket()
{
    int nSize;
    BYTE* pbHdr;
	
	if ((nSize = m_aRcv.GetBegin(pbHdr)) > 0) {
		if (nSize >= (sizeof(WORD) + sizeof(BYTE))) {
			WORD wLenOrg = GetWORD(pbHdr);
			WORD wLen = wLenOrg + sizeof(WORD);
			if (wLen <= nSize) {
				m_pbPacket = &pbHdr[sizeof(WORD)];
				m_bCmd = *m_pbPacket++;
				m_wLen = wLenOrg - 1;

                //TRACE("bCmd: %d, wLen: %d\n", m_bCmd, wLenOrg);
				DoPacketProcessing();
				m_aRcv.RemoveBegin(wLen);

				return TRUE;
			} else {
			}
		} else {
		}
	}

	return FALSE;
}

void CSOC::OnOpen()
{
}

BOOL CSOC::OnSetupSocket()
{
    int err;

	err = setsockopt(m_hSocket, SOL_SOCKET, SO_LINGER, (const char*)&g_opt_ling, sizeof(g_opt_ling));
    if (err != SOCKET_ERROR) {
#ifdef WIN32
        static u_long s_ulOPT = 1;
	    err = ioctl(m_hSocket, FIONBIO, &s_ulOPT);
        if (err == 0) { // don't use SOCKET_ERROR
            return TRUE;
        }
#else // !WIN32
	    int flags = fcntl(m_hSocket, F_GETFL);
        flags |= O_NONBLOCK;
	    err = fcntl(m_hSocket, F_SETFL, flags);
        if (err != -1) { // don't use SOCKET_ERROR
            return TRUE;
        }
#endif /* WIN32 */
    }
    int nErr = WSAGetLastError();

    if ((nErr != WSAEWOULDBLOCK) && (nErr != WSAEINPROGRESS))
        DoErrorReport(nErr);

    return FALSE;
}

void CSOC::OnAddSocket()
{
}

void CSOC::OnDropping()
{
}

void CSOC::OnDelSocket()
{
}

void CSOC::OnShutdown()
{
}

void CSOC::OnClose()
{
}

void CSOC::OnDelete()
{
}

void CSOC::OnSendKeepAlive()
{
    DoSendCmdWOP(X_KEEPALIVE);
}
#if !defined(USE_OVRIO)
BOOL CSOC::OnOverflow(int nSizeReq, int nRetry, DWORD dwTimeBegin)
{
	return FALSE;
}
#endif // !defined(USE_OVRIO)
void CSOC::OnIgnore()
{
    TRACE("Ignored Packet - Check!!!: %d, wLen: %d\n", (int)m_bCmd, (int)m_wLen);
}

void CSOC::OnKeepAlive()
{
    ResetKeepAliveRcv();
}
#ifdef USE_OVRIO
COVRIO* CSOC::newCOVRIO(const BYTE* pb, int nSize)
{
    COVRIO* pOVRIO = (COVRIO*)malloc(sizeof(COVRIO) + nSize);

    if (pOVRIO) {
        pOVRIO->newCOVRIO();
        WSAOVERLAPPED& OVRIO = *pOVRIO;
        memset(&OVRIO, 0, sizeof(WSAOVERLAPPED));
        pOVRIO->m_pSOC = this;
        pOVRIO->m_nSize = nSize;
        memcpy(&pOVRIO->m_ba[0], pb, nSize);

        pOVRIO->AddTail(m_pOVRIOStart);
    }

    return pOVRIO;
}

void CSOC::deleteCOVRIO(COVRIO* pOVRIO)
{
    if (pOVRIO) {
        pOVRIO->CutCheck(m_pOVRIOStart);
        pOVRIO->deleteCOVRIO();
        free(pOVRIO);
    }
}

void CSOC::OnOVRIO_Completion(DWORD dwError, DWORD cbTransferred, COVRIO* pOVRIO, DWORD dwFlags)
{
    if (this) {
        deleteCOVRIO(pOVRIO);
    }
}
#endif // USE_OVRIO
int CSOC::aSnd_GetDataSize()
{
#ifdef USE_OVRIO
    int nSize = 0;
    COVRIO* pOVRIO = m_pOVRIOStart;
    while(pOVRIO) {
        nSize += pOVRIO->m_nSize;
        pOVRIO = pOVRIO->m_pNext;
    }
    return nSize;
#else // !USE_OVRIO
    return m_aSnd.GetDataSize();
#endif // USE_OVRIO
}

BOOL CSOC::DoClose()
{
    if (m_hSocket != INVALID_SOCKET) {
        closesocket(m_hSocket);
        m_hSocket = INVALID_SOCKET;
#if defined(USE_OVRIO)
        SleepEx(0, TRUE);
#endif // defined(USE_OVRIO)
        return TRUE;
    }
    return FALSE;
}

void CSOC::CheckAutoDelete()
{
    if (m_DW.m_xAutoDelete)
        delete this;
    else
        Reset();
}

void CSOC::SetDroppingTicks()
{
    m_dwDroppingTicks = GetTickCount();
}

BOOL CSOC::CreateAndAttach()
{
    if (!IsValid()) {
        SOCKET soc;
#ifdef USE_OVRIO
        if (IsListenSocket()) {
            soc = socket(AF_INET, SOCK_STREAM, 0);
        } else {
            soc = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
        }
#else // !USE_OVRIO
        soc = socket(AF_INET, SOCK_STREAM, 0);
#endif // USE_OVRIO
        if (soc != INVALID_SOCKET) {
            CallOnOpen(soc);
            return TRUE;
        } else {
            int nErr = WSAGetLastError();

            if ((nErr != WSAEWOULDBLOCK) && (nErr != WSAEINPROGRESS))
                DoErrorReport(nErr);
        }
    } else {
        DoExceptions(EEXCP_DupCreation);
    }

    return FALSE;
}

void CSOC::CallOnOpen(SOCKET soc)
{
    m_hSocket = soc;
    OnOpen();
}

void CSOC::CallOnDropping()
{
    m_DW.m_xIsDropping = TRUE;
    if (IsListenSocket()) {
        ((CSOCListen*)this)->SetAllowAccept(FALSE);
    }
    OnDropping();
}

void CSOC::CallOnShutdown(int nHow)
{
    if (!m_DW.m_xGracefulLocal) {
        TRACE("m_DW.m_xGracefulLocal STARTED case NORMAL\n");
        m_DW.m_xGracefulLocal = 1;
        if (IsValid())
            shutdown(*this, nHow);
        m_DW.m_xErrSEND = TRUE;
        OnShutdown();
    }
}

void CSOC::CallOnClose()
{
    if (!(GetType3() & SOCNEXT_EXCLISTEN)) {
        ((CSOCConnect*)this)->DoFTPCloseAllAsError();
    }
    if (DoClose()) {
        OnClose();
    }
}

void CSOC::CallOnDelete()
{
    OnDelete();
    CheckAutoDelete();
}

void CSOC::DoPacketProcessing()
{
    _SPacketMap* pMap = GetPacketMap();
    if (pMap) {
        ASSERT(pMap->m_pPFPP);
        PF_PacketProcessor& refPFPP = pMap->m_pPFPP[m_bCmd];
        PF_PacketProcessor PFPP = refPFPP;
        if (PFPP == PFPP_CallBase) {
            PFPP = pMap->TraceAndModifyBaseCall(m_bCmd, refPFPP);
            ASSERT(PFPP != PFPP_CallBase);
        }
        (this->*PFPP)();
    } else {
        TRACE("No PacketMap - Check!!!: %x\n", this);
        // do nothing...
    }
}

BOOL CSOC::OnReceive()
{
    BOOL bPossible;

    Lock();

    bPossible = IsReceivePossible();
    if (bPossible) {
        BYTE baBuf[MAX_PACKET];
        int nRet;
        int nTotalRead = 0;

        while((nRet = DoReceive(baBuf, sizeof(baBuf))) > 0) {
            nTotalRead += nRet;
            break;
        }
        if (nTotalRead > 0) {
            ResetKeepAliveRcv();
        }
    }
	while(DoProcessPacket())
        ;
    if (!bPossible) {
        m_aRcv.FreeAll();
        TRACE("OnReceive is not possible\n");
        Drop();
    }
    Unlock();

    return bPossible;
}

int CSOC::DoReceive(BYTE* pbBuf, int nLen)
{
    int nSize = m_aRcv.GetFreeSize();
    if (nSize <= 0) {
        // buffer overflow...
        return 0;
    }
    if (nSize < nLen) {
        nLen = nSize;
    }

    nLen = recv(m_hSocket, (char*)pbBuf, nLen, 0);

    if (nLen != SOCKET_ERROR) {
        if (m_DW.m_xIsConnecting) {
            TRACE("Connecting From DoReceive: %x\n", this);
            ((CSOCServer*)this)->CallOnConnect();
        }
        if (nLen > 0) {
            BYTE* pDest = m_aRcv.Prepare(nLen);
            //ASSERT(pDest);
            memcpy(pDest, pbBuf, nLen);
        } else if (nLen == 0) {
            m_DW.m_xGracefulRemote = 1;
        }
    } else {
        // error
        int nErr = WSAGetLastError();
        if ((nErr != WSAEWOULDBLOCK) && (nErr != WSAEINPROGRESS)
#if !defined(WIN32)
            && (nErr != EAGAIN)
#endif // !defined(WIN32)
            )
            DoErrorReport(nErr);
        nLen = 0;
    }

    return nLen;
}

int CSOC::OnSend()
{
    if (IsSendPossible()) {
#ifdef USE_OVRIO
        return aSnd_GetDataSize();
#else // !USE_OVRIO
        BYTE* pbBuf;
        int nSize, nRet;
        if ((nSize = m_aSnd.GetBegin(pbBuf)) > 0) {
            const BYTE* pbBufOrg = pbBuf;
            while(nSize > 0) {
                nRet = send(m_hSocket, (const char*)pbBuf, nSize, 0);
                if (nRet != SOCKET_ERROR) {
				    if (nRet <= 0)
					    break;
                    ASSERT(!m_DW.m_xIsConnecting);
                    m_aSnd.RemoveBegin(nRet);
                    pbBuf += nRet;
                    nSize -= nRet;
                } else {
                    // error
                    int nErr = WSAGetLastError();

                    if ((nErr != WSAEWOULDBLOCK) && (nErr != WSAEINPROGRESS))
                        DoErrorReport(nErr, TRUE);
                    break;
                }
            }
            if (pbBufOrg != pbBuf) {
                ResetKeepAliveSnd();
            }
        }
        return nSize;
#endif // USE_OVRIO
    } else {
#if !defined(USE_OVRIO)
        m_aSnd.FreeAll();
#endif // !defined(USE_OVRIO)
        TRACE("Send is not possible\n");
        Drop();
        return aSnd_GetDataSize();
    }
}

void CSOC::SendKeepAlive()
{
    if (!IsConnecting()) {
        if (aSnd_GetDataSize() == 0) {
            OnSendKeepAlive();
        }
    }
}

void CSOC::ResetKeepAliveAll()
{
    ResetKeepAliveRcv();
    ResetKeepAliveSnd();
}

void CSOC::ResetKeepAliveRcv()
{
    m_dwKARcvTicks = GetTickCount();
}

void CSOC::ResetKeepAliveSnd()
{
    m_dwKASndTicks = GetTickCount();
}

EException CSOC::GetExceptionCode(int nErrorCode)
{
    EException eException;

    switch(nErrorCode) {
    case WSAENETDOWN: // network subsystem failure...
        eException = EEXCP_WSAENETDOWN;
        break;
    case WSANO_RECOVERY:
        eException = EEXCP_WSANO_RECOVERY;
        break;
    case WSAENETRESET: // connection broken due to keep alive...
        eException = EEXCP_WSAENETRESET;
        goto go_AbortyRemote;
    case WSAECONNRESET: // socket reset... by remote side or hardware failure
        eException = EEXCP_WSAECONNRESET;
        goto go_AbortyRemote;
    case WSAECONNABORTED: // The virtual circuit was terminated due to a time-out or other failure.
        eException = EEXCP_WSAECONNABORTED;
go_AbortyRemote:
        if (this) {
            m_DW.m_xAbortyRemote = 1;
            TRACE("m_DW.m_xAbortyRemote Started...\n");
        }
        break;
    case WSAEADDRINUSE:
        eException = EEXCP_WSAEADDRINUSE;
        break;
    case WSAETIMEDOUT: // connection failure due to time out or ...
        eException = EEXCP_WSAETIMEDOUT;
        break;
    case WSAEHOSTUNREACH:
        eException = EEXCP_WSAEHOSTUNREACH;
        break;
    case WSAEACCES: // broadcast address... - sendto...
        eException = EEXCP_WSAEACCES;
        break;

    case WSANOTINITIALISED:
        eException = EEXCP_WSANOTINITIALISED;
        break;
    case WSAEFAULT: // internal error...
        eException = EEXCP_WSAEFAULT;
        break;
    case WSAENOTSOCK: // internal error...
        eException = EEXCP_WSAENOTSOCK;
        break;
    case WSAENOTCONN:
        if (this && IsConnecting()) {
            eException = EEXCP_NoError;
        } else {
            eException = EEXCP_WSAENOTCONN;
        }
        break;
    case WSAEOPNOTSUPP:
        eException = EEXCP_WSAEOPNOTSUPP;
        break;
    case WSAEINVAL:
        eException = EEXCP_WSAEINVAL;
        break;
    case WSAESHUTDOWN: // may be internal error...
#ifdef _DEBUG
        if (this) {
            TRACE("m_DW.m_xGracefulLocal STARTED case ABONORMAL\n");
        }
#endif // _DEBUG
        eException = EEXCP_NoError;
        break;
    case WSAEDISCON:
        if (this) {
            m_DW.m_xGracefulRemote = 1;
            TRACE("m_DW.m_xGracefulRemote STARTED\n");
        }
        eException = EEXCP_NoError;
        break;
    default:
    //case WSAEINTR: // blocking call canceled...
    //case WSAEINPROGRESS:
    //case WSAEWOULDBLOCK: // would block...
    //case WSAEMSGSIZE: // too large message, UDP?
    //case WSAENOBUFS: // No buffer space is available - sending...
        eException = EEXCP_NoError;
        break;
    }

    return eException;
}

EException CSOC::DoErrorReport(int nErrorCode, BOOL bDropIfErrorOccur/* = FALSE*/)
{
    return DoExceptions(GetExceptionCode(nErrorCode), bDropIfErrorOccur);
}

EException CSOC::DoExceptions(EException eException, BOOL bDropIfErrorOccur/* = FALSE*/)
{
    return m_pSOCManager->OnExceptions(this, eException, bDropIfErrorOccur);
}

CSOCListen::CSOCListen(int nPort/* = 0*/, DWORD dwListenTime/* = 500*/)
{
    m_DW.m_xAllowAccept = TRUE;
    m_ID.m_nID = CSOC_LISTEN_ID;

    m_pfnCSC = NULL;
    m_dwCSCParam1 = 0;
    m_dwCSCParam2 = 0;
    SetPort(nPort);
    m_dwListenTime = dwListenTime;

	m_dwLastCheck = 0;
}

CSOCListen::~CSOCListen()
{
}

void CSOCListen::GetCSCParam(PFN_CreateSOCClient& pfnCSC, DWORD& dwCSCParam1, DWORD& dwCSCParam2) const
{
    if (&pfnCSC)
        pfnCSC = m_pfnCSC;
    if (&dwCSCParam1)
        dwCSCParam1 = m_dwCSCParam1;
    if (&dwCSCParam2)
        dwCSCParam2 = m_dwCSCParam2;
}

void CSOCListen::SetCSCParam(PFN_CreateSOCClient pfnCSC, DWORD dwCSCParam1, DWORD dwCSCParam2)
{
    m_pfnCSC = pfnCSC;
    m_dwCSCParam1 = dwCSCParam1;
    m_dwCSCParam2 = dwCSCParam2;
}

BOOL CSOCListen::BindAndListenSocket()
{
    if (bind(m_hSocket, (const struct sockaddr*)&m_Addr, sizeof(sockaddr_in)) != SOCKET_ERROR) {
        if (listen(m_hSocket, SOMAXCONN) != SOCKET_ERROR) { // SOMAXCONN <- 5?
            return TRUE;
        }
    }
    // bind or listen error...
    DoErrorReport(WSAGetLastError());

    return FALSE;
}

void CSOCListen::OnAcception()
{
    //TRACE("Accepting: %x\n", this);
	sockaddr_in Addr;
	socklen_t nLen = sizeof(sockaddr_in);
#ifdef USE_OVRIO
	SOCKET soc = WSAAccept(m_hSocket, (struct sockaddr*)&Addr, &nLen, NULL, NULL);
#else // !USE_OVRIO
	SOCKET soc = accept(m_hSocket, (struct sockaddr*)&Addr, &nLen);
#endif // USE_OVRIO
    if (soc != INVALID_SOCKET) {
        TRACE("acceptting from %s:%d", inet_ntoa(Addr.sin_addr), GetPort());
        CSOCClient* pSOC;
        if (m_DW.m_xAllowAccept && OnAddrAcceptionCheck(Addr) && ((pSOC = OnNewClientSocket()) != NULL)) {
			*pSOC->GetSockAddr().GetSockAddrIn() = Addr;

            pSOC->CallOnOpen(soc);
            if (pSOC->OnSetupSocket()) {
                if (m_pSOCManager->AddSocket(*pSOC)) {
                    pSOC->CallOnAccept();
                    return;
                } else {
                    // too many users
                    pSOC->DoSendFmt(ESTRF_TOOMANY_CONNECTIONS);
                }
            }
            TRACE("OnSetupSocket failure\n");
            pSOC->Drop();
        } else {
            TRACE("Refusing or SOCKET Creation Failure: %x\n", this);
            shutdown(soc, SD_BOTH);
            closesocket(soc);
        }
    } else {
        // error.
        int nErr = WSAGetLastError();

        if ((nErr != WSAEWOULDBLOCK) && (nErr != WSAEINPROGRESS))
            DoErrorReport(nErr, TRUE);
    }
}

CSOCClient* CSOCListen::OnNewClientSocket() const
{
    if (m_pfnCSC) {
        return (*m_pfnCSC)(*this, m_dwCSCParam1, m_dwCSCParam2);
    } else {
        ASSERT(m_pSOCManager);
        return m_pSOCManager->OnNewClientSocket(*this, m_dwCSCParam1, m_dwCSCParam2);
    }
}

CSOCConnect::CSOCConnect(DWORD dwLingerTime)
{
    m_tConnection = 0;
    SetLingerTime(dwLingerTime);
    m_pFTPStart = NULL;
}

CSOCConnect::~CSOCConnect()
{
    ASSERT(m_pFTPStart == NULL);
}

void CSOCConnect::SetConnectionTime()
{
    m_tConnection = time(NULL);
}

DWORD CSOCConnect::GetConnectionTicks() const
{
    if (GetConnectionState() == +1) {
        return (DWORD)(difftime(time(NULL), m_tConnection) * 1000);
    } else {
        return (DWORD)-1;
    }
}

DWORD CSOCConnect::GetConnectionTimeString(CStr& strTime, BOOL bForceShowMin/* = FALSE*/, BOOL bForceShowHour/* = FALSE*/) const
{
    DWORD dwTicks = GetConnectionTicks();
    if (dwTicks != (DWORD)-1) {
        DWORD dw = dwTicks / 1000;

        int nSec = (int)(dw % 60);
        int nMin = (int)(dw / 60);
        nMin %= 60;
        int nHour = (int)(dw / (60 * 60));
        if (bForceShowHour || (nHour > 0)) {
            strTime.Format("%d:%02d:%02d", nHour, nMin, nSec);
        } else if (bForceShowMin || (nMin > 0)) {
            strTime.Format("%02d:%02d", nMin, nSec);
        } else {
            strTime.Format("%02d", nSec);
        }
    } else {
        strTime = NULL;
    }

    return dwTicks;
}

void CSOCConnect::DoTextDisplay(const char* pcszID, const char* pcszMsg, WORD wTX)
{
    TRACE("Do! Implement DoTextDisplay, ID:%s, MSG:%s\n", pcszID, pcszMsg);
}

void CSOCConnect::OnText()
{
    char* pszID;
    char* pcszMsg;
    WORD wTX;

    if (Disassemble("Wss", &wTX, &pszID, &pcszMsg)) {
        DoTextDisplay(pszID, pcszMsg, wTX);
    }
}

void CSOCConnect::OnTextFmt()
{
    WORD wFmt;
    const char* pcszParamType;

    if (Disassemble("Ws", &wFmt, &pcszParamType)) {
        WORD wTX;
        CPacket Packet(this);

        BYTE* pbText = Packet.DisassembleParamsAndTextFmt(*this, wFmt, pcszParamType, wTX);
        if (pbText) {
            DoTextDisplay(SERVERID, (const char*)pbText, wTX);
        }
    }
}

void CSOCConnect::OnFTP()
{
    ASSERT((m_bCmd == C_FTP) || (m_bCmd == S_FTP));
    BOOL bClientSite = (m_bCmd == S_FTP);

    DWORD dwFTID;
    long lCode;

	if (Disassemble("DD", &dwFTID, &lCode)) {
        CFileTransfer* pFTP = NULL;
        DWORD dwError;

        if (dwFTID == 0) {
            // filename & info negociation
            const char* pcszFileNameRemote;
            if (Disassemble("s", &pcszFileNameRemote)) {
                if (bClientSite) {
                    // server response: file info
                    if (Disassemble("D", &dwFTID)) {
                        if (dwFTID == 0) {
                            if (Disassemble("D", &dwError)) {
                                OnFTPSrvrError((DWORD)lCode, pcszFileNameRemote, dwError);
                            }
                        } else {
                            DWORD dwTime;
                            long lSize;

                            if (Disassemble("DD", &dwTime, &lSize)) {
                                pFTP = OnAllocFileTransfer(dwFTID, (DWORD)lCode, pcszFileNameRemote, dwTime, lSize, dwError);
                                if (pFTP) {
                                    DoSendX("BDD", C_FTP, dwFTID, 0);
                                    pFTP->AddTail(m_pFTPStart);
                                } else {
                                    DoSendX("BDD", C_FTP, dwFTID, -1);
                                }
                            }
                        }
                    }
                } else {
                    // client request file info...
                    pFTP = OnAllocFileTransfer((DWORD)lCode, pcszFileNameRemote, dwError);
                    if (pFTP) {
                        OnFTPFileInfoNotify(pFTP);
                        pFTP->AddTail(m_pFTPStart);
                    } else {
                        DoSendX("BDDsD", S_FTP, (DWORD)lCode, pcszFileNameRemote, 0, dwError);
                    }
                }
            }
        } else {
            if ((pFTP = FindFTP(dwFTID)) == NULL) {
                OnFTPNotFound(dwFTID, bClientSite);
            } else {
                if (lCode < 0) { // error code...
                    OnFTPError(pFTP, lCode);
                } else {
                    pFTP->Start();
                    if (bClientSite)
                        OnFTPRead(pFTP, lCode);
                    else
                        OnFTPWrite(pFTP, lCode);
                }
            }
        }
    }
}

BOOL CSOCConnect::DoFileTransfer(DWORD dwGUARD, const char* pcszFileNameRemote)
{
    DoSendX("BDDs", C_FTP, 0, dwGUARD, pcszFileNameRemote);
    return TRUE;
}

CFileTransfer* CSOCConnect::FindFTP(DWORD dwFTID) const
{
    CFileTransfer* pFTP = m_pFTPStart;

    while(pFTP) {
        if (pFTP->m_dwFTID == dwFTID)
            break;
        pFTP = pFTP->m_pNext;
    }

    return pFTP;
}

CFileTransfer* CSOCConnect::FindFTP(const char* pcszFileNameRemote) const
{
    CFileTransfer* pFTP = m_pFTPStart;

    while(pFTP) {
        if (strcmp(pFTP->m_strFileNameRemote, pcszFileNameRemote) == 0)
            break;
        pFTP = pFTP->m_pNext;
    }

    return pFTP;
}

void CSOCConnect::DoFTPCloseAllAsError()
{
    CFileTransfer* pFTP;

    while((pFTP = CFileTransfer::CutHead(m_pFTPStart)) != NULL) {
        OnFreeFileTransfer(pFTP);
    }
}

void CSOCConnect::OnFTPFileInfoNotify(CFileTransfer* pFTP)
{
    DoSendX("BDDsDD", S_FTP, 0, pFTP->m_dwGUARD, pFTP->m_strFileNameRemote.String(), pFTP->m_dwFTID, pFTP->m_dwTime, pFTP->m_lSize);
}

void CSOCConnect::OnFTPError(CFileTransfer* pFTP, long lErrorCode)
{
    pFTP->Cut(m_pFTPStart);
    OnFreeFileTransfer(pFTP);
}

const char* CSOCConnect::OnFTPDirectory(BOOL bClientSite) const
{
    return "";
}

const char* CSOCConnect::OnFTPFileNameValidation(const char* pcszFileName) const
{
    return pcszFileName;
}

void CSOCConnect::OnFTPFileMap(DWORD dwGUARD, const char* pcszFileNameRemote, CStr& strFileNameLocal, BOOL bClientSite) const
{
    const char* pcsz1;
    const char* pcsz2;
    const char* pcszName;

    pcsz1 = strrchr(pcszFileNameRemote, '/');
    pcsz2 = strrchr(pcszFileNameRemote, '\\');
    if (pcsz1) {
        if (pcsz2) {
            pcszName = pcsz1 > pcsz2 ? pcsz1: pcsz2;
            pcszName++;
        } else {
            pcszName = &pcsz1[1];
        }
    } else {
        if (pcsz2) {
            pcszName = &pcsz2[1];
        } else {
            pcszName = pcszFileNameRemote;
        }
    }
    strFileNameLocal = OnFTPDirectory(bClientSite);
    strFileNameLocal += OnFTPFileNameValidation(pcszName);
}

void CSOCConnect::OnFTPSrvrError(DWORD dwGUARD, const char* pcszFileNameRemote, DWORD dwError)
{
    // dw1 >= 0 ? queued files or error...
}

void CSOCConnect::OnFTPNotFound(DWORD dwFTID, BOOL bClientSite)
{
    DoSendX("BDD", bClientSite ? C_FTP: S_FTP, dwFTID, (DWORD)(-1)); // no such FTID...
}

CFileTransfer* CSOCConnect::OnAllocFileTransfer(DWORD dwGUARD, const char* pcszFileNameRemote, DWORD& dwError)
{
    CStr strFileNameLocal;

    OnFTPFileMap(dwGUARD, pcszFileNameRemote, strFileNameLocal, FALSE);

    CFileTransfer* pFTP = OnNewFileTransfer(0, dwGUARD, pcszFileNameRemote, strFileNameLocal);
    if (pFTP->Setup(dwError)) {
        return pFTP;
    } else {
        OnFreeFileTransfer(pFTP);
        return NULL;
    }
}

CFileTransfer* CSOCConnect::OnAllocFileTransfer(DWORD dwFTID, DWORD dwGUARD, const char* pcszFileNameRemote, DWORD dwTime, long lSize, DWORD& dwError)
{
    CStr strFileNameLocal;

    OnFTPFileMap(dwGUARD, pcszFileNameRemote, strFileNameLocal, TRUE);

    CFileTransfer* pFTP = OnNewFileTransfer(dwFTID, dwGUARD, pcszFileNameRemote, strFileNameLocal);
    if (pFTP->Setup(dwTime, lSize, dwError)) {
        return pFTP;
    } else {
        OnFreeFileTransfer(pFTP);
        return NULL;
    }
}

CFileTransfer* CSOCConnect::OnNewFileTransfer(DWORD dwFTID, DWORD dwGUARD, const char* pcszFileNameRemote, const char* pcszFileNameLocal)
{
    return new CFileTransfer(dwFTID, dwGUARD, pcszFileNameRemote, pcszFileNameLocal);
}

void CSOCConnect::OnFreeFileTransfer(CFileTransfer* pFTP)
{
    pFTP->Close();
    delete pFTP;
}

void CSOCConnect::OnFTPRead(CFileTransfer* pFTP, long lSize)
{
    ASSERT((lSize >= 0) && pFTP && pFTP->m_bClientSite);

    if (lSize > 0) {
        BYTE* pb = GetData(lSize);
        if (pb) {
            if (DoFTPWrite(pFTP, pb, lSize)) {
                DoSendX("BDD", C_FTP, pFTP->m_dwFTID, pFTP->m_lPos);
            } else {
                OnFTPError(pFTP, -1);
            }
        }
    } else {
        OnFTPTerminate(pFTP);
    }
}

void CSOCConnect::OnFTPWrite(CFileTransfer* pFTP, long lPosition)
{
    switch(pFTP->CheckReadPosition(lPosition)) {
    case 0: // end of data...
        DoSendX("BDD", S_FTP, pFTP->m_dwFTID, 0);
        OnFTPTerminate(pFTP);
        break;
    case -1: // error.
        OnFTPError(pFTP, -1);
        break;
    case +1: // more data to read...
      {/*
        CPacket Packet(this);

        Packet.Assemble("BD", S_FTP, pFTP->m_dwFTID);
        Packet.Assemble("D"
        lCode = DoFTPRead(pFTP, ba, ba);
      */}
        break;
    }
}

void CSOCConnect::OnFTPTerminate(CFileTransfer* pFTP)
{
}

long CSOCConnect::DoFTPRead(CFileTransfer* pFTP, const BYTE* pb, long lSize)
{
    return FALSE;
}

BOOL CSOCConnect::DoFTPWrite(CFileTransfer* pFTP, const BYTE* pb, long lSize)
{
    return FALSE;
}

BOOL CSOCConnect::CheckDroppingTicks(DWORD dwTicks)
{
    if (OnSend() == 0)
        CallOnShutdown(SD_SEND);
    BOOL bReadPossible = OnReceive();
    BOOL bSendPossible = IsSendPossible();
    if (!(bReadPossible || bSendPossible) || ((m_dwDroppingTicks + m_dwLingerTime) < dwTicks)) {
        CallOnShutdown(SD_BOTH);
        CallOnClose();
        return TRUE;
    } else {
        return FALSE;
    }
}

CSOCClient::CSOCClient(DWORD dwLingerTime/* = MY_LINGER_TIME*/)
    : CSOCConnect(dwLingerTime)
{
    m_ID.m_uID = CSOC_CLIENT_ID;
}

CSOCClient::~CSOCClient()
{
}

BOOL CSOCClient::OnCheckIDAndPassword(const char* pcszID, const char* pcszPW)
{
    return TRUE;
}

void CSOCClient::OnAccept()
{
}

void CSOCClient::CallOnAccept()
{
    m_DW.m_xIsConnecting = FALSE;
    m_DW.m_xIsConnected = TRUE;
    SetConnectionTime();
    ResetKeepAliveAll();
    OnAccept();
}

CSOCServer::CSOCServer(const char* pcszTarget/* = NULL*/, int nPort/* = 0*/, DWORD dwLingerTime/* = MY_LINGER_TIME*/)
    : CSOCConnect(dwLingerTime)
{
    SetTarget(pcszTarget);
    SetPort(nPort);

    m_ID.m_uID = CSOC_SERVER_ID;
}

CSOCServer::~CSOCServer()
{
}

void CSOCServer::CallOnConnectionTimeOut()
{
    if (m_DW.m_xIsConnecting) {
        m_pSOCManager->DoConnecting(this, FALSE);
        m_DW.m_xIsConnecting = FALSE;
        m_DW.m_xIsConnected = FALSE;
        OnConnectionTimeOut();
    }

    DoExceptions(EEXCP_ConnectionTimeOut, TRUE);
}

void CSOCServer::OnConnect()
{
}

void CSOCServer::OnConnectionTimeOut()
{
}

BOOL CSOCServer::DoConnect()
{
    int nErr;

	if (m_Addr.sin_addr.s_addr == INADDR_NONE) {
		LPHOSTENT lpHost;
		lpHost = gethostbyname(m_Addr.m_strTarget);
		if (lpHost != NULL)
			m_Addr.sin_addr.s_addr = ((LPIN_ADDR)lpHost->h_addr)->s_addr;
		else {
            nErr = WSAGetLastError();

            if ((nErr != WSAEWOULDBLOCK) && (nErr != WSAEINPROGRESS))
                DoErrorReport(nErr);
			return FALSE;
		}
	}

#ifdef USE_OVRIO
	nErr = WSAConnect(m_hSocket, (const SOCKADDR*)&m_Addr, sizeof(sockaddr_in), NULL, NULL, NULL, NULL);
#else // !USE_OVRIO
  #ifdef SOLARIS
	nErr = connect(m_hSocket, (SOCKADDR*)&m_Addr, sizeof(sockaddr_in));
  #else // !SOLARIS
    nErr = connect(m_hSocket, (const SOCKADDR*)&m_Addr, sizeof(sockaddr_in));
  #endif // SOLARIS
#endif // USE_OVRIO
    if (nErr == SOCKET_ERROR) {
        nErr = WSAGetLastError();
        if ((nErr != WSAEWOULDBLOCK) && (nErr != WSAEINPROGRESS)) {
            if (DoErrorReport(nErr) != EEXCP_NoError)
                return FALSE;
        }
    }

    return TRUE;
}

void CSOCServer::CallOnConnect()
{
    if (m_DW.m_xIsConnecting) {
        m_pSOCManager->DoConnecting(this, FALSE);
        m_DW.m_xIsConnecting = FALSE;
    }
    if (!m_DW.m_xIsConnected) {
        SetConnectionTime();
        ResetKeepAliveAll();
        m_DW.m_xIsConnected = TRUE;

        OnConnect();
    }
}

CSOCUDP::CSOCUDP(int nPort/* = 0*/)
    : m_Addr(0, (u_long)0)
{
    m_hSocket = INVALID_SOCKET;
    m_nPort = nPort;
}

CSOCUDP::~CSOCUDP()
{
    Drop();
}

UINT CSOCUDP::GetMAX_MSG_SIZE() const
{
    int err;
    unsigned int uMMAX;
    socklen_t nOptLen = (socklen_t)sizeof(uMMAX);

	err = getsockopt(m_hSocket, SOL_SOCKET, SO_MAX_MSG_SIZE, (char*)&uMMAX, &nOptLen);
    if (err != SOCKET_ERROR) {
        return uMMAX;
    }

    return 0;
}

BOOL CSOCUDP::CreateAndBind(int nPort/* = 0*/)
{
    ASSERT(this && !IsValid());

    if ((m_hSocket = socket(AF_INET, SOCK_DGRAM, 0)) != INVALID_SOCKET) {
        if (nPort)
            m_nPort = nPort;

        m_Addr.sin_addr.s_addr = INADDR_ANY;
        m_Addr.sin_port = htons((short)nPort);

        if (bind(m_hSocket, (const struct sockaddr*)&m_Addr, sizeof(sockaddr_in)) != SOCKET_ERROR) {
            return TRUE;
        }
        closesocket(m_hSocket);
        m_hSocket = INVALID_SOCKET;
    }

    return FALSE;
}

void CSOCUDP::Drop()
{
    if (IsValidSafe()) {
        closesocket(m_hSocket);
        m_hSocket = INVALID_SOCKET;
    }
}

BOOL CSOCUDP::IsReadable() const
{
    ASSERT(IsValid());

    XFD_SET fd;
    fd.SET(m_hSocket);

    fd.Select(0);
    if (fd.GetCount() == 1)
        return TRUE;
    return FALSE;
}

BOOL CSOCUDP::IsWritable() const
{
    ASSERT(IsValid());

    XFD_SET fd;
    fd.SET(m_hSocket);

    fd.Select(1);
    if (fd.GetCount() == 1)
        return TRUE;
    return FALSE;
}

int CSOCUDP::Send(const void* pBuffer, int nLen, DWORD dwAddr, int nPort)
{
    ASSERT(IsValid());

    m_Addr.sin_addr.s_addr = htonl(dwAddr);
    m_Addr.sin_port = htons((short)nPort);

    return sendto(m_hSocket, (const SEND_RECV_TYPE*)pBuffer, nLen, 0, (const struct sockaddr*)&m_Addr, sizeof(sockaddr_in));
}

int CSOCUDP::Recv(void* pBuffer, int nLen, DWORD& dwAddr, int& nPort)
{
    ASSERT(IsValid());

    socklen_t nAddrLen = (socklen_t)sizeof(sockaddr_in);

    int nRet = recvfrom(m_hSocket, (SEND_RECV_TYPE*)pBuffer, nLen, 0, (struct sockaddr*)&m_Addr, &nAddrLen);

    dwAddr = ntohl(m_Addr.sin_addr.s_addr);
    nPort = ntohs(m_Addr.sin_port);

    return nRet;
}

int CSOCUDP::Send(const void* pBuffer, int nLen, const sockaddr_in& Addr)
{
    ASSERT(IsValid());

    return sendto(m_hSocket, (const SEND_RECV_TYPE*)pBuffer, nLen, 0, (const struct sockaddr*)&Addr, sizeof(Addr));
}

int CSOCUDP::Recv(void* pBuffer, int nLen, sockaddr_in& Addr)
{
    ASSERT(IsValid());

    socklen_t nAddrLen = (socklen_t)sizeof(Addr);

    return recvfrom(m_hSocket, (SEND_RECV_TYPE*)pBuffer, nLen, 0, (struct sockaddr*)&Addr, &nAddrLen);
}

XFD_SET::XFD_SET()
{
    fd_count = 0;
#if !defined(WIN32)
    FD_ZERO(&m_FD_SET);
#endif // !defined(WIN32)
}

XFD_SET::XFD_SET(SOCKET soc)
{
    fd_count = 1;
    fd_array[0] = soc;
#if !defined(WIN32)
    FD_ZERO(&m_FD_SET);
    FD_SET(soc, &m_FD_SET);
#endif // !defined(WIN32)
}

XFD_SET::XFD_SET(const XFD_SET& that)
{
    Assign(that);
}

void XFD_SET::Assign(const XFD_SET& that)
{
    fd_count = that.fd_count;
    if (fd_count > 0) {
        memcpy(&fd_array[0], &that.fd_array[0], fd_count * sizeof(SOCKET));
    }
#if !defined(WIN32)
    m_FD_SET = that.m_FD_SET;
#endif // !defined(WIN32)
}

int XFD_SET::FindIndexFromHandle(SOCKET soc) const
{
    SOCKET* pSOCKET = TBSEARCH_SAMEVALUEUNSIGNED(SOCKET, soc, &fd_array[0], fd_count);
    if (pSOCKET) {
        ASSERT(*pSOCKET  == soc);
        return pSOCKET - &fd_array[0];
    }
    return -1;
}

void XFD_SET::ZERO()
{
    fd_count = 0;
#if !defined(WIN32)
    FD_ZERO(&m_FD_SET);
#endif // !defined(WIN32)
}

BOOL XFD_SET::ISSET(SOCKET soc)
{
#if defined(WIN32)
    return FindIndexFromHandle(soc) != -1;
#else // !defined(WIN32)
    return FD_ISSET(soc, &m_FD_SET);
#endif // defined(WIN32)
}

void XFD_SET::SET(SOCKET soc)
{
    int nCount = fd_count;

    if (nCount <= 0) {
        ASSERT(nCount == 0);
        fd_array[0] = soc;
    } else {
        int nIndex = TBRANGE_SAMEVALUEUNSIGNED(SOCKET, soc, &fd_array[0], nCount, TRUE) + 1;
        nCount -= nIndex;

        SOCKET* ppHOC = &fd_array[nIndex];
        if (nCount > 0) {
            memmove(&ppHOC[1], ppHOC, sizeof(SOCKET) * nCount);
        }
        *ppHOC = soc;
    }
#if !defined(WIN32)
    FD_SET(soc, &m_FD_SET);
#endif // !defined(WIN32)
    fd_count++;
}

void XFD_SET::CLR(SOCKET soc)
{
    int nIndex = FindIndexFromHandle(soc);
    if (nIndex != -1) {
        CLR_Index(nIndex);
    }
}

void XFD_SET::CLR_Index(int nIndex)
{
    ASSERT((0 <= nIndex) && (nIndex < fd_count));
    int nMV = (--fd_count - nIndex);
    if (nMV > 0) {
        SOCKET* p1 = &fd_array[nIndex];
#if !defined(WIN32)
        FD_CLR(p1[0], &m_FD_SET);
#endif // !defined(WIN32)
        memcpy(p1, &p1[1], sizeof(SOCKET) * nMV);
    } else {
        ASSERT(nMV == 0);
#if !defined(WIN32)
        FD_ZERO(&m_FD_SET);
#endif // !defined(WIN32)
    }
}

XFD_ARRAY& XFD_SET::GetFD_ARRAY() const
{
    return *(XFD_ARRAY*)this;
}

fd_set& XFD_SET::GetFD_SET() const
{
#if defined(WIN32)
    return *(fd_set*)this;
#else // !defined(WIN32)
    return *(fd_set*)&m_FD_SET;
#endif // !defined(WIN32)
}

int XFD_SET::Select(int nType)
{
    int nCount = fd_count, nRet;

    if (nCount > 0) {
#ifdef WIN32
  #define tSTO g_tSelectTimeOut
        switch(nType) {
        case 0:
            nRet = select(nCount, this, NULL, NULL, &tSTO);
            break;
        case 1:
            nRet = select(nCount, NULL, this, NULL, &tSTO);
            break;
        case 2:
            nRet = select(nCount, NULL, NULL, this, &tSTO);
            break;
        default:
            ASSERT(FALSE);
        }
  #undef tSTO
#else // !WIN32
        struct timeval tSTO(g_tSelectTimeOut);
        int nNumFDs = fd_array[nCount - 1] + 1;
        switch(nType) {
        case 0:
            nRet = select(nNumFDs, &m_FD_SET, NULL, NULL, &tSTO);
            break;
        case 1:
            nRet = select(nNumFDs, NULL, &m_FD_SET, NULL, &tSTO);
            break;
        case 2:
            nRet = select(nNumFDs, NULL, NULL, &m_FD_SET, &tSTO);
            break;
        default:
            ASSERT(FALSE);
        }
        SOCKET* p = &fd_array[0];
        SOCKET* pOld = p;
        while(nCount-- > 0) {
            SOCKET soc = *p++;
            if (FD_ISSET(soc, &m_FD_SET)) {
                *pOld++ = soc;
            }
        }
        fd_count = pOld - &fd_array[0];
#endif // WIN32
        return nRet;
    }

    return 0;
}
#ifdef _DEBUG
void XFD_SET::CheckHandleMap() const
{
    ASSERT(fd_count <= FD_SETSIZE);
    int i, nSize = fd_count;

    for(i = 0; i < nSize; i++) {
        SOCKET soc = fd_array[i];
        for(int k = i + 1; k < nSize; k++) {
            ASSERT(soc < fd_array[k]);
        }
#if !defined(WIN32)
        ASSERT(FD_ISSET(soc, &m_FD_SET));
#endif // !defined(WIN32)
    }
}
#endif // _DEBUG
CFDSet::CFDSet()
{
    FD_ZERO(&GetFD_SET());
}

CFDSet::~CFDSet()
{
}

CSOC* CFDSet::GetSOCFromHandle(SOCKET soc) const
{
    SOCKET* pSOCKET = TBSEARCH_SAMEVALUEUNSIGNED(SOCKET, soc, &fd_array[0], fd_count);
    if (pSOCKET) {
        ASSERT(*m_paSOCs[pSOCKET - &fd_array[0]] == soc);
        return m_paSOCs[pSOCKET - &fd_array[0]];
    }
    return NULL;
}

PSOC CFDSet::FindSOC(SOCKET soc, int& nIndex) const
{
    int nIdx = FindIndexFromHandle(soc);
    if (nIdx != -1) {
        return m_paSOCs[nIndex = nIdx];
    }
    return NULL;
}
#ifdef _DEBUG
void CFDSet::CheckHandleMap() const
{
    XFD_SET::CheckHandleMap();

    int i, nSize = fd_count;

    for(i = 0; i < nSize; i++) {
        ASSERT(fd_array[i] == *m_paSOCs[i]);
    }
}
#endif // _DEBUG
void CFDSet::SET(const CSOC& SOC)
{
    SOCKET soc = SOC;
    int nCount = fd_count;

    if (nCount <= 0) {
        ASSERT(nCount == 0);
        fd_array[0] = soc;
        m_paSOCs[0] = (CSOC*)&SOC;
    } else {
        int nIndex = TBRANGE_SAMEVALUEUNSIGNED(SOCKET, soc, &fd_array[0], nCount, TRUE) + 1;
        nCount -= nIndex;

        SOCKET* ppHOC = &fd_array[nIndex];
        PSOC* ppSOC = &m_paSOCs[nIndex];
        if (nCount > 0) {
            memmove(&ppHOC[1], ppHOC, sizeof(SOCKET) * nCount);
            memmove(&ppSOC[1], ppSOC, sizeof(PSOC) * nCount);
        }
        *ppHOC = soc;
        *ppSOC = (CSOC*)&SOC;
    }
#if !defined(WIN32)
    FD_SET(soc, &GetFD_SET());
#endif // !defined(WIN32)
    fd_count++;
}

void CFDSet::CLR(const CSOC& SOC)
{
    int nIndex = FindIndexFromHandle(SOC);
    if (nIndex != -1) {
        CLR_Index(nIndex);
    }
}

void CFDSet::CLR_Index(int nIndex)
{
    int nMV = (--fd_count - nIndex);
    if (nMV > 0) {
        SOCKET* p1 = &fd_array[nIndex];
        memcpy(p1, &p1[1], sizeof(SOCKET) * nMV);
        PSOC* pp2 = &m_paSOCs[nIndex];
        memcpy(pp2, &pp2[1], sizeof(PSOC) * nMV);
    } else {
        ASSERT(nMV == 0);
    }
}

BOOL CFDSet::AddSOC(CSOC& SOC)
{
    ASSERT(&SOC);
    int nCount;
    
    if ((nCount = fd_count) < (FD_SETSIZE - 1)) {
#ifdef _DEBUG
        CheckHandleMap();
#endif // _DEBUG

        ASSERT(GetSOCFromHandle(SOC) == NULL);

        SET(SOC);
        SOC.SetAdded(TRUE);

#ifdef _DEBUG
        CheckHandleMap();
#endif // _DEBUG
        return TRUE;
    } else {
        return FALSE;
    }
}

void CFDSet::DelSOC(CSOC& SOC)
{
    int nIndex = FindIndexFromHandle(SOC);
    if (nIndex != -1) {
        DelSOCFromIndex(SOC, nIndex);
    }
}

void CFDSet::DelSOCFromIndex(CSOC& SOC, int nIndex)
{
#ifdef _DEBUG
    CheckHandleMap();
#endif // _DEBUG
    XFD_SET::CLR_Index(nIndex);
    // fd_count already decremented...
    ASSERT((0 <= nIndex) && (nIndex <= fd_count));
    int nMV = (fd_count - nIndex);
    if (nMV > 0) {
        PSOC* pp1 = &m_paSOCs[nIndex];
        memcpy(pp1, &pp1[1], sizeof(PSOC) * nMV);
    } else {
        ASSERT(nMV == 0);
    }
    SOC.SetAdded(FALSE);
#ifdef _DEBUG
    fd_array[fd_count] = 0;
    m_paSOCs[fd_count] = NULL;
    CheckHandleMap();
#endif // _DEBUG
}

void CDroppings::AddWithFlush(CSOC* pSOC)
{
    if (FindIndex(pSOC) == -1) {
        Add() = pSOC;
    }
}

CSOCManager::CSOCManager(DWORD dwConnectTimeOut/* = 20000*/)
{
    m_nStarted = 0;
// socsystem
    m_wVersionRequested = MAKEWORD(2, 2); // 1.1
#ifdef WIN32
    //memset(&m_wsaData, 0, sizeof(m_wsaData);
#endif // WIN32
// socmanager
    m_nDroppingSockets = 0;
    m_dwConnectTimeOut = dwConnectTimeOut;
}

CSOCManager::~CSOCManager()
{
    int i, nSize = GetCount();

    for(i = 0; i < nSize; i++)
    {
        CSOC* pSOC = m_paSOCs[i];

        pSOC->CheckAutoDelete();
    }

    FD_ZERO(&GetFD_SET());
}

CSOCListen* CSOCManager::GetListenSocket() const
{
    int nCount = GetCount();
    PSOC* ppSOCs = GetStartOfSOCs();

    while(nCount-- > 0) {
        CSOC* pSOC = *ppSOCs++;
        if (pSOC) {
            if (pSOC->IsListenSocket())
                return (CSOCListen*)pSOC;
        }
    }

    return NULL;
}

CSOC* CSOCManager::FindSOC(int nID/* = CSOC_SERVER_ID*/) const
{
    int nCount = GetCount();
    PSOC* ppSOCs = GetStartOfSOCs();

    while(nCount-- > 0) {
        CSOC* pSOC = *ppSOCs++;
        if (pSOC) {
            if (pSOC->m_ID.m_nID == nID)
                return pSOC;
        }
    }

    return NULL;
}

CSOCServer* CSOCManager::FindSOCServeLogin(int nID/* = CSOC_SERVER_ID*/) const
{
    int nCount = GetCount();
    PSOC* ppSOCs = GetStartOfSOCs();

    while(nCount-- > 0) {
        CSOC* pSOC = *ppSOCs++;
        if (pSOC) {
            if (pSOC->m_ID.m_nID == nID) {
                ASSERT(pSOC->GetType3() == SOCNEXT_EXCSERVER);
                if ((pSOC->GetConnectionState() == +1) && pSOC->GetLoginOK()) {
                    return (CSOCServer*)pSOC;
                }
                return NULL;
            }
        }
    }

    return NULL;
}

CSOC* CSOCManager::GetNextSocket(UINT uType, UINT uMask, CSOC* pSOC, int& nIndex, DWORD dwParam/* = 0*/)
{
    int nCount = GetCount();
    if (nCount > 0) {
        int nIdx;
        BOOL bReverse = !!(uMask & SOCNEXT_REVERSE);

        if (pSOC) {
            if (&nIndex && (nIndex != -1)) {
                nIdx = nIndex;
            } else {
                nIdx = FindIndexFromHandle(*pSOC);
                ASSERT(nIdx != -1);
            }
        } else {
            if (bReverse)
                nIdx = nCount;
            else
                nIdx = -1;
        }

        PSOC* ppSOCs = GetStartOfSOCs();
        ppSOCs = &ppSOCs[nIdx];

        while(TRUE) {
            CSOC* pSOC;

            if (bReverse) {
                if (--nIdx < 0) {
                    break;
                }
                pSOC = *--ppSOCs;
            } else {
                if (++nIdx >= nCount) {
                    break;
                }
                pSOC = *++ppSOCs;
            }
            if ((uType == ((UINT)-1)) || (uType == pSOC->GetType())) {
                if (!(uMask & (SOCNEXT_EXCCLIENT | SOCNEXT_EXCSERVER | SOCNEXT_EXCLISTEN)) || !(pSOC->GetType3() & uMask)) {
                    if (!(uMask & SOCNEXT_EXCINVALID) || pSOC->IsValid()) {
                        if (!(uMask & SOCNEXT_EXCDROPPING) || !pSOC->IsDroppingState()) {
                            if (!(uMask & SOCNEXT_EXCCONNECTTING) || !pSOC->IsConnecting()) {
                                if (!(uMask & SOCNEXT_CHECKPROC) || pSOC->CheckProc(dwParam)) {
                                    if (&nIndex)
                                        nIndex = nIdx;
                                    return pSOC;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    if (&nIndex)
        nIndex = -1;
    return NULL;
}

BOOL CSOCManager::DoListen(CSOCListen* pSOCListen, int nPort/* = 0*/)
{
    if (pSOCListen) {
        if (nPort) {
            pSOCListen->SetPort(nPort);
        }
        if (pSOCListen->CreateAndAttach()) {
            if (pSOCListen->OnSetupSocket()) {
                if (pSOCListen->BindAndListenSocket()) {
                    if (AddSocket(*pSOCListen)) {
                        pSOCListen->m_DW.m_xIsConnecting = FALSE;
                        pSOCListen->m_DW.m_xIsConnected = TRUE;
                        TRACE("listenning from %s:%d\n", pSOCListen->m_Addr.GetTarget(), pSOCListen->m_Addr.GetPort());
                        return TRUE;
                    }
                }
            }
        }
        TRACE("DoListen Failure\n");

        pSOCListen->Drop();
    }

    return FALSE;
}

CSOCListen* CSOCManager::DoListen(int nPort, PFN_CreateSOCClient pfnCSC, DWORD dwCSCParam1, DWORD dwCSCParam2)
{
    CSOCListen* pSOCListen;

	pSOCListen = new CSOCListen(nPort);
	pSOCListen->SetCSCParam(pfnCSC, dwCSCParam1, dwCSCParam2);
    if (!DoListen(pSOCListen, nPort)) {
        // Case of Listen on the same port twice?...
        pSOCListen = NULL;
    }

    return pSOCListen;
}

BOOL CSOCManager::DoConnect(CSOCServer* pSOCServer, const char* pcszTarget/* = NULL*/, int nPort/* = 0*/)
{
    if (pSOCServer) {
        if (pcszTarget)
            pSOCServer->SetTarget(pcszTarget);
        if (nPort)
            pSOCServer->SetPort(nPort);

        if (pSOCServer->CreateAndAttach()) {
            if (pSOCServer->OnSetupSocket()) {
                if (pSOCServer->DoConnect()) {
                    if (AddSocket(*pSOCServer)) {
                        pSOCServer->m_DW.m_xIsConnecting = TRUE;
                        pSOCServer->m_DW.m_xIsConnected = FALSE;
                        DoConnecting(pSOCServer, TRUE);
                        return TRUE;
                    }
                }
            }
        }
        TRACE("DoConnect Failure\n");

        pSOCServer->Drop();
    }

    return FALSE;
}

void CSOCManager::DropAll(BOOL bWait/* = TRUE*/)
{
    TRACE("DropAll\n");

    int i, nSize = GetCount();

    for(i = 0; i < nSize; i++)
    {
        m_paSOCs[i]->Drop();
    }

    if (bWait) {
        while(!IsSafeToShutdown()) {
            SingleStep();
        }
    }
}

EException CSOCManager::OnExceptions(CSOC* pSOC, EException eException, BOOL bDropIfErrorOccur/* = FALSE*/)
{
    if (eException == EEXCP_Unknown) {
        if (pSOC) {
            if (!pSOC->IsListenSocket()) {
                // CSOCConnect socket...
                if (pSOC->IsConnecting()) {
                    eException = EEXCP_ConnectionFailure;
                }
            }
        } else {
        }
    }
    switch(eException) {
    case EEXCP_TooLowVersion:
    case EEXCP_Unknown:
    case EEXCP_ConnectionTimeOut:
    case EEXCP_ConnectionFailure:

    case EEXCP_WSAENETDOWN:
    case EEXCP_WSANO_RECOVERY:
    case EEXCP_WSAENETRESET:
    case EEXCP_WSAECONNRESET:
    case EEXCP_WSAECONNABORTED:
    case EEXCP_WSAEADDRINUSE:
    case EEXCP_WSAETIMEDOUT:
    case EEXCP_WSAEHOSTUNREACH:
    case EEXCP_WSAEACCES:

    case EEXCP_WSANOTINITIALISED:
    case EEXCP_WSAEFAULT:
    case EEXCP_WSAENOTSOCK:
    case EEXCP_WSAENOTCONN:
    case EEXCP_WSAEOPNOTSUPP:
    case EEXCP_WSAEINVAL:
        break;
    }
    if (eException != EEXCP_NoError) {
        TRACE("DoExceptions: %x - CODE %s: %d\n", pSOC, EEXCP2MSG(eException));
        //do...
        if (bDropIfErrorOccur) {
            if (pSOC) {
                pSOC->Drop();
            }
        }
    }

    return eException;
}

BOOL CSOCManager::Init()
{
    if (m_nStarted == 0) {
        if (Startup()) {
            if (SetupInitialOption()) {
                m_nStarted++;
                return TRUE;
            }
            Shutdown();
        }
        return FALSE;
    } else {
        m_nStarted++;
        return TRUE;
    }
}

void CSOCManager::Exit()
{
    if (m_nStarted > 0) {
        if (--m_nStarted == 0) {
            DropAll();
            m_aTimerPool.DelTimersAll();
            Shutdown();
        }
    }
}

BOOL CSOCManager::Startup()
{
#ifdef WIN32
	/* gotta initialize the winsock stuff */
	int err;
 
	err = WSAStartup(m_wVersionRequested, &m_wsaData); 
    if (err == 0) { // don't use SOCKET_ERROR
        if (m_wsaData.wVersion == m_wVersionRequested) {
            if (InitSignal())
                return TRUE;
        } else {
            /* Tell the user that we could not find a usable */
            /* WinSock DLL.                                  */
        }
        OnExceptions(NULL, EEXCP_TooLowVersion, FALSE);
        WSACleanup();
    } else {
		/* Tell the user that we couldn't find a useable */ 
		/* winsock.dll.     */ 
        OnExceptions(NULL, ((CSOC*)NULL)->GetExceptionCode(err), FALSE);
    }
    return FALSE;
#else // WIN32
    return TRUE;
#endif // WIN32
}

void CSOCManager::Shutdown()
{
    ExitSignal();
#ifdef WIN32
    WSACleanup();
#else // !WIN32
#endif // WIN32
}

BOOL CSOCManager::SetupInitialOption()
{
#ifdef WIN32
  #if 1
    return TRUE;
  #else
    int err;
	int optval = SO_SYNCHRONOUS_NONALERT;

 	err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (const char*)&optval,sizeof(optval));
    if (err != SOCKET_ERROR) {
        return TRUE;
    }
    DoErrorReport(NULL, WSAGetLastError(), FALSE);
    return FALSE;
  #endif
#else // !WIN32
    return TRUE;
#endif // WIN32
}

BOOL CSOCManager::InitSignal()
{
#ifdef WIN32
#else
	//signal(SIGPIPE, SIG_IGN);
	//signal(SIGTERM, SIG_IGN);
	//signal(SIGCHLD, child_died);
	//signal(SIGHUP, quick_shutdown);
#endif
    return TRUE;
}

void CSOCManager::ExitSignal()
{
}


void CSOCManager::OnAddSocket(CSOC& SOC)
{
    //TRACE("SOC Added: %x - %s\n", pSOC, pSOC ? (pSOC->IsListenSocket() ? "Listenning": "Server"): "?");
}

void CSOCManager::OnDelSocket(CSOC& SOC)
{
    //TRACE("SOC Cutted: %x - %s\n", pSOC, pSOC ? (pSOC->IsListenSocket() ? "Listenning": "Server"): "?");
}

void CSOCManager::SingleStep()
{
    m_aTimerPool.CheckTimers();

    CheckDropping();

    if (GetCount() > 0) {
        m_FD_Dynamic.Alloc();

	    CheckFlush();
	    CheckReading();

        m_FD_Dynamic.Free();
    }
}

CSOCClient* CSOCManager::OnNewClientSocket(const CSOCListen& SOCListen, DWORD dwCSCParam1, DWORD dwCSCParam2) const
{
    ASSERT(FALSE);
    return NULL;
}

BOOL CSOCManager::AddSocket(CSOC& SOC)
{
    ASSERT(&SOC && (&SOC.GetSOCManager() == NULL));
    if (AddSOC(SOC)) {
        CallOnAddSocket(SOC);
        return TRUE;
    }

    return FALSE;
}

void CSOCManager::CallOnAddSocket(CSOC& SOC)
{
    ASSERT(&SOC);

    SOC.SetSOCManager(this);
    SOC.OnAddSocket();
    OnAddSocket(SOC);
}

void CSOCManager::CallOnDelSocket(CSOC& SOC)
{
    ASSERT(&SOC);
    OnDelSocket(SOC);
    //TRACE("DelSOC{FromIndex}: %x\n", &SOC);
    SOC.OnDelSocket();
    SOC.SetSOCManager(NULL);
}

void CSOCManager::OnDrop(CSOC* pSOC)
{
    if (pSOC) {
        if (!pSOC->IsDroppingState()) { // don't use GetLockCount()
            pSOC->Lock();

            TRACE("Dropping: %x\n", pSOC);
            pSOC->CallOnDropping();

            if (this) {
                ASSERT(pSOC->IsAdded());
                pSOC->m_DW.m_xIsSetupFailure = FALSE;
                if (pSOC->IsConnecting()) {
                    pSOC->m_DW.m_xIsConnecting = FALSE;
                    DoConnecting((CSOCServer*)pSOC, FALSE);
                    //TRACE("(Connecting), ");
                }
                pSOC->SetDroppingTicks();
                pSOC->m_DW.m_xDoDelSocket = 1;
                m_nDroppingSockets++;
            } else {
                pSOC->Unlock();
                ASSERT(!pSOC->IsAdded());
                pSOC->m_DW.m_xIsSetupFailure = TRUE;
                pSOC->m_DW.m_xIsConnecting = FALSE;
                pSOC->CallOnShutdown(SD_BOTH);
                pSOC->CallOnClose();
                pSOC->CallOnDelete();
            }
        }
    }
}

void CSOCManager::DoConnecting(CSOCServer* pSOCServer, BOOL bConnecting)
{
    if (pSOCServer) {
        m_aTimerPool.DelTimers(timer_ConnectCheck, pSOCServer, CHK_TIMER_ONCE);
        int nIndex = m_paConnectings.FindIndex(pSOCServer);
        if (bConnecting) {
            TRACE("Connecting Before: %x\n", pSOCServer);
            m_aTimerPool.AddTimer(timer_ConnectCheck, GetConnectTimeOut(), pSOCServer);
            if (nIndex == -1)
                m_paConnectings.Add() = pSOCServer;
        } else {
            if (nIndex != -1) {
                m_paConnectings.Del(nIndex);
            }
            TRACE("Connecting After: %x\n", pSOCServer);
        }
    }
}

BOOL CSOCManager::IsSafeToShutdown() const
{
    if ((GetCount() == 0) && (m_paDroppings.GetSize() == 0)) {
        return TRUE;
    }
    return FALSE;
}

void CSOCManager::CheckDropping()
{
    int nSize;

    if ((nSize = m_paDroppings.GetSize()) > 0) {
        PSOC* ppSOC = &m_paDroppings.GetAt(0);
        int i = 0;
        DWORD dwTicks = GetTickCount();

        while(i < nSize) {
            CSOC* pSOC = *ppSOC;
            BOOL bDelete;
            if (pSOC->IsListenSocket()) {
                pSOC->m_DW.m_xErrSEND = pSOC->m_DW.m_xErrRECEIVE = TRUE;
                pSOC->CallOnShutdown(SD_BOTH);
                pSOC->CallOnClose();
                bDelete = TRUE;; // do delete...
            } else {
                bDelete = ((CSOCConnect*)pSOC)->CheckDroppingTicks(dwTicks);
            }
            if (bDelete) {
                m_paDroppings.Del(i);
                nSize--;

                pSOC->CallOnDelete();
            } else {
                i++;
                ppSOC++;
            }
        }
        m_paDroppings.FreeExtra();
    }
    if (m_nDroppingSockets > 0) {
        nSize = GetCount();
        PSOC* pp = &GetStartOfSOCs()[nSize];

        while(nSize-- > 0) {
            CSOC* pSOC = *--pp;
            if (pSOC->m_DW.m_xDoDelSocket) {
                DelSOCFromIndex(*pSOC, nSize);
                CallOnDelSocket(*pSOC);
                m_paDroppings.AddWithFlush(pSOC);

                pSOC->Unlock();

                if (--m_nDroppingSockets <= 0) {
                    break;
                }
            }
        }
        ASSERT(m_nDroppingSockets == 0);
    }
}

void CSOCManager::CheckFlush()
{
#if defined(USE_OVRIO)
    SleepEx(0, TRUE);
    XFD_SET& fd = *m_FD_Dynamic.GetFD_SET(m_paConnectings);
    ASSERT(m_paConnectings.GetSize() == fd.GetCount());
#else // !defined(USE_OVRIO)
    XFD_SET& fd = *m_FD_Dynamic.GetFD_SET(*this);
#endif // defined(USE_OVRIO)
    int nErrorCode = fd.Select(1);
    if (nErrorCode != SOCKET_ERROR) {
        int i, nCount = fd.GetCount();
        SOCKET* p = fd.GetFDs();

        for(i = 0; i < nCount; i++) {
            SOCKET soc = *p++;
            CSOC* pSOC = GetSOCFromHandle(soc);
            if (pSOC) {
                if (!pSOC->GetLockCount()) {
#if defined(USE_OVRIO)
                    ASSERT(pSOC->IsConnecting());
                    ((CSOCServer*)pSOC)->CallOnConnect();
#else // !defined(USE_OVRIO)
                    if (pSOC->IsListenSocket()) {
                        // buffer empty...
                        pSOC->m_aSnd.FreeAll();
                    } else {
                        if (pSOC->IsConnecting()) {
                            //if (((CSOCServer*)pSOC)->OnConnectionCheck()) {
                                ((CSOCServer*)pSOC)->CallOnConnect();
                            //}
                        } else {
                            pSOC->OnSend();
                        }
                    }
#endif // defined(USE_OVRIO)
                }
            } else {
                // already freed?
            }
        }
    } else {
#ifdef _DEBUG
        int nErr = WSAGetLastError();
        if ((nErr != WSAEWOULDBLOCK) && (nErr != WSAEINPROGRESS))
            OnExceptions(NULL, ((CSOC*)NULL)->GetExceptionCode(nErr), TRUE);
#endif // _DEBUG
    }
}

void CSOCManager::CheckReading()
{
    XFD_SET& fd = *m_FD_Dynamic.GetFD_SET(*this);

    int nErrorCode = fd.Select(0);
    if (nErrorCode != SOCKET_ERROR) {
        int i, nCount = fd.GetCount();
        SOCKET* p = fd.GetFDs();

        for(i = 0; i < nCount; i++) {
            SOCKET soc = *p++;
            CSOC* pSOC = GetSOCFromHandle(soc);
            if (pSOC) {
                if (!pSOC->GetLockCount()) {
		            if (pSOC->IsListenSocket()) {
                        //if (((CSOCListen*)pSOC)->OnAcceptionCheck()) {
                            ((CSOCListen*)pSOC)->OnAcception();
			            //}
		            } else {
			            pSOC->OnReceive();
                        if (pSOC->m_dwKARcvTimeOut) {
                            if (pSOC->m_dwKARcvTicks) {
                                DWORD dwTicks = GetTickCount();
                                if (dwTicks - pSOC->m_dwKARcvTicks >= pSOC->m_dwKARcvTimeOut) {
                                    TRACE("RcvTimeOut %ld => %ld\n", dwTicks - pSOC->m_dwKARcvTicks, pSOC->m_dwKARcvTimeOut);
                                    pSOC->Drop();
                                }
                            }
                        }
		            }
                }
            } else {
                // already freed?
            }
        }
    } else {
#ifdef _DEBUG
        int nErr = WSAGetLastError();
        if ((nErr != WSAEWOULDBLOCK) && (nErr != WSAEINPROGRESS))
            OnExceptions(NULL, ((CSOC*)NULL)->GetExceptionCode(nErr), TRUE);
#endif // _DEBUG
    }
}

EException CSOCManager::DoErrorReport(CSOC* pSOC, int nErrorCode, BOOL bDropIfErrorOccur/* = FALSE*/)
{
    return OnExceptions(pSOC, pSOC->GetExceptionCode(nErrorCode), bDropIfErrorOccur);
}
