Forráskód Böngészése

Remove ignored files

Deben Oldert 10 éve
szülő
commit
a0bfb4e4e9

+ 146 - 0
ARS Server/include/WinHttpClient/Common/Include/RegExp.h

@@ -0,0 +1,146 @@
+/**
+ *  Copyright 2008-2009 Cheng Shi.  All rights reserved.
+ *  Email: shicheng107@hotmail.com
+ */
+
+#ifndef REGEXP_H
+#define REGEXP_H
+
+#include <iostream>
+#include <string>
+#include <vector>
+using namespace std;
+
+#pragma warning(push)
+#pragma warning(disable: 6385 6011 4127)
+#include "..\..\ThirdParty\ATLRegExp\atlrx.h"
+#pragma warning(pop)
+
+/*
+ * Parameters
+ *  [in] regExp: Value of type string which is the input regular expression.
+ *  [in] caseSensitive: Value of type bool which indicate whether the parse is case sensitive.
+ *  [in] groupCount: Value of type int which is the group count of the regular expression.
+ *  [in] source: Value of type string reference which is the source to parse.
+ *  [out] result: Value of type vecotr of strings which is the output of the parse.
+ *  [in] allowDuplicate: Value of type bool which indicates whether duplicate items are added to the output result.
+ *
+ * Return Value
+ *  Returns true if the function succeeds, or false otherwise.
+ *
+ * Remarks
+ *  The output result is devided into groups.  User should get the groups according to the group count.  For example:
+ *  1. RegExp = L"{ab}", source = L"abcabe", then result = L"ab", L"ab".
+ *  2. RegExp = L"{ab}{cd}", source = L"abcdeabecd", then result = L"ab", L"cd", L"ab", L"cd".
+*/
+inline bool ParseRegExp(const wstring &regExp, bool caseSensitive, int groupCount, const wstring &source, vector<wstring> &result, bool allowDuplicate = false)
+{
+    result.clear();
+    if (regExp.size() <= 0)
+    {
+        return false;
+    }
+    if (groupCount <= 0)
+    {
+        return false;
+    }
+    if (source.size() <= 0)
+    {
+        return false;
+    }
+    CAtlRegExp<> re;
+    REParseError error = re.Parse(regExp.c_str(), caseSensitive);
+    if (error != REPARSE_ERROR_OK)
+    {
+        return false;
+    }
+    wchar_t *pSource = new wchar_t[source.size()+1];
+    wchar_t *pSourceEnd = pSource + source.size();
+    if (pSource == NULL)
+    {
+        return false;
+    }
+    wcscpy_s(pSource, source.size()+1, source.c_str());
+    BOOL bSucceed = TRUE;
+    CAtlREMatchContext<> mc;
+    const wchar_t *pFrom = pSource;
+    const wchar_t *pTo = NULL;
+    while (bSucceed)
+    {
+        bSucceed = re.Match(pFrom, &mc, &pTo);
+        if (bSucceed)
+        {
+            const wchar_t *pStart = NULL;
+            const wchar_t *pEnd = NULL;
+            vector<wstring> tempMatch;
+            for (int i = 0; i < groupCount; i++)
+            {
+                mc.GetMatch(i, &pStart, &pEnd);
+                if (pStart != NULL && pEnd != NULL)
+                {
+                    wstring match(pStart, pEnd-pStart);
+                    tempMatch.push_back(match);
+                }
+                else
+                {
+                    break;
+                }
+            }
+            bool bAdd = true;
+            if (!allowDuplicate)
+            {
+                // Check whether this match already exists in the vector.
+                for (vector<wstring>::iterator it = result.begin(); it != result.end();)
+                {
+                    bool bEqual = true;
+                    for (vector<wstring>::iterator tempMatchIt = tempMatch.begin(); tempMatchIt != tempMatch.end(); tempMatchIt++, it++)
+                    {
+                        bool bGroupEqual = true;
+                        if (caseSensitive)
+                        {
+                            bGroupEqual = (wcscmp(it->c_str(), tempMatchIt->c_str()) == 0);
+                        }
+                        else
+                        {
+                            bGroupEqual = (_wcsicmp(it->c_str(), tempMatchIt->c_str()) == 0);
+                        }
+                        if (!bGroupEqual)
+                        {
+                            bEqual = false;
+                        }
+                    }
+                    if (bEqual)
+                    {
+                        bAdd = false;
+                        break;
+                    }
+                }
+            }
+            if (bAdd)
+            {
+                for (vector<wstring>::iterator tempMatchIt = tempMatch.begin(); tempMatchIt != tempMatch.end(); tempMatchIt++)
+                {
+                    result.push_back(*tempMatchIt);
+                }
+            }
+            if (pTo < pSourceEnd)
+            {
+                pFrom = pTo;
+            }
+            else
+            {
+                break;
+            }
+        }
+        else
+        {
+            break;
+        }
+    }
+
+    delete[] pSource;
+
+    return true;
+}
+
+#endif // REGEXP_H

+ 569 - 0
ARS Server/include/WinHttpClient/Common/Include/StringProcess.h

@@ -0,0 +1,569 @@
+/**
+ *  Copyright 2008-2009 Cheng Shi.  All rights reserved.
+ *  Email: shicheng107@hotmail.com
+ */
+
+#ifndef STRINGPROCESS_H
+#define STRINGPROCESS_H
+
+#include "RegExp.h"
+#include <Windows.h>
+#include <iostream>
+#include <string>
+#include <comutil.h>
+#pragma warning(push)
+#pragma warning(disable: 4127)
+#include <atlcomtime.h>
+#pragma warning(pop)
+using namespace std;
+
+inline wstring Trim(const wstring &source, const wstring &targets)
+{
+    wstring::size_type start = 0;
+    wstring::size_type end = 0;
+    for (start = 0; start < source.size(); start++)
+    {
+        bool bIsTarget = false;
+        for (wstring::size_type i = 0; i < targets.size(); i++)
+        {
+            if (source[start] == targets[i])
+            {
+                bIsTarget = true;
+                break;
+            }
+        }
+        if (!bIsTarget)
+        {
+            break;
+        }
+    }
+    for (end = source.size() - 1; (int)end >= 0; end--)
+    {
+        bool bIsTarget = false;
+        for (wstring::size_type i = 0; i < targets.size(); i++)
+        {
+            if (source[end] == targets[i])
+            {
+                bIsTarget = true;
+                break;
+            }
+        }
+        if (!bIsTarget)
+        {
+            break;
+        }
+    }
+    wstring result = L"";
+    if (end >= start && start < source.size() && end >= 0)
+    {
+        result = source.substr(start, end-start+1);
+    }
+
+    return result;
+}
+
+inline bool PrepareString(wchar_t *dest, size_t *size, const wstring &src)
+{
+    if (dest == NULL)
+    {
+        if (size != NULL)
+        {
+            *size = src.size();
+        }
+        return false;
+    }
+    else
+    {
+        if (size != NULL)
+        {
+            wcsncpy_s(dest, *size, src.c_str(), _TRUNCATE);
+            if (*size <= src.size())
+            {
+                ::SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+inline wstring ReplaceString(const wstring &srcStr, const wstring &oldStr, const wstring &newStr)
+{
+    if (srcStr.size() <= 0 || oldStr.size() <= 0)
+    {
+        return srcStr;
+    }
+    wstring strReturn = srcStr;
+    wstring::size_type offset = 0;
+    wstring::size_type start = strReturn.find(oldStr);
+    while (start != wstring::npos)
+    {
+        offset = start + newStr.size();
+        strReturn.replace(start, oldStr.size(), newStr);
+        start = strReturn.find(oldStr, offset);
+    }
+
+    return strReturn;
+}
+
+inline int StringToInteger(const wstring &number)
+{
+    if (number.size() <= 0)
+    {
+        return 0;
+    }
+    wstring num = ReplaceString(number, L",", L"");
+    num = ReplaceString(num, L" ", L"");
+
+    return _wtoi(num.c_str());
+}
+
+inline wstring LowerString(const wstring &text)
+{
+    if (text.size() <= 0)
+    {
+        return L"";
+    }
+    unsigned int iLength = text.size() + 1;
+    wchar_t *pTemp = new wchar_t[iLength];
+    if (pTemp == NULL)
+    {
+        return L"";
+    }
+    wcscpy_s(pTemp, iLength, text.c_str());
+    _wcslwr_s(pTemp, iLength);
+    wstring retStr = pTemp;
+    delete[] pTemp;
+
+    return retStr;
+}
+
+inline wstring UpperString(const wstring &text)
+{
+    if (text.size() <= 0)
+    {
+        return L"";
+    }
+    unsigned int iLength = text.size() + 1;
+    wchar_t *pTemp = new wchar_t[iLength];
+    if (pTemp == NULL)
+    {
+        return L"";
+    }
+    wcscpy_s(pTemp, iLength, text.c_str());
+    _wcsupr_s(pTemp, iLength);
+    wstring retStr = pTemp;
+    delete[] pTemp;
+
+    return retStr;
+}
+
+inline wstring GetAnchorText(const wstring &anchor)
+{
+    wstring regExp = L"<a.*?>[ \t\r\n]*{.*?}[ \t\r\n]*</a>";
+    vector<wstring> result;
+    if (ParseRegExp(regExp, false, 1, anchor, result) && result.size() == 1)
+    {
+        wstring text = result[0];
+        return text;
+    }
+
+    return L"";
+}
+
+inline wstring GetAnchorLink(const wstring &anchor)
+{
+    wstring regExp = L"<a.*?href=\"|\'{.*?}\"|\'.*?>.*?</a>";
+    vector<wstring> result;
+    if (ParseRegExp(regExp, false, 1, anchor, result) && result.size() == 1)
+    {
+        wstring link = result[0];
+        return link;
+    }
+
+    return L"";
+}
+
+inline bool SeparateString(const wstring &content, const wstring &delimiter, vector<wstring> &result)
+{
+    if (content.size() <= 0 || delimiter.size() <= 0)
+    {
+        return false;
+    }
+
+    result.clear();
+    wstring::size_type start = 0;
+    wstring::size_type index = 0;
+    index = content.find(delimiter, start);
+    while (index != wstring::npos)
+    {
+        wstring::size_type size = index - start;
+        if (size > 0)
+        {
+            wstring temp = content.substr(start, size);
+            if (temp.size() > 0)
+            {
+                result.push_back(temp);
+            }
+        }
+        start  += size + delimiter.size();
+        index = content.find(delimiter, start);
+    }
+    if (content.find(delimiter) != wstring::npos)
+    {
+        wstring last = content.substr(start);
+        if (last.size() > 0)
+        {
+            result.push_back(last);
+        }
+    }
+    else
+    {
+        false;
+    }
+
+    return true;
+}
+
+inline wstring URLEncoding(const wstring &keyword, bool convertToUTF8 = true)
+{
+    int iLength = 0;
+    char *szKeyword = NULL;
+
+    if (convertToUTF8)
+    {
+        iLength = ::WideCharToMultiByte(CP_UTF8,
+                                        0,
+                                        keyword.c_str(),
+                                        keyword.length(),
+                                        NULL,
+                                        0,
+                                        NULL,
+                                        NULL);
+        if (iLength <= 0)
+        {
+            return L"";
+        }
+
+        szKeyword = new char[iLength];
+        if (szKeyword == NULL)
+        {
+            return L"";
+        }
+        iLength = ::WideCharToMultiByte(CP_UTF8,
+                                        0,
+                                        keyword.c_str(),
+                                        keyword.length(),
+                                        szKeyword,
+                                        iLength,
+                                        NULL,
+                                        NULL);
+    }
+    else
+    {
+        string strKeyword = (char *)(_bstr_t)keyword.c_str();
+        iLength = (int)strKeyword.length();
+        szKeyword = new char[strKeyword.length() + 1];
+        strcpy_s(szKeyword, strKeyword.length() + 1, strKeyword.c_str());
+    }
+
+    wstring encodedKeyword = L"";
+    string strEncodedKeyword = "";
+    for (int i = 0; i < iLength; i++)
+    {
+        unsigned char c = (unsigned char)szKeyword[i];
+        char temp[MAX_PATH] = "";
+        sprintf_s(temp, MAX_PATH, "%%%2X", c);
+        if (temp[1] == ' ')
+        {
+            temp[1] = '0';
+        }
+        strEncodedKeyword += temp;
+    }
+    if (szKeyword != NULL)
+    {
+        delete[] szKeyword;
+    }
+    encodedKeyword = (wchar_t *)(_bstr_t)strEncodedKeyword.c_str();
+    encodedKeyword = ReplaceString(encodedKeyword, L" ", L"+");
+
+    return encodedKeyword;
+}
+
+inline unsigned int GetSeparateKeywordMatchGrade(const wstring &source, const wstring &keyword)
+{
+    if (source.length() <= 0 || keyword.length() <= 0)
+    {
+        return 0;
+    }
+
+    wstring lowerSource = LowerString(source);
+    wstring lowerKeyword = LowerString(keyword);
+
+    unsigned int grade = 0;
+    if (lowerKeyword.length() <= 3)
+    {
+        if (lowerSource.find(lowerKeyword) != wstring::npos)
+        {
+            grade = 100;
+        }
+        else
+        {
+            grade = 0;
+        }
+    }
+    else
+    {
+        unsigned int matchLength = 0;
+        unsigned int index = 0;
+        while (index < lowerKeyword.length())
+        {
+            unsigned int compareLength = lowerKeyword.length() - index;
+            while (compareLength > 0 && index < lowerKeyword.length())
+            {
+                wstring subKeyword = lowerKeyword.substr(index, compareLength);
+                if (lowerSource.find(subKeyword) != wstring::npos)
+                {
+                    matchLength += compareLength;
+                    index += compareLength;
+                }
+                else
+                {
+                    compareLength--;
+                }
+            }
+            index++;
+        }
+        grade = matchLength * 100 / lowerKeyword.length();
+    }
+
+    return grade;
+}
+
+inline unsigned int GetKeywordMatchGrade(const wstring &source, const wstring & keyword)
+{
+    if (source.length() <= 0 || keyword.length() <= 0)
+    {
+        return 0;
+    }
+
+    unsigned int grade = 0;
+    wstring src = source;
+    while (src.find(L"\t") != wstring::npos)
+    {
+        src = ReplaceString(src, L"\t", L" ");
+    }
+    while (src.find(L"  ") != wstring::npos)
+    {
+        src = ReplaceString(src, L"  ", L" ");
+    }
+    vector<wstring> results;
+    if (SeparateString(keyword, L" ", results) && results.size() > 0)
+    {
+        unsigned int keywordTotalLength = 0;
+        for (vector<wstring>::size_type index = 0; index < results.size(); index++)
+        {
+            keywordTotalLength += results[index].length();
+        }
+        for (vector<wstring>::size_type index = 0; index < results.size(); index++)
+        {
+            grade += GetSeparateKeywordMatchGrade(src, results[index]) * results[index].length() / keywordTotalLength;
+        }
+    }
+    else
+    {
+        grade = GetSeparateKeywordMatchGrade(src, keyword);
+    }
+
+    return grade;
+}
+
+inline wstring GetDateString(const COleDateTime &time, const wstring &separator = L"-", bool align = true)
+{
+    wstring date = L"";
+    wchar_t szTemp[MAX_PATH] = L"";
+
+    swprintf_s(szTemp, MAX_PATH, L"%d", time.GetYear());
+    date += szTemp;
+    date += separator;
+
+    memset(szTemp, 0, sizeof(wchar_t) * MAX_PATH);
+    swprintf_s(szTemp, MAX_PATH, L"%d", time.GetMonth());
+    if (time.GetMonth() < 10 && align)
+    {
+        date += L"0";
+    }
+    date += szTemp;
+    date += separator;
+
+    memset(szTemp, 0, sizeof(wchar_t) * MAX_PATH);
+    swprintf_s(szTemp, MAX_PATH, L"%d", time.GetDay());
+    if (time.GetDay() < 10 && align)
+    {
+        date += L"0";
+    }
+    date += szTemp;
+
+    return date;
+}
+
+inline wstring GetDateString(int dayOffset, const wstring &separator = L"-", bool align = true)
+{
+    COleDateTime time = COleDateTime::GetCurrentTime();
+    time += COleDateTimeSpan(dayOffset, 0, 0, 0);
+
+    return GetDateString(time, separator, align);
+}
+
+inline wstring GetTimeString(const COleDateTime &time, const wstring &separator = L":", bool align = true)
+{
+    wstring date = L"";
+    wchar_t szTemp[MAX_PATH] = L"";
+
+    swprintf_s(szTemp, MAX_PATH, L"%d", time.GetHour());
+    date += szTemp;
+    date += separator;
+
+    memset(szTemp, 0, sizeof(wchar_t) * MAX_PATH);
+    swprintf_s(szTemp, MAX_PATH, L"%d", time.GetMinute());
+    if (time.GetMinute() < 10 && align)
+    {
+        date += L"0";
+    }
+    date += szTemp;
+    date += separator;
+
+    memset(szTemp, 0, sizeof(wchar_t) * MAX_PATH);
+    swprintf_s(szTemp, MAX_PATH, L"%d", time.GetSecond());
+    if (time.GetSecond() < 10 && align)
+    {
+        date += L"0";
+    }
+    date += szTemp;
+
+    return date;
+}
+
+inline wstring MD5(const wstring &text)
+{
+    if (text.size() <= 0)
+    {
+        return L"";
+    }
+    string asciiText = (char *)(_bstr_t)text.c_str();
+    wstring encrypted = L"";
+    HCRYPTPROV hCryptProv = NULL;
+    if (::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET))
+    {
+        HCRYPTHASH hHash = NULL;
+        if (::CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash))
+        {
+            if (::CryptHashData(hHash, (BYTE *)asciiText.c_str(), asciiText.size(), 0))
+            {
+                BYTE result[16];
+                DWORD dwSize = 16;
+                wchar_t temp[3] = L"";
+                if (::CryptGetHashParam(hHash, HP_HASHVAL, result, &dwSize, 0))
+                {
+                    for (unsigned int i = 0; i < 16; i++)
+                    {
+                        memset(temp, 0, 6);
+                        swprintf(temp, 3, L"%02x", result[i]);
+                        encrypted += temp;
+                    }
+                }
+            }
+            ::CryptDestroyHash(hHash);
+            ::CryptReleaseContext(hCryptProv, 0);
+        }
+    }
+
+    return encrypted;
+}
+
+inline wstring FilterFileName(const wstring &name)
+{
+    if (name.size() <= 0)
+    {
+        return L"";
+    }
+
+    wstring filteredName = name;
+    filteredName = ReplaceString(filteredName, L"/", L"_");
+    filteredName = ReplaceString(filteredName, L"\\", L"_");
+    filteredName = ReplaceString(filteredName, L":", L"_");
+    filteredName = ReplaceString(filteredName, L"*", L"_");
+    filteredName = ReplaceString(filteredName, L"?", L"_");
+    filteredName = ReplaceString(filteredName, L"\"", L"_");
+    filteredName = ReplaceString(filteredName, L"<", L"_");
+    filteredName = ReplaceString(filteredName, L">", L"_");
+    filteredName = ReplaceString(filteredName, L"|", L"_");
+
+    return filteredName;
+}
+
+inline wstring GetMagic(unsigned int length)
+{
+    srand(::GetTickCount());
+    if (length <= 0)
+    {
+        return L"";
+    }
+
+    wstring margic = L"";
+    for (unsigned int i = 0; i < length; i++)
+    {
+        wchar_t szMargic[50] = L"";
+        swprintf_s(szMargic, 50, L"%c", rand() % 26 + L'a');
+        margic += szMargic;
+    }
+
+    return margic;
+}
+
+inline wstring GetHost(const wstring &url)
+{
+    if (url.size() <= 0)
+    {
+        return L"";
+    }
+
+    wstring urlWidthoutHttp = ReplaceString(LowerString(url), L"http://", L"");
+
+    unsigned int index = urlWidthoutHttp.find(L"/");
+    if (index == wstring::npos)
+    {
+        index = urlWidthoutHttp.find(L"\\");
+    }
+    if (index == wstring ::npos)
+    {
+        return urlWidthoutHttp;
+    }
+
+    return urlWidthoutHttp.substr(0, index);
+}
+
+inline wstring GetValidFileName(const wstring &fileName)
+{
+    if (fileName.size() == 0)
+    {
+        return L"";
+    }
+    wstring tempFileName = fileName;
+    tempFileName = ReplaceString(tempFileName, L"\\", L"_");
+    tempFileName = ReplaceString(tempFileName, L"/", L"_");
+    tempFileName = ReplaceString(tempFileName, L":", L"_");
+    tempFileName = ReplaceString(tempFileName, L"*", L"_");
+    tempFileName = ReplaceString(tempFileName, L"?", L"_");
+    tempFileName = ReplaceString(tempFileName, L"\"", L"_");
+    tempFileName = ReplaceString(tempFileName, L"<", L"_");
+    tempFileName = ReplaceString(tempFileName, L">", L"_");
+    tempFileName = ReplaceString(tempFileName, L"|", L"_");
+    tempFileName = ReplaceString(tempFileName, L"\r", L"_");
+    tempFileName = ReplaceString(tempFileName, L"\n", L"_");
+    tempFileName = ReplaceString(tempFileName, L"%", L"_");
+
+    return tempFileName;
+}
+
+#endif // STRINGPROCESS_H

+ 842 - 0
ARS Server/include/WinHttpClient/Common/Include/WinHttpClient.h

@@ -0,0 +1,842 @@
+/**
+ *  Copyright 2008-2010 Cheng Shi.  All rights reserved.
+ *  Email: shicheng107@hotmail.com
+ */
+
+#ifndef WINHTTPCLIENT_H
+#define WINHTTPCLIENT_H
+
+#pragma comment(lib, "Winhttp.lib")
+
+#include "RegExp.h"
+#include "StringProcess.h"
+#include <comutil.h>
+#include <windows.h>
+#include <Winhttp.h>
+#include <string>
+using namespace std;
+
+typedef bool (*PROGRESSPROC)(double);
+
+static const unsigned int INT_RETRYTIMES = 3;
+static wchar_t *SZ_AGENT = L"WinHttpClient";
+static const int INT_BUFFERSIZE = 10240;    // Initial 10 KB temporary buffer, double if it is not enough.
+
+class WinHttpClient
+{
+public:
+    inline WinHttpClient(const wstring &url, PROGRESSPROC progressProc = NULL);
+    inline ~WinHttpClient(void);
+
+    // It is a synchronized method and may take a long time to finish.
+    inline bool SendHttpRequest(const wstring &httpVerb = L"GET", bool disableAutoRedirect = false);
+    inline wstring GetResponseHeader(void);
+    inline wstring GetResponseContent(void);
+    inline wstring GetResponseCharset(void);
+    inline wstring GetResponseStatusCode(void);
+    inline wstring GetResponseLocation(void);
+    inline wstring GetRequestHost(void);
+    inline const BYTE *GetRawResponseContent(void);
+    inline unsigned int GetRawResponseContentLength(void);
+    inline unsigned int GetRawResponseReceivedContentLength(void);
+    inline bool SaveResponseToFile(const wstring &filePath);
+    inline wstring GetResponseCookies(void);
+    inline bool SetAdditionalRequestCookies(const wstring &cookies);
+    inline bool SetAdditionalDataToSend(BYTE *data, unsigned int dataSize);
+    inline bool UpdateUrl(const wstring &url);
+    inline bool ResetAdditionalDataToSend(void);
+    inline bool SetAdditionalRequestHeaders(const wstring &additionalRequestHeaders);
+    inline bool SetRequireValidSslCertificates(bool require);
+    inline bool SetProxy(const wstring &proxy);
+    inline DWORD GetLastError(void);
+    inline bool SetUserAgent(const wstring &userAgent);
+    inline bool SetForceCharset(const wstring &charset);
+    inline bool SetProxyUsername(const wstring &username);
+    inline bool SetProxyPassword(const wstring &password);
+    inline bool SetTimeouts(unsigned int resolveTimeout = 0,
+                            unsigned int connectTimeout = 60000,
+                            unsigned int sendTimeout = 30000,
+                            unsigned int receiveTimeout = 30000);
+
+private:
+    inline WinHttpClient(const WinHttpClient &other);
+    inline WinHttpClient &operator =(const WinHttpClient &other);
+    inline bool SetProgress(unsigned int byteCountReceived);
+
+    HINTERNET m_sessionHandle;
+    bool m_requireValidSsl;
+    wstring m_requestURL;
+    wstring m_requestHost;
+    wstring m_responseHeader;
+    wstring m_responseContent;
+    wstring m_responseCharset;
+    BYTE *m_pResponse;
+    unsigned int m_responseByteCountReceived;   // Up to 4GB.
+    PROGRESSPROC m_pfProcessProc;
+    unsigned int m_responseByteCount;
+    wstring m_responseCookies;
+    wstring m_additionalRequestCookies;
+    BYTE *m_pDataToSend;
+    unsigned int m_dataToSendSize;
+    wstring m_additionalRequestHeaders;
+    wstring m_proxy;
+    DWORD m_dwLastError;
+    wstring m_statusCode;
+    wstring m_userAgent;
+    bool m_bForceCharset;
+    wstring m_proxyUsername;
+    wstring m_proxyPassword;
+    wstring m_location;
+    unsigned int m_resolveTimeout;
+    unsigned int m_connectTimeout;
+    unsigned int m_sendTimeout;
+    unsigned int m_receiveTimeout;
+};
+
+WinHttpClient::WinHttpClient(const wstring &url, PROGRESSPROC progressProc)
+    : m_requestURL(url),
+      m_sessionHandle(NULL),
+      m_requireValidSsl(false),
+      m_responseHeader(L""),
+      m_responseContent(L""),
+      m_responseCharset(L""),
+      m_requestHost(L""),
+      m_pResponse(NULL),
+      m_responseByteCountReceived(0),
+      m_pfProcessProc(progressProc),
+      m_responseByteCount(0),
+      m_responseCookies(L""),
+      m_additionalRequestCookies(L""),
+      m_pDataToSend(NULL),
+      m_dataToSendSize(0),
+      m_proxy(L""),
+      m_dwLastError(0),
+      m_statusCode(L""),
+      m_userAgent(SZ_AGENT),
+      m_bForceCharset(false),
+      m_proxyUsername(L""),
+      m_proxyPassword(L""),
+      m_location(L""),
+      m_resolveTimeout(0),
+      m_connectTimeout(60000),
+      m_sendTimeout(30000),
+      m_receiveTimeout(30000)
+{
+}
+
+WinHttpClient::~WinHttpClient(void)
+{
+    if (m_pResponse != NULL)
+    {
+        delete[] m_pResponse;
+    }
+    if (m_pDataToSend != NULL)
+    {
+        delete[] m_pDataToSend;
+    }
+
+    if (m_sessionHandle != NULL)
+    {
+        ::WinHttpCloseHandle(m_sessionHandle);
+    }
+}
+
+bool WinHttpClient::SendHttpRequest(const wstring &httpVerb, bool disableAutoRedirect)
+{
+    if (m_requestURL.size() <= 0)
+    {
+        m_dwLastError = ERROR_PATH_NOT_FOUND;
+        return false;
+    }
+    // Make verb uppercase.
+    wstring verb = httpVerb;
+    if (_wcsicmp(verb.c_str(), L"GET") == 0)
+    {
+        verb = L"GET";
+    }
+    else if (_wcsicmp(verb.c_str(), L"POST") == 0)
+    {
+        verb = L"POST";
+    }
+    else
+    {
+        m_dwLastError = ERROR_INVALID_PARAMETER;
+        return false;
+    }
+    bool bRetVal = true;
+
+    if (m_sessionHandle == NULL)
+    {
+        m_sessionHandle = ::WinHttpOpen(m_userAgent.c_str(),  
+                                        WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+                                        WINHTTP_NO_PROXY_NAME, 
+                                        WINHTTP_NO_PROXY_BYPASS,
+                                        0);
+        if (m_sessionHandle == NULL)
+        {
+            m_dwLastError = ::GetLastError();
+            return false;
+        }
+    }
+
+    ::WinHttpSetTimeouts(m_sessionHandle,
+                         m_resolveTimeout,
+                         m_connectTimeout,
+                         m_sendTimeout,
+                         m_receiveTimeout);
+
+    wchar_t szHostName[MAX_PATH] = L"";
+    wchar_t szURLPath[MAX_PATH * 5] = L"";
+    URL_COMPONENTS urlComp;
+    memset(&urlComp, 0, sizeof(urlComp));
+    urlComp.dwStructSize = sizeof(urlComp);
+    urlComp.lpszHostName = szHostName;
+    urlComp.dwHostNameLength = MAX_PATH;
+    urlComp.lpszUrlPath = szURLPath;
+    urlComp.dwUrlPathLength = MAX_PATH * 5;
+    urlComp.dwSchemeLength = 1; // None zero
+
+    if (::WinHttpCrackUrl(m_requestURL.c_str(), m_requestURL.size(), 0, &urlComp))
+    {
+        m_requestHost = szHostName;
+        HINTERNET hConnect = NULL;
+        hConnect = ::WinHttpConnect(m_sessionHandle, szHostName, urlComp.nPort, 0);
+        if (hConnect != NULL)
+        {
+            DWORD dwOpenRequestFlag = (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0;
+            HINTERNET hRequest = NULL;
+            hRequest = ::WinHttpOpenRequest(hConnect,
+                                            verb.c_str(),
+                                            urlComp.lpszUrlPath,
+                                            NULL,
+                                            WINHTTP_NO_REFERER,
+                                            WINHTTP_DEFAULT_ACCEPT_TYPES,
+                                            dwOpenRequestFlag);
+            if (hRequest != NULL)
+            {
+                // If HTTPS, then client is very susceptable to invalid certificates
+                // Easiest to accept anything for now
+                if (!m_requireValidSsl && urlComp.nScheme == INTERNET_SCHEME_HTTPS)
+                {
+                    DWORD options = SECURITY_FLAG_IGNORE_CERT_CN_INVALID
+                                    | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
+                                    | SECURITY_FLAG_IGNORE_UNKNOWN_CA;
+                    ::WinHttpSetOption(hRequest,
+                                       WINHTTP_OPTION_SECURITY_FLAGS,
+                                       (LPVOID)&options,
+                                       sizeof(DWORD));
+                }
+
+                bool bGetReponseSucceed = false;
+                unsigned int iRetryTimes = 0;
+
+                // Retry for several times if fails.
+                while (!bGetReponseSucceed && iRetryTimes++ < INT_RETRYTIMES)
+                {
+                    if (m_additionalRequestHeaders.size() > 0)
+                    {
+                        if (!::WinHttpAddRequestHeaders(hRequest, m_additionalRequestHeaders.c_str(), m_additionalRequestHeaders.size(), WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON))
+                        {
+                            m_dwLastError = ::GetLastError();
+                        }
+                    }
+                    if (m_additionalRequestCookies.size() > 0)
+                    {
+                        wstring cookies = L"Cookie: ";
+                        cookies += m_additionalRequestCookies;
+                        if (!::WinHttpAddRequestHeaders(hRequest, cookies.c_str(), cookies.size(), WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON))
+                        {
+                            m_dwLastError = ::GetLastError();
+                        }
+                    }
+                    if (m_proxy.size() > 0)
+                    {
+                        WINHTTP_PROXY_INFO proxyInfo;
+                        memset(&proxyInfo, 0, sizeof(proxyInfo));
+                        proxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+                        wchar_t szProxy[MAX_PATH] = L"";
+                        wcscpy_s(szProxy, MAX_PATH, m_proxy.c_str());
+                        proxyInfo.lpszProxy = szProxy;
+
+                        if (!::WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY, &proxyInfo, sizeof(proxyInfo)))
+                        {
+                            m_dwLastError = ::GetLastError();
+                        }
+
+                        if (m_proxyUsername.size() > 0)
+                        {
+                            if (!::WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY_USERNAME, (LPVOID)m_proxyUsername.c_str(), m_proxyUsername.size() * sizeof(wchar_t)))
+                            {
+                                m_dwLastError = ::GetLastError();
+                            }
+                            if (m_proxyPassword.size() > 0)
+                            {
+                                if (!::WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY_PASSWORD, (LPVOID)m_proxyPassword.c_str(), m_proxyPassword.size() * sizeof(wchar_t)))
+                                {
+                                    m_dwLastError = ::GetLastError();
+                                }
+                            }
+                        }
+                    }
+
+                    if (disableAutoRedirect)
+                    {
+                        DWORD dwDisableFeature = WINHTTP_DISABLE_REDIRECTS;
+                        if (!::WinHttpSetOption(hRequest, WINHTTP_OPTION_DISABLE_FEATURE, &dwDisableFeature, sizeof(dwDisableFeature)))
+                        {
+                            m_dwLastError = ::GetLastError();
+                        }
+                    }
+                    bool bSendRequestSucceed = false;
+                    if (::WinHttpSendRequest(hRequest,
+                                             WINHTTP_NO_ADDITIONAL_HEADERS,
+                                             0,
+                                             WINHTTP_NO_REQUEST_DATA,
+                                             0,
+                                             0,
+                                             NULL))
+                    {
+                        bSendRequestSucceed = true;
+                    }
+                    else
+                    {
+                        // Query the proxy information from IE setting and set the proxy if any.
+                        WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig;
+                        memset(&proxyConfig, 0, sizeof(proxyConfig));
+                        if (::WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig))
+                        {
+                            if (proxyConfig.lpszAutoConfigUrl != NULL)
+                            {
+                                WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions;
+                                memset(&autoProxyOptions, 0, sizeof(autoProxyOptions));
+                                autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT | WINHTTP_AUTOPROXY_CONFIG_URL;
+                                autoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP;
+                                autoProxyOptions.lpszAutoConfigUrl = proxyConfig.lpszAutoConfigUrl;
+                                autoProxyOptions.fAutoLogonIfChallenged = TRUE;
+                                autoProxyOptions.dwReserved = 0;
+                                autoProxyOptions.lpvReserved = NULL;
+
+                                WINHTTP_PROXY_INFO proxyInfo;
+                                memset(&proxyInfo, 0, sizeof(proxyInfo));
+
+                                if (::WinHttpGetProxyForUrl(m_sessionHandle, m_requestURL.c_str(), &autoProxyOptions, &proxyInfo))
+                                {
+                                    if (::WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY, &proxyInfo, sizeof(proxyInfo)))
+                                    {
+                                        if (::WinHttpSendRequest(hRequest,
+                                                                 WINHTTP_NO_ADDITIONAL_HEADERS,
+                                                                 0,
+                                                                 WINHTTP_NO_REQUEST_DATA,
+                                                                 0,
+                                                                 0,
+                                                                 NULL))
+                                        {
+                                            bSendRequestSucceed = true;
+                                        }
+                                    }
+                                    if (proxyInfo.lpszProxy != NULL)
+                                    {
+                                        ::GlobalFree(proxyInfo.lpszProxy);
+                                    }
+                                    if (proxyInfo.lpszProxyBypass != NULL)
+                                    {
+                                        ::GlobalFree(proxyInfo.lpszProxyBypass);
+                                    }
+                                }
+                                else
+                                {
+                                    m_dwLastError = ::GetLastError();
+                                }
+                            }
+                            else if (proxyConfig.lpszProxy != NULL)
+                            {
+                                WINHTTP_PROXY_INFO proxyInfo;
+                                memset(&proxyInfo, 0, sizeof(proxyInfo));
+                                proxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+                                wchar_t szProxy[MAX_PATH] = L"";
+                                wcscpy_s(szProxy, MAX_PATH, proxyConfig.lpszProxy);
+                                proxyInfo.lpszProxy = szProxy;
+
+                                if (proxyConfig.lpszProxyBypass != NULL)
+                                {
+                                    wchar_t szProxyBypass[MAX_PATH] = L"";
+                                    wcscpy_s(szProxyBypass, MAX_PATH, proxyConfig.lpszProxyBypass);
+                                    proxyInfo.lpszProxyBypass = szProxyBypass;
+                                }
+
+                                if (!::WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY, &proxyInfo, sizeof(proxyInfo)))
+                                {
+                                    m_dwLastError = ::GetLastError();
+                                }
+                            }
+
+                            if (proxyConfig.lpszAutoConfigUrl != NULL)
+                            {
+                                ::GlobalFree(proxyConfig.lpszAutoConfigUrl);
+                            }
+                            if (proxyConfig.lpszProxy != NULL)
+                            {
+                                ::GlobalFree(proxyConfig.lpszProxy);
+                            }
+                            if (proxyConfig.lpszProxyBypass != NULL)
+                            {
+                                ::GlobalFree(proxyConfig.lpszProxyBypass);
+                            }
+                        }
+                        else
+                        {
+                            m_dwLastError = ::GetLastError();
+                        }
+                    }
+                    if (bSendRequestSucceed)
+                    {
+                        if (m_pDataToSend != NULL)
+                        {
+                            DWORD dwWritten = 0;
+                            if (!::WinHttpWriteData(hRequest,
+                                                    m_pDataToSend,
+                                                    m_dataToSendSize,
+                                                    &dwWritten))
+                            {
+                                m_dwLastError = ::GetLastError();
+                            }
+                        }
+                        if (::WinHttpReceiveResponse(hRequest, NULL))
+                        {
+                            DWORD dwSize = 0;
+                            BOOL bResult = FALSE;
+                            bResult = ::WinHttpQueryHeaders(hRequest,
+                                                            WINHTTP_QUERY_RAW_HEADERS_CRLF,
+                                                            WINHTTP_HEADER_NAME_BY_INDEX,
+                                                            NULL,
+                                                            &dwSize,
+                                                            WINHTTP_NO_HEADER_INDEX);
+                            if (bResult || (!bResult && (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)))
+                            {
+                                wchar_t *szHeader = new wchar_t[dwSize];
+                                if (szHeader != NULL)
+                                {
+                                    memset(szHeader, 0, dwSize* sizeof(wchar_t));
+                                    if (::WinHttpQueryHeaders(hRequest,
+                                                              WINHTTP_QUERY_RAW_HEADERS_CRLF,
+                                                              WINHTTP_HEADER_NAME_BY_INDEX,
+                                                              szHeader,
+                                                              &dwSize,
+                                                              WINHTTP_NO_HEADER_INDEX))
+                                    {
+                                        m_responseHeader.assign(szHeader);
+                                        vector<wstring> result;
+                                        wstring regExp = L"";
+                                        if (!m_bForceCharset)
+                                        {
+                                            regExp = L"charset={[A-Za-z0-9\\-_]+}";
+                                            if (ParseRegExp(regExp, false, 1, m_responseHeader, result) && result.size() > 0)
+                                            {
+                                                m_responseCharset = result[0];
+                                            }
+                                        }
+                                        regExp = L"Content-Length: {[0-9]+}";
+                                        if (ParseRegExp(regExp, false, 1, m_responseHeader, result) && result.size() > 0)
+                                        {
+                                            m_responseByteCount = (unsigned int)_wtoi(result[0].c_str());
+                                        }
+                                        regExp = L"Location: {[0-9]+}";
+                                        if (ParseRegExp(regExp, false, 1, m_responseHeader, result) && result.size() > 0)
+                                        {
+                                            m_location = result[0];
+                                        }
+                                        regExp = L"Set-Cookie:\\b*{.+?}\\n";
+                                        if (ParseRegExp(regExp, false, 1, m_responseHeader, result) && result.size() > 0)
+                                        {
+                                            for (vector<wstring>::size_type i = 0; i < result.size(); i++)
+                                            {
+                                                m_responseCookies += result[i];
+                                                if (i != result.size() - 1)
+                                                {
+                                                    m_responseCookies += L"; ";
+                                                }
+                                            }
+                                            m_responseCookies = Trim(m_responseCookies, L" ");
+                                            if (m_responseCookies.size() > 0 && m_responseCookies[m_responseCookies.size() - 1] != L';')
+                                            {
+                                                m_responseCookies += L";";
+                                            }
+                                        }
+                                    }
+                                    delete[] szHeader;
+                                }
+                            }
+                            
+                            dwSize = 0;
+                            bResult = ::WinHttpQueryHeaders(hRequest,
+                                                            WINHTTP_QUERY_STATUS_CODE,
+                                                            WINHTTP_HEADER_NAME_BY_INDEX,
+                                                            NULL,
+                                                            &dwSize,
+                                                            WINHTTP_NO_HEADER_INDEX);
+                            if (bResult || (!bResult && (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)))
+                            {
+                                wchar_t *szStatusCode = new wchar_t[dwSize];
+                                if (szStatusCode != NULL)
+                                {
+                                    memset(szStatusCode, 0, dwSize* sizeof(wchar_t));
+                                    if (::WinHttpQueryHeaders(hRequest,
+                                                              WINHTTP_QUERY_STATUS_CODE,
+                                                              WINHTTP_HEADER_NAME_BY_INDEX,
+                                                              szStatusCode,
+                                                              &dwSize,
+                                                              WINHTTP_NO_HEADER_INDEX))
+                                    {
+                                        m_statusCode = szStatusCode;
+                                    }
+                                    delete[] szStatusCode;
+                                }
+                            }
+
+                            unsigned int iMaxBufferSize = INT_BUFFERSIZE;
+                            unsigned int iCurrentBufferSize = 0;
+                            if (m_pResponse != NULL)
+                            {
+                                delete[] m_pResponse;
+                                m_pResponse = NULL;
+                            }
+                            m_pResponse = new BYTE[iMaxBufferSize];
+                            if (m_pResponse == NULL)
+                            {
+                                bRetVal = false;
+                                break;
+                            }
+                            memset(m_pResponse, 0, iMaxBufferSize);
+                            do
+                            {
+                                dwSize = 0;
+                                if (::WinHttpQueryDataAvailable(hRequest, &dwSize))
+                                {
+                                    SetProgress(iCurrentBufferSize);
+                                    BYTE *pResponse = new BYTE[dwSize + 1];
+                                    if (pResponse != NULL)
+                                    {
+                                        memset(pResponse, 0, (dwSize + 1)*sizeof(BYTE));
+                                        DWORD dwRead = 0;
+                                        if (::WinHttpReadData(hRequest,
+                                                              pResponse,
+                                                              dwSize,
+                                                              &dwRead))
+                                        {
+                                            if (dwRead + iCurrentBufferSize > iMaxBufferSize)
+                                            {
+                                                BYTE *pOldBuffer = m_pResponse;
+                                                m_pResponse = new BYTE[iMaxBufferSize * 2];
+                                                if (m_pResponse == NULL)
+                                                {
+                                                    m_pResponse = pOldBuffer;
+                                                    bRetVal = false;
+                                                    break;
+                                                }
+                                                iMaxBufferSize *= 2;
+                                                memset(m_pResponse, 0, iMaxBufferSize);
+                                                memcpy(m_pResponse, pOldBuffer, iCurrentBufferSize);
+                                                delete[] pOldBuffer;
+                                            }
+                                            memcpy(m_pResponse + iCurrentBufferSize, pResponse, dwRead);
+                                            iCurrentBufferSize += dwRead;
+                                        }
+                                        delete[] pResponse;
+                                    }
+                                }
+                                else
+                                {
+                                    m_dwLastError = ::GetLastError();
+                                }
+                            }
+                            while (dwSize > 0);
+                            SetProgress(iCurrentBufferSize);
+                            m_responseByteCountReceived = iCurrentBufferSize;
+
+                            UINT codePage = CP_ACP;
+                            DWORD dwFlag = MB_PRECOMPOSED;
+                            if (_wcsnicmp(m_responseCharset.c_str(), L"utf-8", 5) == 0)
+                            {
+                                codePage = CP_UTF8;
+                                dwFlag = 0;
+                            }
+                            int iLength = ::MultiByteToWideChar(codePage,
+                                                                dwFlag, 
+                                                                (LPCSTR)m_pResponse, 
+                                                                m_responseByteCountReceived + 1, 
+                                                                NULL, 
+                                                                0);
+                            if (iLength <= 0)
+                            {
+                                // Use CP_ACP if UTF-8 fail
+                                codePage = CP_ACP;
+                                dwFlag = MB_PRECOMPOSED;
+                                iLength = ::MultiByteToWideChar(codePage,
+                                                                dwFlag, 
+                                                                (LPCSTR)m_pResponse, 
+                                                                m_responseByteCountReceived + 1, 
+                                                                NULL, 
+                                                                0);
+                            }
+                            if (iLength > 0)
+                            {
+                                wchar_t *wideChar = new wchar_t[iLength];
+                                if (wideChar != NULL)
+                                {
+                                    memset(wideChar, 0, iLength * sizeof(wchar_t));
+                                    iLength = ::MultiByteToWideChar(codePage,
+                                                                    dwFlag, 
+                                                                    (LPCSTR)m_pResponse, 
+                                                                    m_responseByteCountReceived + 1, 
+                                                                    wideChar, 
+                                                                    iLength);
+                                    if (iLength > 0)
+                                    {
+                                        m_responseContent = wideChar;
+                                    }
+                                    delete[] wideChar;
+                                }
+                            }
+                            bGetReponseSucceed = true;
+
+                            // If the resposne html web page size is less than 200, retry.
+                            if (verb == L"GET" && !disableAutoRedirect)
+                            {
+                                wstring regExp = L"{<html>}";
+                                vector<wstring> result;
+                                if (ParseRegExp(regExp, false, 1, m_responseContent, result) && result.size() > 0)
+                                {
+                                    regExp = L"{</html>}";
+                                    if (!ParseRegExp(regExp, false, 1, m_responseContent, result) || result.size() <= 0)
+                                    {
+                                        m_dwLastError = ERROR_INVALID_DATA;
+                                        bGetReponseSucceed = false;
+                                    }
+                                }
+                            }
+                        }
+                        else
+                        {
+                            m_dwLastError = ::GetLastError();
+                        }
+                    }
+                } // while
+                if (!bGetReponseSucceed)
+                {
+                    bRetVal = false;
+                }
+
+                ::WinHttpCloseHandle(hRequest);
+            }
+            ::WinHttpCloseHandle(hConnect);
+        }
+
+    }
+
+    return bRetVal;
+}
+
+wstring WinHttpClient::GetResponseHeader(void)
+{
+    return m_responseHeader;
+}
+
+wstring WinHttpClient::GetResponseContent(void)
+{
+    return m_responseContent;
+}
+
+wstring WinHttpClient::GetResponseCharset(void)
+{
+    return m_responseCharset;
+}
+
+wstring WinHttpClient::GetRequestHost(void)
+{
+    return m_requestHost;
+}
+
+bool WinHttpClient::SaveResponseToFile(const wstring &filePath)
+{
+    if (m_pResponse == NULL || m_responseByteCountReceived <= 0)
+    {
+        return false;
+    }
+    FILE *f = NULL;
+    int iResult = _wfopen_s(&f, filePath.c_str(), L"wb");
+    if (iResult == 0 && f != NULL)
+    {
+        fwrite(m_pResponse, m_responseByteCountReceived, 1, f);
+        fclose(f);
+        return true;
+    }
+
+    return false;
+}
+
+bool WinHttpClient::SetProgress(unsigned int byteCountReceived)
+{
+    bool bReturn = false;
+    if (m_pfProcessProc != NULL && m_responseByteCount > 0)
+    {
+        double dProgress = (double)byteCountReceived * 100 / m_responseByteCount;
+        m_pfProcessProc(dProgress);
+        bReturn = true;
+    }
+
+    return bReturn;
+}
+
+wstring WinHttpClient::GetResponseCookies(void)
+{
+    return m_responseCookies;
+}
+
+bool WinHttpClient::SetAdditionalRequestCookies(const wstring &cookies)
+{
+    m_additionalRequestCookies = cookies;
+
+    return true;
+}
+
+bool WinHttpClient::SetAdditionalDataToSend(BYTE *data, unsigned int dataSize)
+{
+    if (data == NULL || dataSize < 0)
+    {
+        return false;
+    }
+
+    if (m_pDataToSend != NULL)
+    {
+        delete[] m_pDataToSend;
+    }
+    m_pDataToSend = NULL;
+    m_pDataToSend = new BYTE[dataSize];
+    if (m_pDataToSend != NULL)
+    {
+        memcpy(m_pDataToSend, data, dataSize);
+        m_dataToSendSize = dataSize;
+        return true;
+    }
+
+    return false;
+}
+
+// Reset additional data fields
+bool WinHttpClient::ResetAdditionalDataToSend(void)
+{
+    if (m_pDataToSend != NULL)
+    {
+        delete[] m_pDataToSend;
+    }
+
+    m_pDataToSend = NULL;
+    m_dataToSendSize = 0;
+
+    return true;
+}
+
+// Allow us to reset the url on subsequent requests
+bool WinHttpClient::UpdateUrl(const wstring &url)
+{
+    m_requestURL = url;
+    ResetAdditionalDataToSend();
+
+    return true;
+}
+
+bool WinHttpClient::SetAdditionalRequestHeaders(const wstring &additionalRequestHeaders)
+{
+    m_additionalRequestHeaders = additionalRequestHeaders;
+
+    return true;
+}
+
+bool WinHttpClient::SetProxy(const wstring &proxy)
+{
+    m_proxy = proxy;
+
+    return true;
+}
+
+// If we don't require valid SSL Certs then accept any
+// certificate on an SSL connection
+bool WinHttpClient::SetRequireValidSslCertificates(bool require)
+{
+    m_requireValidSsl = require;
+
+    return true;
+}
+
+const BYTE *WinHttpClient::GetRawResponseContent(void)
+{
+    return m_pResponse;
+}
+
+unsigned int WinHttpClient::GetRawResponseContentLength(void)
+{
+    return m_responseByteCount;
+}
+
+unsigned int WinHttpClient::GetRawResponseReceivedContentLength(void)
+{
+    return m_responseByteCountReceived;
+}
+
+DWORD WinHttpClient::GetLastError(void)
+{
+    return m_dwLastError;
+}
+
+wstring WinHttpClient::GetResponseStatusCode(void)
+{
+    return m_statusCode;
+}
+
+bool WinHttpClient::SetUserAgent(const wstring &userAgent)
+{
+    m_userAgent = userAgent;
+
+    return true;
+}
+
+bool WinHttpClient::SetForceCharset(const wstring &charset)
+{
+    m_responseCharset = charset;
+
+    return true;
+}
+
+bool WinHttpClient::SetProxyUsername(const wstring &username)
+{
+    m_proxyUsername = username;
+
+    return true;
+}
+
+bool WinHttpClient::SetProxyPassword(const std::wstring &password)
+{
+    m_proxyPassword = password;
+
+    return true;
+}
+    
+wstring WinHttpClient::GetResponseLocation(void)
+{
+    return m_location;
+}
+
+bool WinHttpClient::SetTimeouts(unsigned int resolveTimeout,
+                                unsigned int connectTimeout,
+                                unsigned int sendTimeout,
+                                unsigned int receiveTimeout)
+{
+    m_resolveTimeout = resolveTimeout;
+    m_connectTimeout = connectTimeout;
+    m_sendTimeout = sendTimeout;
+    m_receiveTimeout = receiveTimeout;
+
+    return true;
+}
+
+#endif // WINHTTPCLIENT_H

+ 2013 - 0
ARS Server/include/WinHttpClient/ThirdParty/ATLRegExp/atlrx.h

@@ -0,0 +1,2013 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLRX_H__
+#define __ATLRX_H__
+
+#pragma once
+
+#include <atlbase.h>
+#include <atlcoll.h>
+#include <mbstring.h>
+
+#ifndef ATL_REGEXP_MIN_STACK
+#define ATL_REGEXP_MIN_STACK 256
+#endif
+
+/* 
+	Regular Expression Grammar
+
+	R    - top level grammar rule
+	RE   - regular expression
+	AltE - Alternative expression
+	E    - expression
+	SE   - simple expression
+
+	R -> RE
+		 '^'RE		(matches begining of string)
+
+	RE -> AltE RE
+		  AltE
+
+
+	AltE -> E
+			E '|' AltE
+	E -> SE (RepeatOp '?'?)?
+	SE -> Arg
+		Group
+		CharClass
+		'\'Abbrev		(see below)
+		'\'EscapedChar	(any character including reserved symbols)
+		'\'Digit+    (Arg back reference)
+		'!'   (not)
+		'.'   (any char)
+		'$'   (end of input)
+		Symbol			(any non-reserved character)
+	Arg -> '{'RE'}'
+	Group -> '('RE')'
+	CharClass -> '[' '^'? CharSet ']'
+	CharSet -> CharItem+
+	CharItem -> Char('-'Char)?
+	RepeatOp ->  '*'
+				 '+'
+				 '?'
+	Abbrev -> Abbreviation defined in CAtlRECharTraits
+		Abbrev  Expansion					Meaning
+		a		([a-zA-Z0-9])				alpha numeric
+		b		([ \\t])					white space (blank)
+		c		([a-zA-Z])					alpha
+		d		([0-9])						digit
+		h		([0-9a-fA-F])				hex digit
+		n		(\r|(\r?\n))				newline
+		q		(\"[^\"]*\")|(\'[^\']*\')	quoted string
+		w		([a-zA-Z]+)					simple word
+		z		([0-9]+)					integer
+*/
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL {
+
+//Convertion utility classes used to convert char* to RECHAR.
+//Used by rx debugging printing.
+template <typename RECHARTYPE=char>
+class CAToREChar
+{
+public:
+	CAToREChar(const char* psz) throw()
+	: m_psz(psz)
+	{
+	}
+	operator const RECHARTYPE*() const throw() { return m_psz; }
+	const char* m_psz;
+};
+
+template<>
+class CAToREChar<wchar_t>
+{
+public:
+	CAToREChar(const char* psz) throw()
+	: m_a2w(psz)
+	{
+	}
+	operator const wchar_t*() const throw() { return (wchar_t*)m_a2w; }
+	
+private:
+	CA2W m_a2w;
+};
+
+class CAtlRECharTraitsA
+{
+public:
+	typedef char RECHARTYPE;
+
+	static size_t GetBitFieldForRangeArrayIndex(const RECHARTYPE *sz) throw()
+	{
+#ifndef ATL_NO_CHECK_BIT_FIELD
+		ATLASSERT(UseBitFieldForRange());
+#endif
+		return static_cast<size_t>(static_cast<unsigned char>(*sz));		
+	}
+	static RECHARTYPE *Next(const RECHARTYPE *sz) throw()
+	{
+		return (RECHARTYPE *) (sz+1);
+	}
+
+	static int Strncmp(const RECHARTYPE *szLeft, const RECHARTYPE *szRight, size_t nCount) throw()
+	{
+		return strncmp(szLeft, szRight, nCount);
+	}
+
+	static int Strnicmp(const RECHARTYPE *szLeft, const RECHARTYPE *szRight, size_t nCount) throw()
+	{
+		return _strnicmp(szLeft, szRight, nCount);
+	}
+
+	_ATL_INSECURE_DEPRECATE("CAtlRECharTraitsA::Strlwr must be passed a buffer size.")
+	static RECHARTYPE *Strlwr(RECHARTYPE *sz) throw()
+	{
+		#pragma warning (push)
+		#pragma warning(disable : 4996)
+		return _strlwr(sz);
+		#pragma warning (pop)
+	}
+
+	static RECHARTYPE *Strlwr(RECHARTYPE *sz, int nSize) throw()
+	{
+		Checked::strlwr_s(sz, nSize);
+		return sz;
+	}
+
+	static long Strtol(const RECHARTYPE *sz, RECHARTYPE **szEnd, int nBase) throw()
+	{
+		return strtol(sz, szEnd, nBase);
+	}
+
+	static int Isdigit(RECHARTYPE ch) throw()
+	{
+		return isdigit(static_cast<unsigned char>(ch));
+	}
+
+	static const RECHARTYPE** GetAbbrevs()
+	{
+		static const RECHARTYPE *s_szAbbrevs[] = 
+		{
+			"a([a-zA-Z0-9])",	// alpha numeric
+			"b([ \\t])",		// white space (blank)
+			"c([a-zA-Z])",	// alpha
+			"d([0-9])",		// digit
+			"h([0-9a-fA-F])",	// hex digit
+			"n(\r|(\r?\n))",	// newline
+			"q(\"[^\"]*\")|(\'[^\']*\')",	// quoted string
+			"w([a-zA-Z]+)",	// simple word
+			"z([0-9]+)",		// integer
+			NULL
+		};
+
+		return s_szAbbrevs;
+	}
+
+	static BOOL UseBitFieldForRange() throw()
+	{
+		return TRUE;
+	}
+
+	static int ByteLen(const RECHARTYPE *sz) throw()
+	{
+		return int(strlen(sz));
+	}
+};
+
+class CAtlRECharTraitsW
+{
+public:
+	typedef WCHAR RECHARTYPE;
+	
+	static size_t GetBitFieldForRangeArrayIndex(const RECHARTYPE *sz) throw()
+	{		
+#ifndef ATL_NO_CHECK_BIT_FIELD
+		ATLASSERT(UseBitFieldForRange());
+#endif
+		return static_cast<size_t>(*sz);
+	}
+	static RECHARTYPE *Next(const RECHARTYPE *sz) throw()
+	{
+		return (RECHARTYPE *) (sz+1);
+	}
+
+	static int Strncmp(const RECHARTYPE *szLeft, const RECHARTYPE *szRight, size_t nCount) throw()
+	{
+		return wcsncmp(szLeft, szRight, nCount);
+	}
+
+	static int Strnicmp(const RECHARTYPE *szLeft, const RECHARTYPE *szRight, size_t nCount) throw()
+	{
+		return _wcsnicmp(szLeft, szRight, nCount);
+	}
+
+	_ATL_INSECURE_DEPRECATE("CAtlRECharTraitsW::Strlwr must be passed a buffer size.")
+	static RECHARTYPE *Strlwr(RECHARTYPE *sz) throw()
+	{
+		#pragma warning (push)
+		#pragma warning(disable : 4996)
+		return _wcslwr(sz);
+		#pragma warning (pop)
+	}
+
+	static RECHARTYPE *Strlwr(RECHARTYPE *sz, int nSize) throw()
+	{
+		Checked::wcslwr_s(sz, nSize);
+		return sz;
+	}
+
+	static long Strtol(const RECHARTYPE *sz, RECHARTYPE **szEnd, int nBase) throw()
+	{
+		return wcstol(sz, szEnd, nBase);
+	}
+
+	static int Isdigit(RECHARTYPE ch) throw()
+	{
+		return iswdigit(ch);
+	}
+
+	static const RECHARTYPE** GetAbbrevs()
+	{
+		static const RECHARTYPE *s_szAbbrevs[] = 
+		{
+			L"a([a-zA-Z0-9])",	// alpha numeric
+			L"b([ \\t])",		// white space (blank)
+			L"c([a-zA-Z])",	// alpha
+			L"d([0-9])",		// digit
+			L"h([0-9a-fA-F])",	// hex digit
+			L"n(\r|(\r?\n))",	// newline
+			L"q(\"[^\"]*\")|(\'[^\']*\')",	// quoted string
+			L"w([a-zA-Z]+)",	// simple word
+			L"z([0-9]+)",		// integer
+			NULL
+		};
+
+		return s_szAbbrevs;
+	}
+
+	static BOOL UseBitFieldForRange() throw()
+	{
+		return FALSE;
+	}
+
+	static int ByteLen(const RECHARTYPE *sz) throw()
+	{
+		return int(wcslen(sz)*sizeof(WCHAR));
+	}
+};
+
+class CAtlRECharTraitsMB
+{
+public:
+	typedef unsigned char RECHARTYPE;
+
+	static size_t GetBitFieldForRangeArrayIndex(const RECHARTYPE *sz) throw()
+	{		
+#ifndef ATL_NO_CHECK_BIT_FIELD
+		ATLASSERT(UseBitFieldForRange());
+#endif
+
+		return static_cast<size_t>(*sz);		
+	}
+
+	static RECHARTYPE *Next(const RECHARTYPE *sz) throw()
+	{
+		return _mbsinc(sz);
+	}
+
+	static int Strncmp(const RECHARTYPE *szLeft, const RECHARTYPE *szRight, size_t nCount) throw()
+	{
+		return _mbsncmp(szLeft, szRight, nCount);
+	}
+
+	static int Strnicmp(const RECHARTYPE *szLeft, const RECHARTYPE *szRight, size_t nCount) throw()
+	{
+		return _mbsnicmp(szLeft, szRight, nCount);
+	}
+
+	_ATL_INSECURE_DEPRECATE("CAtlRECharTraitsMB::Strlwr must be passed a buffer size.")
+	static RECHARTYPE *Strlwr(RECHARTYPE *sz) throw()
+	{
+		#pragma warning (push)
+		#pragma warning(disable : 4996)
+		return _mbslwr(sz);
+		#pragma warning (pop)
+	}
+
+	static RECHARTYPE *Strlwr(RECHARTYPE *sz, int nSize) throw()
+	{
+		Checked::mbslwr_s(sz, nSize);
+		return sz;
+	}
+
+	static long Strtol(const RECHARTYPE *sz, RECHARTYPE **szEnd, int nBase) throw()
+	{
+		return strtol((const char *) sz, (char **) szEnd, nBase);
+	}
+
+	static int Isdigit(RECHARTYPE ch) throw()
+	{
+		return _ismbcdigit((unsigned int) ch);
+	}
+
+	static const RECHARTYPE** GetAbbrevs()
+	{
+		return reinterpret_cast<const RECHARTYPE **>(CAtlRECharTraitsA::GetAbbrevs());
+	}
+
+	static BOOL UseBitFieldForRange() throw()
+	{
+		return FALSE;
+	}
+
+	static int ByteLen(const RECHARTYPE *sz) throw()
+	{
+		return (int)strlen((const char *) sz);
+	}
+};
+
+#ifndef _UNICODE
+typedef CAtlRECharTraitsA CAtlRECharTraits;
+#else	// _UNICODE
+typedef CAtlRECharTraitsW CAtlRECharTraits;
+#endif // !_UNICODE
+// Note: If you want to use CAtlRECharTraitsMB you must pass it in
+// as a template argument
+
+template <class CharTraits=CAtlRECharTraits>
+class CAtlRegExp;	// forward declaration
+
+template <class CharTraits=CAtlRECharTraits>
+class CAtlREMatchContext
+{
+public:
+	friend CAtlRegExp<CharTraits>;
+	typedef typename CharTraits::RECHARTYPE RECHAR;
+
+	struct MatchGroup
+	{
+		const RECHAR *szStart;
+		const RECHAR *szEnd;
+	};
+
+	UINT m_uNumGroups;
+
+	MatchGroup m_Match;
+
+	void GetMatch(UINT nIndex, const RECHAR **szStart, const RECHAR **szEnd)
+	{
+		ATLENSURE(szStart != NULL);
+		ATLENSURE(szEnd != NULL);
+		ATLENSURE(nIndex >=0 && nIndex < m_uNumGroups);
+		*szStart = m_Matches[nIndex].szStart;
+		*szEnd = m_Matches[nIndex].szEnd;
+	}
+
+	void GetMatch(UINT nIndex, MatchGroup *pGroup)
+	{
+		 
+		ATLENSURE(pGroup != NULL);
+		ATLENSURE(nIndex >=0&&(static_cast<UINT>(nIndex))< m_uNumGroups);
+		pGroup->szStart = m_Matches[nIndex].szStart;
+		pGroup->szEnd = m_Matches[nIndex].szEnd;
+	}
+
+protected:
+	CAutoVectorPtr<void *> m_Mem;
+	CAutoVectorPtr<MatchGroup> m_Matches;
+	CAtlArray<void *> m_stack;
+	size_t m_nTos;
+
+public:
+	CAtlREMatchContext(size_t nInitStackSize=ATL_REGEXP_MIN_STACK)
+	{
+		m_uNumGroups = 0;
+		m_nTos = 0;
+		m_stack.SetCount(nInitStackSize);
+		m_Match.szStart = NULL;
+		m_Match.szEnd = NULL;
+	}
+
+protected:
+	BOOL Initialize(UINT uRequiredMem, UINT uNumGroups) throw()
+	{
+		m_nTos = 0;
+
+		m_uNumGroups = 0;
+		m_Matches.Free();
+
+		if (!m_Matches.Allocate(uNumGroups))
+			return FALSE;
+
+		m_uNumGroups = uNumGroups;
+
+		m_Mem.Free();
+
+		if (!m_Mem.Allocate(uRequiredMem))
+			return FALSE;
+
+		memset(m_Mem.m_p, 0x00, uRequiredMem*sizeof(void *));
+
+		memset(m_Matches, 0x00, m_uNumGroups * sizeof(MatchGroup));
+		return TRUE;
+	}
+
+	BOOL Push(void *p)
+	{
+		m_nTos++;
+		if (m_stack.GetCount() <= (UINT) m_nTos)
+		{
+			if (!m_stack.SetCount((m_nTos+1)*2))
+			{
+				m_nTos--;
+				return FALSE;
+			}
+		}
+		m_stack[m_nTos] = p;
+		return TRUE;
+	}
+
+	BOOL Push(size_t n)
+	{
+		return Push((void *) n);
+	}
+
+	void *Pop() throw()
+	{
+		if (m_nTos==0)
+		{
+			// stack underflow
+			// this should never happen at match time.
+			// (the parsing succeeded when it shouldn't have)
+			ATLASSERT(FALSE);
+			return NULL;
+		}
+		void *p = m_stack[m_nTos];
+		m_nTos--;
+		return p;
+	}
+};
+
+enum REParseError {
+	REPARSE_ERROR_OK = 0,				// No error occurred
+	REPARSE_ERROR_OUTOFMEMORY,			// Out of memory
+	REPARSE_ERROR_BRACE_EXPECTED,		// A closing brace was expected
+	REPARSE_ERROR_PAREN_EXPECTED,		// A closing parenthesis was expected
+	REPARSE_ERROR_BRACKET_EXPECTED,		// A closing bracket was expected
+	REPARSE_ERROR_UNEXPECTED,			// An unspecified fatal error occurred
+	REPARSE_ERROR_EMPTY_RANGE,			// A range expression was empty
+	REPARSE_ERROR_INVALID_GROUP,		// A backreference was made to a group
+										// that did not exist
+	REPARSE_ERROR_INVALID_RANGE,		// An invalid range was specified
+	REPARSE_ERROR_EMPTY_REPEATOP,		// A possibly empty * or + was detected
+	REPARSE_ERROR_INVALID_INPUT,		// The input string was invalid
+};
+
+template <class CharTraits /* =CAtlRECharTraits */>
+class CAtlRegExp
+{
+public:
+	CAtlRegExp() throw()
+	{
+		m_uNumGroups = 0;
+		m_uRequiredMem = 0;
+		m_bCaseSensitive = TRUE;
+		m_LastError = REPARSE_ERROR_OK;
+	}
+
+	typedef typename CharTraits::RECHARTYPE RECHAR;
+
+	// CAtlRegExp::Parse
+	// Parses the regular expression
+	// returns REPARSE_ERROR_OK if successful, an REParseError otherwise
+	REParseError Parse(const RECHAR *szRE, BOOL bCaseSensitive=TRUE)
+	{
+		ATLASSERT(szRE);
+		if (!szRE)
+			return REPARSE_ERROR_INVALID_INPUT;
+
+		Reset();
+
+		m_bCaseSensitive = bCaseSensitive;
+
+		const RECHAR *szInput = szRE;
+
+		if (!bCaseSensitive)
+		{
+			// copy the string
+			int nSize = CharTraits::ByteLen(szRE)+sizeof(RECHAR);
+			szInput = (const RECHAR *) malloc(nSize);
+			if (!szInput)
+				return REPARSE_ERROR_OUTOFMEMORY;
+
+			Checked::memcpy_s((char *) szInput, nSize, szRE, nSize);
+
+			CharTraits::Strlwr(const_cast<RECHAR *>(szInput), nSize/sizeof(RECHAR));
+		}
+		const RECHAR *sz = szInput;
+
+		int nCall = AddInstruction(RE_CALL);
+		if (nCall < 0)
+			return REPARSE_ERROR_OUTOFMEMORY;
+
+		if (*sz == '^')
+		{
+			if (AddInstruction(RE_FAIL) < 0)
+				return REPARSE_ERROR_OUTOFMEMORY;
+			sz++;
+		}
+		else
+		{
+			if (AddInstruction(RE_ADVANCE) < 0)
+				return REPARSE_ERROR_OUTOFMEMORY;
+		}
+
+		bool bEmpty = true;
+		ParseRE(&sz, bEmpty);
+		if (!GetLastParseError())
+		{
+			GetInstruction(nCall).call.nTarget = 2;
+
+			if (AddInstruction(RE_MATCH) < 0)
+				return REPARSE_ERROR_OUTOFMEMORY;
+		}
+
+		if (szInput != szRE)
+			free((void *) szInput);
+
+		return GetLastParseError();
+	}
+
+	BOOL Match(const RECHAR *szIn, CAtlREMatchContext<CharTraits> *pContext, const RECHAR **ppszEnd=NULL)
+	{
+		ATLASSERT(szIn);
+		ATLASSERT(pContext);
+
+		if (!szIn || !pContext)
+			return FALSE;
+
+		if (ppszEnd)
+			*ppszEnd = NULL;
+
+		const RECHAR *szInput = szIn;
+
+		if (!m_bCaseSensitive)
+		{
+			int nSize = CharTraits::ByteLen(szIn)+sizeof(RECHAR);
+			szInput = (const RECHAR *) malloc(nSize);
+			if (!szInput)
+				return FALSE;
+
+			Checked::memcpy_s((char *) szInput, nSize, szIn, nSize);
+			CharTraits::Strlwr(const_cast<RECHAR *>(szInput), nSize/sizeof(RECHAR));
+		}
+
+		if (!pContext->Initialize(m_uRequiredMem, m_uNumGroups))
+		{
+			if (szInput != szIn)
+				free((void *) szInput);
+			return FALSE;
+		}
+
+		size_t ip = 0;
+
+		const RECHAR *sz = szInput;
+		const RECHAR *szCurrInput = szInput;
+
+#pragma warning(push)
+#pragma warning(disable:4127) // conditional expression is constant
+
+		while (1)
+		{
+#ifdef ATLRX_DEBUG
+			OnDebugEvent(ip, szInput, sz, pContext);
+#endif
+			if (ip == 0)
+				pContext->m_Match.szStart = sz;
+
+			switch (GetInstruction(ip).type)
+ 			{
+			case RE_NOP:
+				ip++;
+				break;
+
+			case RE_SYMBOL:
+				if (GetInstruction(ip).symbol.nSymbol == static_cast<size_t>(*sz))
+				{
+					sz = CharTraits::Next(sz);
+					ip++;
+				}
+				else
+				{
+					ip = (size_t) pContext->Pop();
+				}
+				break;
+
+			case RE_ANY:
+				if (*sz)
+				{
+					sz = CharTraits::Next(sz);
+					ip++;
+				}
+				else
+				{
+					ip = (size_t) pContext->Pop();
+				}
+				break;
+
+			case RE_GROUP_START:
+				pContext->m_Matches[GetInstruction(ip).group.nGroup].szStart = sz;
+				ip++;
+				break;
+
+			case RE_GROUP_END:
+				pContext->m_Matches[GetInstruction(ip).group.nGroup].szEnd = sz;
+				ip++;
+				break;
+
+			case RE_PUSH_CHARPOS:
+				pContext->Push((void *) sz);
+				ip++;
+				break;
+
+			case RE_POP_CHARPOS:
+				sz = (RECHAR *) pContext->Pop();
+				ip++;
+				break;
+
+			case RE_CALL:
+				pContext->Push(ip+1);
+				ip = GetInstruction(ip).call.nTarget;
+				break;
+
+			case RE_JMP:
+				ip = GetInstruction(ip).jmp.nTarget;
+				break;
+
+			case RE_RETURN:
+				ip = (size_t) pContext->Pop();
+				break;
+
+			case RE_PUSH_MEMORY:
+				pContext->Push((void *) (pContext->m_Mem[GetInstruction(ip).memory.nIndex]));
+				ip++;
+				break;
+
+			case RE_POP_MEMORY:
+				pContext->m_Mem[GetInstruction(ip).memory.nIndex] = pContext->Pop();
+				ip++;
+				break;
+
+			case RE_STORE_CHARPOS:
+				pContext->m_Mem[GetInstruction(ip).memory.nIndex] = (void *) sz;
+				ip++;
+				break;
+
+			case RE_GET_CHARPOS:
+				sz = (RECHAR *) pContext->m_Mem[GetInstruction(ip).memory.nIndex];
+				ip++;
+				break;
+
+			case RE_STORE_STACKPOS:
+				pContext->m_Mem[GetInstruction(ip).memory.nIndex] = (void *) pContext->m_nTos;
+				ip++;
+				break;
+
+			case RE_GET_STACKPOS:
+				pContext->m_nTos = (size_t) pContext->m_Mem[GetInstruction(ip).memory.nIndex];
+				ip++;
+				break;
+
+			case RE_RET_NOMATCH:
+				if (sz == (RECHAR *) pContext->m_Mem[GetInstruction(ip).memory.nIndex])
+				{
+					// do a return
+					ip = (size_t) pContext->Pop();
+				}
+				else
+					ip++;
+				break;
+
+			case RE_ADVANCE:
+				sz = CharTraits::Next(szCurrInput);
+				szCurrInput = sz;
+				if (*sz == '\0')
+					goto Error;
+				ip = 0;
+				pContext->m_nTos = 0;
+				break;
+
+			case RE_FAIL:
+				goto Error;
+
+			case RE_RANGE:
+				{
+					if (*sz == '\0')
+					{
+						ip = (size_t) pContext->Pop();
+						break;
+					}
+
+					RECHAR *pBits = reinterpret_cast<RECHAR *>((&m_Instructions[ip]+1));
+					size_t u = CharTraits::GetBitFieldForRangeArrayIndex(sz);
+					if (pBits[u >> 3] & 1 << (u & 0x7))
+					{
+						ip += InstructionsPerRangeBitField();
+						ip++;
+						sz = CharTraits::Next(sz);
+					}
+					else
+					{
+						ip = (size_t) pContext->Pop();
+					}
+				}
+				break;
+
+			case RE_NOTRANGE:
+				{
+					if (*sz == '\0')
+					{
+						ip = (size_t) pContext->Pop();
+						break;
+					}
+
+					RECHAR *pBits = reinterpret_cast<RECHAR *>((&m_Instructions[ip]+1));
+					size_t u = static_cast<size_t>(* ((RECHAR *) sz));
+					if (pBits[u >> 3] & 1 << (u & 0x7))
+					{
+						ip = (size_t) pContext->Pop();
+					}
+					else
+					{
+						ip += InstructionsPerRangeBitField();
+						ip++;
+						sz = CharTraits::Next(sz);
+					}
+				}
+				break;
+
+			case RE_RANGE_EX:
+				{
+					if (*sz == '\0')
+					{
+						ip = (size_t) pContext->Pop();
+						break;
+					}
+
+					BOOL bMatch = FALSE;
+					size_t inEnd = GetInstruction(ip).range.nTarget;
+					ip++;
+
+					while (ip < inEnd)
+					{						
+						if (static_cast<size_t>(*sz) >= GetInstruction(ip).memory.nIndex && 
+							static_cast<size_t>(*sz) <= GetInstruction(ip+1).memory.nIndex)
+						{
+							// if we match, we jump to the end
+							sz = CharTraits::Next(sz);
+							ip = inEnd;
+							bMatch = TRUE;
+						}
+						else
+						{
+							ip += 2;
+						}
+					}
+					if (!bMatch)
+					{
+						ip = (size_t) pContext->Pop();
+					}
+				}
+				break;
+
+			case RE_NOTRANGE_EX:
+				{
+					if (*sz == '\0')
+					{
+						ip = (size_t) pContext->Pop();
+						break;
+					}
+
+					BOOL bMatch = TRUE;
+					size_t inEnd = GetInstruction(ip).range.nTarget;
+					ip++;
+
+					while (ip < inEnd)
+					{
+						if (static_cast<size_t>(*sz) >= GetInstruction(ip).memory.nIndex && 
+							static_cast<size_t>(*sz) <= GetInstruction(ip+1).memory.nIndex)
+						{
+							ip = (size_t) pContext->Pop();
+							bMatch = FALSE;
+							break;
+						}
+						else
+						{
+							// if we match, we jump to the end
+							ip += 2;
+						}
+					}
+					if (bMatch)
+						sz = CharTraits::Next(sz);
+				}
+				break;
+
+			case RE_PREVIOUS:
+				{
+					BOOL bMatch = FALSE;
+					if (m_bCaseSensitive)
+					{
+						bMatch = !CharTraits::Strncmp(sz, pContext->m_Matches[GetInstruction(ip).prev.nGroup].szStart,
+							pContext->m_Matches[GetInstruction(ip).prev.nGroup].szEnd-pContext->m_Matches[GetInstruction(ip).prev.nGroup].szStart);
+					}
+					else
+					{
+						bMatch = !CharTraits::Strnicmp(sz, pContext->m_Matches[GetInstruction(ip).prev.nGroup].szStart,
+							pContext->m_Matches[GetInstruction(ip).prev.nGroup].szEnd-pContext->m_Matches[GetInstruction(ip).prev.nGroup].szStart);
+					}
+					if (bMatch)
+					{
+						sz += pContext->m_Matches[GetInstruction(ip).prev.nGroup].szEnd-pContext->m_Matches[GetInstruction(ip).prev.nGroup].szStart;
+						ip++;
+						break;
+					}
+					ip = (size_t) pContext->Pop();
+				}
+				break;
+
+			case RE_MATCH:
+				pContext->m_Match.szEnd = sz;
+				if (!m_bCaseSensitive)
+					FixupMatchContext(pContext, szIn, szInput);
+				if (ppszEnd)
+					*ppszEnd = szIn + (sz - szInput);
+				if (szInput != szIn)
+					free((void *) szInput);
+				return TRUE;
+				break;
+
+			case RE_PUSH_GROUP:
+				pContext->Push((void *) pContext->m_Matches[GetInstruction(ip).group.nGroup].szStart);
+				pContext->Push((void *) pContext->m_Matches[GetInstruction(ip).group.nGroup].szEnd);
+				ip++;
+				break;
+
+			case RE_POP_GROUP:
+				pContext->m_Matches[GetInstruction(ip).group.nGroup].szEnd = (const RECHAR *) pContext->Pop();
+				pContext->m_Matches[GetInstruction(ip).group.nGroup].szStart = (const RECHAR *) pContext->Pop();
+				ip++;
+				break;
+
+			default:
+				ATLASSERT(FALSE);
+				break;
+			}
+		}
+
+#pragma warning(pop) // 4127
+
+		ATLASSERT(FALSE);
+Error:
+		pContext->m_Match.szEnd = sz;
+		if (!m_bCaseSensitive)
+			FixupMatchContext(pContext, szIn, szInput);
+		if (ppszEnd)
+			*ppszEnd = szIn + (sz - szInput);
+		if (szInput != szIn)
+			free((void *) szInput);
+		return FALSE;
+	}
+
+protected:
+	REParseError m_LastError;
+
+	REParseError GetLastParseError() throw()
+	{
+		return m_LastError;
+	}
+
+	void SetLastParseError(REParseError Error) throw()
+	{
+		m_LastError = Error;
+	}
+	// CAtlRegExp::Reset
+	// Removes all instructions to allow reparsing into the same instance
+	void Reset() throw()
+	{
+		m_Instructions.RemoveAll();
+		m_uRequiredMem = 0;
+		m_bCaseSensitive = TRUE;
+		m_uNumGroups = 0;
+		SetLastParseError(REPARSE_ERROR_OK);
+	}
+
+
+	enum REInstructionType { 
+		RE_NOP,
+		RE_GROUP_START,
+		RE_GROUP_END, 
+		RE_SYMBOL,
+		RE_ANY,
+		RE_RANGE,
+		RE_NOTRANGE,
+		RE_RANGE_EX,
+		RE_NOTRANGE_EX,
+		RE_PLUS,
+		RE_NG_PLUS,
+		RE_QUESTION,
+		RE_NG_QUESTION,
+		RE_JMP,
+		RE_PUSH_CHARPOS,
+		RE_POP_CHARPOS,
+		RE_CALL,
+		RE_RETURN,
+		RE_STAR_BEGIN,
+		RE_NG_STAR_BEGIN, 
+		RE_PUSH_MEMORY,
+		RE_POP_MEMORY,
+		RE_STORE_CHARPOS,
+		RE_STORE_STACKPOS,
+		RE_GET_CHARPOS,
+		RE_GET_STACKPOS,
+		RE_RET_NOMATCH,
+		RE_PREVIOUS,
+		RE_FAIL,
+		RE_ADVANCE,
+		RE_MATCH,
+		RE_PUSH_GROUP,
+		RE_POP_GROUP,
+	};
+
+	struct INSTRUCTION_SYMBOL
+	{
+		size_t nSymbol;
+	};
+
+	struct INSTRUCTION_JMP
+	{
+		size_t nTarget;	
+	};
+
+	struct INSTRUCTION_GROUP
+	{
+		size_t nGroup;
+	};
+
+	struct INSTRUCTION_CALL
+	{
+		size_t nTarget;
+	};
+
+	struct INSTRUCTION_MEMORY
+	{
+		size_t nIndex;
+	};
+
+	struct INSTRUCTION_PREVIOUS
+	{
+		size_t nGroup;
+	};
+
+	struct INSTRUCTION_RANGE_EX
+	{
+		size_t nTarget;
+	};
+
+	struct INSTRUCTION
+	{
+		REInstructionType type;
+		union
+		{
+			INSTRUCTION_SYMBOL symbol;
+			INSTRUCTION_JMP jmp;
+			INSTRUCTION_GROUP group;
+			INSTRUCTION_CALL call;
+			INSTRUCTION_MEMORY memory;
+			INSTRUCTION_PREVIOUS prev;
+			INSTRUCTION_RANGE_EX range;
+		};
+	};
+
+	inline int InstructionsPerRangeBitField() throw()
+	{
+		return (256/8) / sizeof(INSTRUCTION) + (((256/8) % sizeof(INSTRUCTION)) ? 1 : 0);
+	}
+
+	CAtlArray<INSTRUCTION> m_Instructions;
+
+	UINT m_uNumGroups;
+	UINT m_uRequiredMem;
+	BOOL m_bCaseSensitive;
+
+
+	// class used internally to restore
+	// parsing state when unwinding
+	class CParseState
+	{
+	public:
+		int m_nNumInstructions;
+		UINT m_uNumGroups;
+		UINT m_uRequiredMem;
+
+		CParseState(CAtlRegExp *pRegExp) throw()
+		{
+			m_nNumInstructions = (int) pRegExp->m_Instructions.GetCount();
+			m_uNumGroups = pRegExp->m_uNumGroups;
+			m_uRequiredMem = pRegExp->m_uRequiredMem;
+		}
+
+		void Restore(CAtlRegExp *pRegExp)
+		{
+			pRegExp->m_Instructions.SetCount(m_nNumInstructions);
+			pRegExp->m_uNumGroups = m_uNumGroups;
+			pRegExp->m_uRequiredMem = m_uRequiredMem;
+		}
+	};
+
+	int AddInstruction(REInstructionType type)
+	{
+		if (!m_Instructions.SetCount(m_Instructions.GetCount()+1))
+		{
+			SetLastParseError(REPARSE_ERROR_OUTOFMEMORY);
+			return -1;
+		}
+
+		m_Instructions[m_Instructions.GetCount()-1].type = type;
+		return (int) m_Instructions.GetCount()-1;
+	}
+
+	BOOL PeekToken(const RECHAR **ppszRE, int ch) throw()
+	{
+		if (**ppszRE != ch)
+			return FALSE;
+		return TRUE;
+	}
+
+	BOOL MatchToken(const RECHAR **ppszRE, int ch) throw()
+	{
+		if (!PeekToken(ppszRE, ch))
+			return FALSE;
+		*ppszRE = CharTraits::Next(*ppszRE);
+		return TRUE;
+	}
+
+	INSTRUCTION &GetInstruction(size_t nIndex) throw()
+	{
+		return m_Instructions[nIndex];
+	}
+
+	// ParseArg: parse grammar rule Arg
+	int ParseArg(const RECHAR **ppszRE, bool &bEmpty)
+	{
+		int nPushGroup = AddInstruction(RE_PUSH_GROUP);
+		if (nPushGroup < 0)
+			return -1;
+
+		GetInstruction(nPushGroup).group.nGroup = m_uNumGroups;
+
+		int p = AddInstruction(RE_GROUP_START);
+		if (p < 0)
+			return -1;
+		GetInstruction(p).group.nGroup = m_uNumGroups++;
+
+		int nCall = AddInstruction(RE_CALL);
+		if (nCall < 0)
+			return -1;
+
+		int nPopGroup = AddInstruction(RE_POP_GROUP);
+		if (nPopGroup < 0)
+			return -1;
+		GetInstruction(nPopGroup).group.nGroup = GetInstruction(nPushGroup).group.nGroup;
+
+		if (AddInstruction(RE_RETURN) < 0)
+			return -1;
+
+		int nAlt = ParseRE(ppszRE, bEmpty);
+		if (nAlt < 0)
+		{
+			if (GetLastParseError())
+				return -1;
+
+			if (!PeekToken(ppszRE, '}'))
+			{
+				SetLastParseError(REPARSE_ERROR_BRACE_EXPECTED);
+				return -1;
+			}
+
+			// in the case of an empty group, we add a nop
+			nAlt = AddInstruction(RE_NOP);
+			if (nAlt < 0)
+				return -1;
+		}
+
+		GetInstruction(nCall).call.nTarget = nAlt;
+
+		if (!MatchToken(ppszRE, '}'))
+		{
+			SetLastParseError(REPARSE_ERROR_BRACE_EXPECTED);
+			return -1;
+		}
+
+		int nEnd = AddInstruction(RE_GROUP_END);
+		if (nEnd < 0)
+			return -1;
+		GetInstruction(nEnd).group.nGroup = GetInstruction(p).group.nGroup;
+		return nPushGroup;
+	}
+
+	// ParseGroup: parse grammar rule Group
+	int ParseGroup(const RECHAR **ppszRE, bool &bEmpty)
+	{
+		int nCall = AddInstruction(RE_CALL);
+		if (nCall < 0)
+			return -1;
+
+		if (AddInstruction(RE_RETURN) < 0)
+			return -1;
+
+		int nAlt = ParseRE(ppszRE, bEmpty);
+		if (nAlt < 0)
+		{
+			if (GetLastParseError())
+				return -1;
+
+			if (!PeekToken(ppszRE, ')'))
+			{
+				SetLastParseError(REPARSE_ERROR_PAREN_EXPECTED);
+				return -1;
+			}
+
+			// in the case of an empty group, we add a nop
+			nAlt = AddInstruction(RE_NOP);
+			if (nAlt < 0)
+				return -1;
+		}
+
+		GetInstruction(nCall).call.nTarget = nAlt;
+
+		if (!MatchToken(ppszRE, ')'))
+		{
+			SetLastParseError(REPARSE_ERROR_PAREN_EXPECTED);
+			return -1;
+		}
+
+		return nCall;
+	}
+
+	RECHAR GetEscapedChar(RECHAR ch) throw()
+	{
+		if (ch == 't')
+			return '\t';
+		return ch;
+	}
+
+	// ParseCharItem: parse grammar rule CharItem
+	int ParseCharItem(const RECHAR **ppszRE, RECHAR *pchStartChar, RECHAR *pchEndChar) throw()
+	{
+		if (**ppszRE == '\\')
+		{
+			*ppszRE = CharTraits::Next(*ppszRE);
+			*pchStartChar = GetEscapedChar(**ppszRE);
+		}
+		else
+			*pchStartChar = **ppszRE;
+		*ppszRE = CharTraits::Next(*ppszRE);
+
+		if (!MatchToken(ppszRE, '-'))
+		{
+			*pchEndChar = *pchStartChar;
+			return 0;
+		}
+
+		// check for unterminated range
+		if (!**ppszRE || PeekToken(ppszRE, ']'))
+		{
+			SetLastParseError(REPARSE_ERROR_BRACKET_EXPECTED);
+			return -1;
+		}
+
+		*pchEndChar = **ppszRE;
+		*ppszRE = CharTraits::Next(*ppszRE);
+
+		if (*pchEndChar < *pchStartChar)
+		{
+			SetLastParseError(REPARSE_ERROR_INVALID_RANGE);
+			return -1;
+		}
+		return 0;
+	}
+
+	int AddInstructions(int nNumInstructions)
+	{
+		size_t nCurr = m_Instructions.GetCount();
+		if (!m_Instructions.SetCount(nCurr+nNumInstructions))
+		{
+			SetLastParseError(REPARSE_ERROR_OUTOFMEMORY);
+			return -1;
+		}
+		return (int) nCurr;
+	}
+
+	// ParseCharSet: parse grammar rule CharSet
+	int ParseCharSet(const RECHAR **ppszRE, BOOL bNot)
+	{
+		int p = -1;
+
+		unsigned char *pBits = NULL;
+
+		if (CharTraits::UseBitFieldForRange())
+		{
+			// we use a bit field to represent the characters
+			// a 1 bit means match against the character
+			// the last 5 bits are used as an index into 
+			// the byte array, and the first 3 bits
+			// are used to index into the selected byte
+
+			p = AddInstruction(bNot ? RE_NOTRANGE : RE_RANGE);
+			if (p < 0)
+				return -1;
+
+			// add the required space to hold the character
+			// set.  We use one bit per character for ansi
+			if (AddInstructions(InstructionsPerRangeBitField()) < 0)
+				return -1;
+
+			pBits = (unsigned char *) (&m_Instructions[p+1]);
+			memset(pBits, 0x00, 256/8);
+		}
+		else
+		{
+			p = AddInstruction(bNot ? RE_NOTRANGE_EX : RE_RANGE_EX);
+			if (p < 0)
+				return -1;
+		}
+
+		RECHAR chStart;
+		RECHAR chEnd;
+
+		while (**ppszRE && **ppszRE != ']')
+		{
+			if (ParseCharItem(ppszRE, &chStart, &chEnd))
+				return -1;
+
+			if (CharTraits::UseBitFieldForRange())
+			{
+				for (int i=chStart; i<=chEnd; i++)
+					pBits[i >> 3] |= 1 << (i & 0x7);
+			}
+			else
+			{
+				int nStart = AddInstruction(RE_NOP);
+				if (nStart < 0)
+					return -1;
+
+				int nEnd = AddInstruction(RE_NOP);
+				if (nEnd < 0)
+					return -1;
+
+				GetInstruction(nStart).memory.nIndex = (int) chStart;
+				GetInstruction(nEnd).memory.nIndex = (int) chEnd;
+			}
+		}
+
+		if (!CharTraits::UseBitFieldForRange())
+			GetInstruction(p).range.nTarget = m_Instructions.GetCount();
+
+		return p;
+	}
+
+	// ParseCharClass: parse grammar rule CharClass
+	int ParseCharClass(const RECHAR **ppszRE, bool &bEmpty)
+	{
+		bEmpty = false;
+		if (MatchToken(ppszRE, ']'))
+		{
+			SetLastParseError(REPARSE_ERROR_EMPTY_RANGE);
+			return -1;
+		}
+
+		BOOL bNot = FALSE;
+		if (MatchToken(ppszRE, '^'))
+			bNot = TRUE;
+
+		if (MatchToken(ppszRE, ']'))
+		{
+			SetLastParseError(REPARSE_ERROR_EMPTY_RANGE);
+			return -1;
+		}
+
+		int p = ParseCharSet(ppszRE, bNot);
+		if (p < 0)
+			return p;
+		if (!MatchToken(ppszRE, ']'))
+		{
+			SetLastParseError(REPARSE_ERROR_BRACKET_EXPECTED);
+			return -1;
+		}
+
+		return p;
+	}
+
+	int AddMemInstruction(REInstructionType type)
+	{
+		int p = AddInstruction(type);
+		if (p < 0)
+			return p;
+		GetInstruction(p).memory.nIndex = m_uRequiredMem++;
+		return p;
+	}
+
+	// helper for parsing !SE
+	int ParseNot(const RECHAR **ppszRE, bool &bEmpty)
+	{
+		int nStoreCP = AddMemInstruction(RE_STORE_CHARPOS);
+		int nStoreSP = AddMemInstruction(RE_STORE_STACKPOS);
+
+		int nCall = AddInstruction(RE_CALL);
+		if (nCall < 0)
+			return -1;
+
+		int nGetCP = AddInstruction(RE_GET_CHARPOS);
+		if (nGetCP < 0)
+			return -1;
+		GetInstruction(nGetCP).memory.nIndex = GetInstruction(nStoreCP).memory.nIndex;
+
+		int nGetSP = AddInstruction(RE_GET_STACKPOS);
+		if (nGetSP < 0)
+			return -1;
+		GetInstruction(nGetSP).memory.nIndex = GetInstruction(nStoreSP).memory.nIndex;
+
+		int nJmp = AddInstruction(RE_JMP);
+		if (nJmp < 0)
+			return -1;
+
+		int nSE = ParseSE(ppszRE, bEmpty);
+		if (nSE < 0)
+			return nSE;
+
+		// patch the call
+		GetInstruction(nCall).call.nTarget = nSE;
+
+		int nGetCP1 = AddInstruction(RE_GET_CHARPOS);
+		if (nGetCP1 < 0)
+			return -1;
+		GetInstruction(nGetCP1).memory.nIndex = GetInstruction(nStoreCP).memory.nIndex;
+
+		int nGetSP1 = AddInstruction(RE_GET_STACKPOS);
+		if (nGetSP1 < 0)
+			return -1;
+		GetInstruction(nGetSP1).memory.nIndex = GetInstruction(nStoreSP).memory.nIndex;
+
+		int nRet = AddInstruction(RE_RETURN);
+		if (nRet < 0)
+			return -1;
+
+		GetInstruction(nJmp).jmp.nTarget = nRet+1;
+
+		return nStoreCP;
+	}
+
+	// ParseAbbrev: parse grammar rule Abbrev
+	int ParseAbbrev(const RECHAR **ppszRE, bool &bEmpty)
+	{
+		const RECHAR **szAbbrevs = CharTraits::GetAbbrevs();
+
+		while (*szAbbrevs)
+		{
+			if (**ppszRE == **szAbbrevs)
+			{
+				const RECHAR *szAbbrev = (*szAbbrevs)+1;
+				int p = ParseE(&szAbbrev, bEmpty);
+				if (p < 0)
+				{
+					SetLastParseError(REPARSE_ERROR_UNEXPECTED);
+					return p;
+				}
+				*ppszRE = CharTraits::Next(*ppszRE);
+				return p;
+			}
+			szAbbrevs++;
+		}
+		return -1;
+	}
+
+	// ParseSE: parse grammar rule SE (simple expression)
+	int ParseSE(const RECHAR **ppszRE, bool &bEmpty)
+	{
+
+		if (MatchToken(ppszRE, '{'))
+			return ParseArg(ppszRE, bEmpty);
+		if (MatchToken(ppszRE, '('))
+			return ParseGroup(ppszRE, bEmpty);
+		if (MatchToken(ppszRE, '['))
+			return ParseCharClass(ppszRE, bEmpty);
+
+		if (MatchToken(ppszRE, '\\'))
+		{
+			if (!CharTraits::Isdigit(**ppszRE))
+			{
+				// check for abbreviations
+				int p;
+				p = ParseAbbrev(ppszRE, bEmpty);
+				if (p >= 0)
+					return p;
+
+				if (GetLastParseError())
+					return -1;
+
+				// escaped char
+				p = AddInstruction(RE_SYMBOL);
+				if (p < 0)
+					return -1;
+				GetInstruction(p).symbol.nSymbol = (int) **ppszRE;
+				*ppszRE = CharTraits::Next(*ppszRE);
+				return p;
+			}
+			// previous match
+			bEmpty = false;
+			int nPrev = AddInstruction(RE_PREVIOUS);
+			if (nPrev < 0)
+				return -1;
+
+			UINT uValue = (UINT) CharTraits::Strtol(*ppszRE, (RECHAR **) ppszRE, 10);
+			if (uValue >= m_uNumGroups)
+			{
+				SetLastParseError(REPARSE_ERROR_INVALID_GROUP);
+				return -1;
+			}
+			GetInstruction(nPrev).prev.nGroup = (size_t) uValue;
+			return nPrev;
+		}
+
+		if (MatchToken(ppszRE, '!'))
+			return ParseNot(ppszRE, bEmpty);
+
+		if (**ppszRE == '}' || **ppszRE == ']' || **ppszRE == ')')
+		{
+			return -1;
+		}
+
+		if (**ppszRE == '\0')
+		{
+			return -1;
+		}
+
+		int p;
+		if (**ppszRE == '.')
+		{
+			p = AddInstruction(RE_ANY);
+			if (p < 0)
+				return -1;
+			bEmpty = false;
+		}
+		else if (**ppszRE == '$' && (*ppszRE)[1] == '\0')
+		{
+			p = AddInstruction(RE_SYMBOL);
+			if (p < 0)
+				return -1;
+			GetInstruction(p).symbol.nSymbol = 0;
+			bEmpty = false;
+		}
+		else
+		{
+			p = AddInstruction(RE_SYMBOL);
+			if (p < 0)
+				return -1;
+			GetInstruction(p).symbol.nSymbol = (int) **ppszRE;
+			bEmpty = false;
+		}
+		*ppszRE = CharTraits::Next(*ppszRE);
+		return p;
+	}
+
+	// ParseE: parse grammar rule E (expression)
+	int ParseE(const RECHAR **ppszRE, bool &bEmpty)
+	{
+		CParseState ParseState(this);
+		const RECHAR *sz = *ppszRE;
+
+		int nSE;
+
+		int nFirst = ParseSE(ppszRE, bEmpty);
+		if (nFirst < 0)
+			return nFirst;
+
+		REInstructionType type = RE_MATCH;
+
+		if (MatchToken(ppszRE, '*'))
+			if(MatchToken(ppszRE, '?'))
+				type = RE_NG_STAR_BEGIN;
+			else
+				type = RE_STAR_BEGIN;
+
+
+		else if (MatchToken(ppszRE, '+'))
+			if(MatchToken(ppszRE, '?'))
+				type = RE_NG_PLUS;
+			else
+				type = RE_PLUS;
+
+		else if (MatchToken(ppszRE, '?'))
+			if(MatchToken(ppszRE, '?'))
+				type = RE_NG_QUESTION;
+			else
+				type = RE_QUESTION;
+
+
+		if (type == RE_MATCH)
+			return nFirst;
+
+		if (type == RE_STAR_BEGIN || type == RE_QUESTION|| type == RE_NG_STAR_BEGIN || type == RE_NG_QUESTION)
+		{
+			ParseState.Restore(this);
+		}
+		else
+		{
+			m_uNumGroups = ParseState.m_uNumGroups;
+		}
+		*ppszRE = sz;
+
+		int nE;
+
+		if (type == RE_NG_STAR_BEGIN || type == RE_NG_PLUS || type == RE_NG_QUESTION) // Non-Greedy
+		{			
+			int nCall = AddInstruction(RE_CALL);
+			if (nCall < 0)
+				return -1;
+
+			bEmpty = false;
+
+			nSE = ParseSE(ppszRE, bEmpty);
+			if (nSE < 0)
+				return nSE;
+
+			if (bEmpty && (type == RE_NG_STAR_BEGIN || type == RE_NG_PLUS))
+			{
+				SetLastParseError(REPARSE_ERROR_EMPTY_REPEATOP);
+				return -1;
+			}
+			bEmpty = true;
+
+			*ppszRE = CharTraits::Next(*ppszRE);
+			*ppszRE = CharTraits::Next(*ppszRE);
+
+			if (type == RE_NG_STAR_BEGIN || type == RE_NG_PLUS)
+			{
+				int nJmp = AddInstruction(RE_JMP);
+				if (nJmp < 0)
+					return -1;
+				GetInstruction(nCall).call.nTarget = nJmp+1;
+				GetInstruction(nJmp).jmp.nTarget = nCall;
+			}
+			else
+				GetInstruction(nCall).call.nTarget = nSE+1;
+
+			if (type == RE_NG_PLUS)
+				nE = nFirst;
+			else
+				nE = nCall;
+		}
+		else // Greedy
+		{
+
+			int nPushMem = AddInstruction(RE_PUSH_MEMORY);
+			if (nPushMem < 0)
+				return -1;
+
+			int nStore = AddInstruction(RE_STORE_CHARPOS);
+			if (nStore < 0)
+				return -1;
+
+			if (AddInstruction(RE_PUSH_CHARPOS) < 0)
+				return -1;
+
+			int nCall = AddInstruction(RE_CALL);
+			if (nCall < 0)
+				return -1;
+
+			if (AddInstruction(RE_POP_CHARPOS) < 0)
+				return -1;
+
+			int nPopMem = AddInstruction(RE_POP_MEMORY);
+			if (nPopMem < 0)
+				return -1;
+
+			int nJmp = AddInstruction(RE_JMP);
+			if (nJmp < 0)
+				return -1;
+
+			GetInstruction(nPushMem).memory.nIndex = m_uRequiredMem++;
+			GetInstruction(nStore).memory.nIndex = GetInstruction(nPushMem).memory.nIndex;
+			GetInstruction(nCall).call.nTarget = nJmp+1;
+			GetInstruction(nPopMem).memory.nIndex = GetInstruction(nPushMem).memory.nIndex;
+
+			bEmpty = false;
+
+			nSE = ParseSE(ppszRE, bEmpty);
+			if (nSE < 0)
+				return nSE;
+
+			if (bEmpty && (type == RE_STAR_BEGIN || type == RE_PLUS))
+			{
+				SetLastParseError(REPARSE_ERROR_EMPTY_REPEATOP);
+				return -1;
+			}
+
+			if (type != RE_PLUS && type != RE_NG_PLUS)
+				bEmpty = true;
+
+			*ppszRE = CharTraits::Next(*ppszRE);
+
+
+			int nRetNoMatch = AddInstruction(RE_RET_NOMATCH);
+			if (nRetNoMatch < 0)
+				return -1;
+
+			int nStore1 = AddInstruction(RE_STORE_CHARPOS);
+			if (nStore1 < 0)
+				return -1;
+
+			GetInstruction(nRetNoMatch).memory.nIndex = GetInstruction(nPushMem).memory.nIndex;
+			GetInstruction(nStore1).memory.nIndex = GetInstruction(nPushMem).memory.nIndex;
+
+			if (type != RE_QUESTION)
+			{
+				int nJmp1 = AddInstruction(RE_JMP);
+				if (nJmp1 < 0)
+					return -1;
+				GetInstruction(nJmp1).jmp.nTarget = nPushMem;
+			}
+
+			GetInstruction(nJmp).jmp.nTarget = m_Instructions.GetCount();
+			if (type == RE_PLUS)
+				nE = nFirst;
+			else
+				nE = nPushMem;
+		}
+
+		return nE;
+	}
+
+
+	// ParseAltE: parse grammar rule AltE
+	int ParseAltE(const RECHAR **ppszRE, bool &bEmpty)
+	{
+		const RECHAR *sz = *ppszRE;
+		CParseState ParseState(this);
+
+		int nPush = AddInstruction(RE_PUSH_CHARPOS);
+		if (nPush < 0)
+			return -1;
+
+		int nCall = AddInstruction(RE_CALL);
+		if (nCall < 0)
+			return -1;
+
+		GetInstruction(nCall).call.nTarget = nPush+4;
+		if (AddInstruction(RE_POP_CHARPOS) < 0)
+			return -1;
+
+		int nJmpNext = AddInstruction(RE_JMP);
+		if (nJmpNext < 0)
+			return -1;
+
+		int nE = ParseE(ppszRE, bEmpty);
+		if (nE < 0)
+		{
+			if (GetLastParseError())
+				return -1;
+			ParseState.Restore(this);
+			return nE;
+		}
+
+		int nJmpEnd = AddInstruction(RE_JMP);
+		if (nJmpEnd < 0)
+			return -1;
+
+		GetInstruction(nJmpNext).jmp.nTarget = nJmpEnd+1;
+
+		if (!MatchToken(ppszRE, '|'))
+		{
+			ParseState.Restore(this);
+			*ppszRE = sz;
+
+			return ParseE(ppszRE, bEmpty);
+		}
+
+		bool bEmptyAltE;
+		int nAltE = ParseAltE(ppszRE, bEmptyAltE);
+		GetInstruction(nJmpEnd).jmp.nTarget = m_Instructions.GetCount();
+		GetInstruction(nJmpNext).jmp.nTarget = nAltE;
+		if (nAltE < 0)
+		{
+			if (GetLastParseError())
+				return -1;
+			ParseState.Restore(this);
+			return nAltE;
+		}
+		bEmpty = bEmpty | bEmptyAltE;
+		return nPush;
+	}
+
+	// ParseRE: parse grammar rule RE (regular expression)
+	int ParseRE(const RECHAR **ppszRE, bool &bEmpty)
+	{
+		if (**ppszRE == '\0')
+			return -1;
+
+		int p = ParseAltE(ppszRE, bEmpty);
+		if (p < 0)
+			return p;
+
+		bool bEmptyRE = true;
+		ParseRE(ppszRE, bEmptyRE);
+		if (GetLastParseError())
+			return -1;
+		bEmpty = bEmpty && bEmptyRE;
+		return p;
+	}
+
+	//pointers to the matched string and matched groups, currently point into an internal allocated 
+	//buffer that hold a copy of the input string.
+	//This function fix these pointers to point into the original, user supplied buffer (first param to Match method).
+	//Example: If a ptr (szStart) currently point to <internal buffer>+3, it is fixed to <user supplied buffer>+3
+	void FixupMatchContext(CAtlREMatchContext<CharTraits> *pContext, const RECHAR *szOrig, const RECHAR *szNew) 
+	{
+		ATLENSURE(pContext);
+		ATLASSERT(szOrig);
+		ATLASSERT(szNew);
+
+		pContext->m_Match.szStart = szOrig + (pContext->m_Match.szStart - szNew);
+		pContext->m_Match.szEnd = szOrig + (pContext->m_Match.szEnd - szNew);
+		for (UINT i=0; i<pContext->m_uNumGroups; i++)
+		{
+			if (pContext->m_Matches[i].szStart==NULL || pContext->m_Matches[i].szEnd==NULL)
+			{
+				continue; //Do not fix unmatched groups.
+			}
+			pContext->m_Matches[i].szStart = szOrig + (pContext->m_Matches[i].szStart - szNew);
+			pContext->m_Matches[i].szEnd = szOrig + (pContext->m_Matches[i].szEnd - szNew);
+		}
+	}
+	// implementation
+	// helpers for dumping and debugging the rx engine
+public:
+#ifdef ATL_REGEXP_DUMP
+	size_t DumpInstruction(size_t ip)
+	{
+		printf("%08x ", ip);
+		switch (GetInstruction(ip).type)
+		{
+		case RE_NOP:
+			printf("NOP\n");
+			ip++;
+			break;
+
+		case RE_SYMBOL:
+			AtlprintfT<RECHAR>(CAToREChar<RECHAR>("Symbol %c\n"),GetInstruction(ip).symbol.nSymbol);			
+			ip++;
+			break;
+
+		case RE_ANY:
+			printf("Any\n");
+			ip++;
+			break;
+
+		case RE_RANGE:
+			printf("Range\n");
+			ip++;
+			ip += InstructionsPerRangeBitField();
+			break;
+
+		case RE_NOTRANGE:
+			printf("NOT Range\n");
+			ip++;
+			ip += InstructionsPerRangeBitField();
+			break;
+
+		case RE_RANGE_EX:
+			printf("RangeEx %08x\n", GetInstruction(ip).range.nTarget);
+			ip++;
+			break;
+
+		case RE_NOTRANGE_EX:
+			printf("NotRangeEx %08x\n", GetInstruction(ip).range.nTarget);
+			ip++;
+			break;
+
+		case RE_GROUP_START:
+			printf("Start group %d\n", GetInstruction(ip).group.nGroup);
+			ip++;
+			break;
+
+		case RE_GROUP_END:
+			printf("Group end %d\n", GetInstruction(ip).group.nGroup);
+			ip++;
+			break;
+
+		case RE_PUSH_CHARPOS:
+			printf("Push char pos\n");
+			ip++;
+			break;
+
+		case RE_POP_CHARPOS:
+			printf("Pop char pos\n");
+			ip++;
+			break;
+
+		case RE_STORE_CHARPOS:
+			printf("Store char pos %d\n", GetInstruction(ip).memory.nIndex);
+			ip++;
+			break;
+
+		case RE_GET_CHARPOS:
+			printf("Get char pos %d\n", GetInstruction(ip).memory.nIndex);
+			ip++;
+			break;
+
+		case RE_STORE_STACKPOS:
+			printf("Store stack pos %d\n", GetInstruction(ip).memory.nIndex);
+			ip++;
+			break;
+
+		case RE_GET_STACKPOS:
+			printf("Get stack pos %d\n", GetInstruction(ip).memory.nIndex);
+			ip++;
+			break;
+
+		case RE_CALL:
+			printf("Call %08x\n", GetInstruction(ip).call.nTarget);
+			ip++;
+			break;
+
+		case RE_JMP:
+			printf("Jump %08x\n", GetInstruction(ip).jmp.nTarget);
+			ip++;
+			break;
+
+		case RE_RETURN:
+			printf("return\n");
+			ip++;
+			break;
+
+		case RE_PUSH_MEMORY:
+			printf("Push memory %08x\n", GetInstruction(ip).memory.nIndex);
+			ip++;
+			break;
+
+		case RE_POP_MEMORY:
+			printf("Pop memory %08x\n", GetInstruction(ip).memory.nIndex);
+			ip++;
+			break;
+
+		case RE_RET_NOMATCH:
+			printf("Return no match %08x\n", GetInstruction(ip).memory.nIndex);
+			ip++;
+			break;
+
+		case RE_MATCH:
+			printf("END\n");
+			ip++;
+			break;
+
+		case RE_ADVANCE:
+			printf("ADVANCE\n");
+			ip++;
+			break;
+
+		case RE_FAIL:
+			printf("FAIL\n");
+			ip++;
+			break;
+
+		case RE_PREVIOUS:
+			printf("Prev %d\n", GetInstruction(ip).prev.nGroup);
+			ip++;
+			break;
+
+		case RE_PUSH_GROUP:
+			printf("Push group %d\n", GetInstruction(ip).group.nGroup);
+			ip++;
+			break;
+
+		case RE_POP_GROUP:
+			printf("Pop group %d\n", GetInstruction(ip).group.nGroup);
+			ip++;
+			break;
+
+
+		default:
+			printf("????\n");
+			ip++;
+			break;
+		}
+		return ip;
+	}
+
+	void Dump(size_t ipCurrent = 0)
+	{
+		size_t ip = 0;
+
+		while (ip < m_Instructions.GetCount())
+		{
+			if (ip == ipCurrent)
+				printf("->");
+			ip = DumpInstruction(ip);
+		}
+	}
+#endif
+
+#ifdef ATLRX_DEBUG
+	 void cls( HANDLE hConsole )
+	{
+		COORD coordScreen = { 0, 0 };    /* here's where we'll home the
+											cursor */ 
+		BOOL bSuccess;
+		DWORD cCharsWritten;
+		CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */ 
+		DWORD dwConSize;                 /* number of character cells in
+											the current buffer */ 
+
+		/* get the number of character cells in the current buffer */ 
+
+		bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi );
+		dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
+
+		/* fill the entire screen with blanks */ 
+
+		bSuccess = FillConsoleOutputCharacter( hConsole, (TCHAR) ' ',
+		   dwConSize, coordScreen, &cCharsWritten );
+
+		/* get the current text attribute */ 
+
+		bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi );
+
+		/* now set the buffer's attributes accordingly */ 
+
+		bSuccess = FillConsoleOutputAttribute( hConsole, csbi.wAttributes,
+		   dwConSize, coordScreen, &cCharsWritten );
+
+		/* put the cursor at (0, 0) */ 
+
+		bSuccess = SetConsoleCursorPosition( hConsole, coordScreen );
+		return;
+	} 
+
+	void DumpStack(CAtlREMatchContext<CharTraits> *pContext)
+	{
+		for (size_t i=pContext->m_nTos; i>0; i--)
+		{
+			if (pContext->m_stack[i] < (void *) m_Instructions.GetCount())
+				printf("0x%p\n", pContext->m_stack[i]);
+			else
+			{
+				// assume a pointer into the input
+				AtlprintfT<RECHAR>(CAToREChar<RECHAR>("%s\n"), pContext->m_stack[i]);
+			}
+		}
+	}
+
+	void DumpMemory(CAtlREMatchContext<CharTraits> *pContext)
+	{
+		for (UINT i=0; i<m_uRequiredMem; i++)
+		{
+			AtlprintfT<RECHAR>(CAToREChar<RECHAR>("%d: %s\n"), i, pContext->m_Mem.m_p[i]);
+		}
+	}
+
+	virtual void OnDebugEvent(size_t ip, const RECHAR *szIn, const RECHAR *sz, CAtlREMatchContext<CharTraits> *pContext)
+	{
+		cls(GetStdHandle(STD_OUTPUT_HANDLE));
+		printf("----------Code---------\n");
+		Dump(ip);
+		printf("----------Input---------\n");		
+		AtlprintfT<RECHAR>(CAToREChar<RECHAR>("%s\n"), szIn);
+		for (int s=0; szIn+s < sz; s++)
+		{
+			printf(" ");
+		}		
+		printf("^\n");
+		printf("----------Memory---------\n");
+		DumpMemory(pContext);
+		printf("----------Stack---------\n");		
+		DumpStack(pContext);
+		getchar();
+	}
+#endif
+
+};
+
+} // namespace ATL
+#pragma pack(pop)
+
+#endif // __ATLRX_H__