// --------------------------------------------------------------------------------
// Ixphttpm.cpp
// Copyright (c)1998 Microsoft Corporation, All Rights Reserved
// Greg Friedman
// --------------------------------------------------------------------------------

#include "pch.hxx"
#include "dllmain.h"
#include "ixphttpm.h"
#include "wininet.h"
#include "ocidl.h"
#include "Vstream.h"
#include "shlwapi.h"
#include "htmlstr.h"
#include "strconst.h"
#include "propfind.h"
#include "davparse.h"
#include "davstrs.h"
#include <ntverp.h>
#include <process.h>
#include "oleutil.h"
#include "ixputil.h"
#include <demand.h>

typedef struct tagHTTPERROR
{
    DWORD   dwHttpError;
    HRESULT ixpResult;
} HTTPERROR, *LPHTTPERROR;

static const HTTPERROR c_rgHttpErrorMap[] =
{
    { HTTP_STATUS_USE_PROXY, IXP_E_HTTP_USE_PROXY },
    { HTTP_STATUS_BAD_REQUEST, IXP_E_HTTP_BAD_REQUEST  },
    { HTTP_STATUS_DENIED, IXP_E_HTTP_UNAUTHORIZED },
    { HTTP_STATUS_FORBIDDEN, IXP_E_HTTP_FORBIDDEN },
    { HTTP_STATUS_NOT_FOUND, IXP_E_HTTP_NOT_FOUND },
    { HTTP_STATUS_BAD_METHOD, IXP_E_HTTP_METHOD_NOT_ALLOW },
    { HTTP_STATUS_NONE_ACCEPTABLE, IXP_E_HTTP_NOT_ACCEPTABLE },
    { HTTP_STATUS_PROXY_AUTH_REQ, IXP_E_HTTP_PROXY_AUTH_REQ },
    { HTTP_STATUS_REQUEST_TIMEOUT, IXP_E_HTTP_REQUEST_TIMEOUT },
    { HTTP_STATUS_CONFLICT, IXP_E_HTTP_CONFLICT },
    { HTTP_STATUS_GONE, IXP_E_HTTP_GONE },
    { HTTP_STATUS_LENGTH_REQUIRED, IXP_E_HTTP_LENGTH_REQUIRED },
    { HTTP_STATUS_PRECOND_FAILED, IXP_E_HTTP_PRECOND_FAILED },
    { HTTP_STATUS_SERVER_ERROR, IXP_E_HTTP_INTERNAL_ERROR },
    { HTTP_STATUS_NOT_SUPPORTED, IXP_E_HTTP_NOT_IMPLEMENTED },
    { HTTP_STATUS_BAD_GATEWAY, IXP_E_HTTP_BAD_GATEWAY },
    { HTTP_STATUS_SERVICE_UNAVAIL, IXP_E_HTTP_SERVICE_UNAVAIL },
    { HTTP_STATUS_GATEWAY_TIMEOUT, IXP_E_HTTP_GATEWAY_TIMEOUT },
    { HTTP_STATUS_VERSION_NOT_SUP, IXP_E_HTTP_VERS_NOT_SUP },
    { 425, IXP_E_HTTP_INSUFFICIENT_STORAGE },   // obsolete out of storage error
    { 507, IXP_E_HTTP_INSUFFICIENT_STORAGE },   // preferred out of storage error
    { ERROR_INTERNET_OUT_OF_HANDLES, E_OUTOFMEMORY },
    { ERROR_INTERNET_TIMEOUT, IXP_E_TIMEOUT },
    { ERROR_INTERNET_NAME_NOT_RESOLVED, IXP_E_CANT_FIND_HOST },
    { ERROR_INTERNET_CANNOT_CONNECT, IXP_E_FAILED_TO_CONNECT },
    { HTTP_STATUS_NOT_MODIFIED, IXP_E_HTTP_NOT_MODIFIED},
};

#define FAIL_ABORT \
    if (WasAborted()) \
    { \
        hr = TrapError(IXP_E_USER_CANCEL); \
        goto exit; \
    } \
    else

#define FAIL_EXIT_STREAM_WRITE(stream, psz) \
    if (FAILED(hr = stream.Write(psz, lstrlen(psz), NULL))) \
        goto exit; \
    else

#define FAIL_EXIT(hr) \
    if (FAILED(hr)) \
        goto exit; \
    else

#define FAIL_CREATEWND \
    if (!m_hwnd && !CreateWnd()) \
    { \
        hr = TrapError(E_OUTOFMEMORY); \
        goto exit;  \
    } \
    else
    
// these arrays describe element stack states, and are used to asses
// the current state of the element stack
static const HMELE c_rgPropFindPropStatStack[] =
{
    HMELE_DAV_MULTISTATUS,
    HMELE_DAV_RESPONSE,
    HMELE_DAV_PROPSTAT    
};

static const HMELE c_rgPropFindPropValueStack[] = 
{
    HMELE_DAV_MULTISTATUS,
    HMELE_DAV_RESPONSE,
    HMELE_DAV_PROPSTAT,
    HMELE_DAV_PROP,
    HMELE_UNKNOWN   // wildcard
};

static const HMELE c_rgPropFindResponseStack[] =
{
    HMELE_DAV_MULTISTATUS,
    HMELE_DAV_RESPONSE
};

static const HMELE c_rgMultiStatusResponseChildStack[] =
{
    HMELE_DAV_MULTISTATUS,
    HMELE_DAV_RESPONSE,
    HMELE_UNKNOWN
};

static const HMELE c_rgPropFindStatusStack[] =
{
    HMELE_DAV_MULTISTATUS,
    HMELE_DAV_RESPONSE,
    HMELE_DAV_PROPSTAT,
    HMELE_DAV_STATUS
}; 
// GET command
static const PFNHTTPMAILOPFUNC c_rgpfGet[] = 
{
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessGetResponse,
    &CHTTPMailTransport::FinalizeRequest
};

// DELETE command
static const PFNHTTPMAILOPFUNC c_rgpfDelete[] = 
{
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::FinalizeRequest
};

// PROPPATCH command
static const PFNHTTPMAILOPFUNC c_rgpfnPropPatch[] =
{
    &CHTTPMailTransport::GeneratePropPatchXML,
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::FinalizeRequest
};

// MKCOL command
static const PFNHTTPMAILOPFUNC c_rgpfnMkCol[] =
{
    &CHTTPMailTransport::OpenRequest,
	&CHTTPMailTransport::AddCharsetLine,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessCreatedResponse,
    &CHTTPMailTransport::ProcessLocationResponse,
    &CHTTPMailTransport::FinalizeRequest
};

// COPY command
static const PFNHTTPMAILOPFUNC c_rgpfnCopy[] =
{
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddDestinationHeader,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessCreatedResponse,
    &CHTTPMailTransport::ProcessLocationResponse,
    &CHTTPMailTransport::FinalizeRequest
};

// MOVE command
static const PFNHTTPMAILOPFUNC c_rgpfnMove[] =
{
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddDestinationHeader,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessCreatedResponse,
    &CHTTPMailTransport::ProcessLocationResponse,
    &CHTTPMailTransport::FinalizeRequest
};

// BMOVE command (data applies to bcopy and bmove)

#define BCOPYMOVE_MAXRESPONSES  10

XP_BEGIN_SCHEMA(HTTPMAILBCOPYMOVE)
    XP_SCHEMA_COL(HMELE_DAV_HREF, XPCF_MSVALIDMSRESPONSECHILD, XPCDT_STRA, HTTPMAILBCOPYMOVE, pszHref)
    XP_SCHEMA_COL(HMELE_DAV_LOCATION, XPCF_MSVALIDMSRESPONSECHILD, XPCDT_STRA, HTTPMAILBCOPYMOVE, pszLocation)
    XP_SCHEMA_COL(HMELE_DAV_STATUS, XPCF_MSVALIDMSRESPONSECHILD, XPCDT_IXPHRESULT, HTTPMAILBCOPYMOVE, hrResult)
XP_END_SCHEMA

static const PFNHTTPMAILOPFUNC c_rgpfnBMove[] =
{
    &CHTTPMailTransport::InitBCopyMove,
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddDestinationHeader,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessXMLResponse,
    &CHTTPMailTransport::FinalizeRequest
};

static const XMLPARSEFUNCS c_rgpfnBCopyMoveParse[] =
{
    &CHTTPMailTransport::CreateElement,
    &CHTTPMailTransport::BCopyMove_HandleText,
    &CHTTPMailTransport::BCopyMove_EndChildren
};

// MemberInfo Data. There are four schemas associated with this command.
// The first three are used to build up the propfind request, and are
// not used to parse responses. The fourth is the combined schema that
// is used for parsing.
//
// THE FOURTH SCHEMA MUST BE KEPT IN SYNC WITH THE FIRST THREE TO
// GUARANTEE THAT RESPONSES WILL BE PARSED CORRECTLY.

#define MEMBERINFO_MAXRESPONSES    10

// common property schema - used only for building the request
XP_BEGIN_SCHEMA(HTTPMEMBERINFO_COMMON)
    // common properties
    XP_SCHEMA_COL(HMELE_DAV_HREF, XPCF_PROPFINDHREF, XPCDT_STRA, HTTPMEMBERINFO, pszHref)
    XP_SCHEMA_COL(HMELE_DAV_ISFOLDER, XPFC_PROPFINDPROP, XPCDT_BOOL, HTTPMEMBERINFO, fIsFolder)
XP_END_SCHEMA

// folder property schema - used only for building the request
XP_BEGIN_SCHEMA(HTTPMEMBERINFO_FOLDER)
    XP_SCHEMA_COL(HMELE_DAV_DISPLAYNAME, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPMEMBERINFO, pszDisplayName)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_SPECIAL, XPFC_PROPFINDPROP, XPCDT_HTTPSPECIALFOLDER, HTTPMEMBERINFO, tySpecial)    
    XP_SCHEMA_COL(HMELE_DAV_HASSUBS, XPFC_PROPFINDPROP, XPCDT_BOOL, HTTPMEMBERINFO, fHasSubs)
    XP_SCHEMA_COL(HMELE_DAV_NOSUBS, XPFC_PROPFINDPROP, XPCDT_BOOL, HTTPMEMBERINFO, fNoSubs)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_UNREADCOUNT, XPFC_PROPFINDPROP, XPCDT_DWORD, HTTPMEMBERINFO, dwUnreadCount)
    XP_SCHEMA_COL(HMELE_DAV_VISIBLECOUNT, XPFC_PROPFINDPROP, XPCDT_DWORD, HTTPMEMBERINFO, dwVisibleCount)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_SPECIAL, XPFC_PROPFINDPROP, XPCDT_HTTPSPECIALFOLDER, HTTPMEMBERINFO, tySpecial)    
XP_END_SCHEMA

// message property schema - used only for building the request
XP_BEGIN_SCHEMA(HTTPMEMBERINFO_MESSAGE)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_READ, XPFC_PROPFINDPROP, XPCDT_BOOL, HTTPMEMBERINFO, fRead)
    XP_SCHEMA_COL(HMELE_MAIL_HASATTACHMENT, XPFC_PROPFINDPROP, XPCDT_BOOL, HTTPMEMBERINFO, fHasAttachment)
    XP_SCHEMA_COL(HMELE_MAIL_TO, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPMEMBERINFO, pszTo)
    XP_SCHEMA_COL(HMELE_MAIL_FROM, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPMEMBERINFO, pszFrom)
    XP_SCHEMA_COL(HMELE_MAIL_SUBJECT, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPMEMBERINFO, pszSubject)
    XP_SCHEMA_COL(HMELE_MAIL_DATE, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPMEMBERINFO, pszDate)
    XP_SCHEMA_COL(HMELE_DAV_GETCONTENTLENGTH, XPFC_PROPFINDPROP, XPCDT_DWORD, HTTPMEMBERINFO, dwContentLength)
XP_END_SCHEMA

// combined schema - used for parsing responses
XP_BEGIN_SCHEMA(HTTPMEMBERINFO)
    // common properties
    XP_SCHEMA_COL(HMELE_DAV_HREF, XPCF_PROPFINDHREF, XPCDT_STRA, HTTPMEMBERINFO, pszHref)
    XP_SCHEMA_COL(HMELE_DAV_ISFOLDER, XPFC_PROPFINDPROP, XPCDT_BOOL, HTTPMEMBERINFO, fIsFolder)

    // folder properties    
    XP_SCHEMA_COL(HMELE_DAV_DISPLAYNAME, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPMEMBERINFO, pszDisplayName)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_SPECIAL, XPFC_PROPFINDPROP, XPCDT_HTTPSPECIALFOLDER, HTTPMEMBERINFO, tySpecial)    
    XP_SCHEMA_COL(HMELE_DAV_HASSUBS, XPFC_PROPFINDPROP, XPCDT_BOOL, HTTPMEMBERINFO, fHasSubs)
    XP_SCHEMA_COL(HMELE_DAV_NOSUBS, XPFC_PROPFINDPROP, XPCDT_BOOL, HTTPMEMBERINFO, fNoSubs)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_UNREADCOUNT, XPFC_PROPFINDPROP, XPCDT_DWORD, HTTPMEMBERINFO, dwUnreadCount)
    XP_SCHEMA_COL(HMELE_DAV_VISIBLECOUNT, XPFC_PROPFINDPROP, XPCDT_DWORD, HTTPMEMBERINFO, dwVisibleCount)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_SPECIAL, XPFC_PROPFINDPROP, XPCDT_HTTPSPECIALFOLDER, HTTPMEMBERINFO, tySpecial)    

    // message properties
    XP_SCHEMA_COL(HMELE_HTTPMAIL_READ, XPFC_PROPFINDPROP, XPCDT_BOOL, HTTPMEMBERINFO, fRead)
    XP_SCHEMA_COL(HMELE_MAIL_HASATTACHMENT, XPFC_PROPFINDPROP, XPCDT_BOOL, HTTPMEMBERINFO, fHasAttachment)
    XP_SCHEMA_COL(HMELE_MAIL_TO, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPMEMBERINFO, pszTo)
    XP_SCHEMA_COL(HMELE_MAIL_FROM, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPMEMBERINFO, pszFrom)
    XP_SCHEMA_COL(HMELE_MAIL_SUBJECT, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPMEMBERINFO, pszSubject)
    XP_SCHEMA_COL(HMELE_MAIL_DATE, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPMEMBERINFO, pszDate)
    XP_SCHEMA_COL(HMELE_DAV_GETCONTENTLENGTH, XPFC_PROPFINDPROP, XPCDT_DWORD, HTTPMEMBERINFO, dwContentLength)
XP_END_SCHEMA

static const XMLPARSEFUNCS c_rgpfnMemberInfoParse[] =
{
    &CHTTPMailTransport::CreateElement,
    &CHTTPMailTransport::MemberInfo_HandleText,
    &CHTTPMailTransport::MemberInfo_EndChildren
};

static const PFNHTTPMAILOPFUNC c_rgpfnMemberInfo[] =
{
    &CHTTPMailTransport::InitMemberInfo,
    &CHTTPMailTransport::GeneratePropFindXML,
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddDepthHeader,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessXMLResponse,
    &CHTTPMailTransport::RequireMultiStatus,
    &CHTTPMailTransport::FinalizeRequest
};
    
// Operations which share MemberError-based responses (MarkRead, BDELETE)


#define MEMBERERROR_MAXRESPONSES    10

XP_BEGIN_SCHEMA(HTTPMEMBERERROR)
    XP_SCHEMA_COL(HMELE_DAV_HREF, XPCF_PROPFINDHREF, XPCDT_STRA, HTTPMEMBERERROR, pszHref)
    XP_SCHEMA_COL(HMELE_DAV_STATUS, XPCF_MSVALIDMSRESPONSECHILD, XPCDT_IXPHRESULT, HTTPMEMBERERROR, hrResult)
XP_END_SCHEMA

static const XMLPARSEFUNCS c_rgpfnMemberErrorParse[] =
{
    &CHTTPMailTransport::CreateElement,
    &CHTTPMailTransport::MemberError_HandleText,
    &CHTTPMailTransport::MemberError_EndChildren
};

static const PFNHTTPMAILOPFUNC c_rgpfnMarkRead[] =
{
    &CHTTPMailTransport::InitMemberError,
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessXMLResponse,
    &CHTTPMailTransport::FinalizeRequest
};

// SendMessage

static const PFNHTTPMAILOPFUNC c_rgpfnSendMessage[] =
{
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddContentTypeHeader,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendPostRequest,
    &CHTTPMailTransport::ProcessPostResponse,
    &CHTTPMailTransport::FinalizeRequest
};

// RootProps

XP_BEGIN_SCHEMA(ROOTPROPS)
    XP_SCHEMA_COL(HMELE_HOTMAIL_ADBAR, XPFC_PROPFINDPROP, XPCDT_STRA, ROOTPROPS, pszAdbar)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_CONTACTS, XPFC_PROPFINDPROP, XPCDT_STRA, ROOTPROPS, pszContacts)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_INBOX, XPFC_PROPFINDPROP, XPCDT_STRA, ROOTPROPS, pszInbox)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_OUTBOX, XPFC_PROPFINDPROP, XPCDT_STRA, ROOTPROPS, pszOutbox)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_SENDMSG, XPFC_PROPFINDPROP, XPCDT_STRA, ROOTPROPS, pszSendMsg)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_SENTITEMS, XPFC_PROPFINDPROP, XPCDT_STRA, ROOTPROPS, pszSentItems)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_DELETEDITEMS, XPFC_PROPFINDPROP, XPCDT_STRA, ROOTPROPS, pszDeletedItems)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_DRAFTS, XPFC_PROPFINDPROP, XPCDT_STRA, ROOTPROPS, pszDrafts)
    XP_SCHEMA_COL(HMELE_HTTPMAIL_MSGFOLDERROOT, XPFC_PROPFINDPROP, XPCDT_STRA, ROOTPROPS, pszMsgFolderRoot)
    XP_SCHEMA_COL(HMELE_HOTMAIL_MAXPOLLINGINTERVAL, XPFC_PROPFINDPROP, XPCDT_DWORD, ROOTPROPS, dwMaxPollingInterval)
    XP_SCHEMA_COL(HMELE_HOTMAIL_SIG, XPFC_PROPFINDPROP, XPCDT_STRA, ROOTPROPS, pszSig)
XP_END_SCHEMA

static const XMLPARSEFUNCS c_rgpfnRootPropsParse[] =
{
    &CHTTPMailTransport::CreateElement,
    &CHTTPMailTransport::RootProps_HandleText,
    &CHTTPMailTransport::RootProps_EndChildren
};

static const PFNHTTPMAILOPFUNC c_rgpfnRootProps[] = 
{
    &CHTTPMailTransport::InitRootProps,
    &CHTTPMailTransport::GeneratePropFindXML,
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddDepthHeader,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessXMLResponse,
    &CHTTPMailTransport::FinalizeRootProps
};

static const PFNHTTPMAILOPFUNC c_rgpfnPost[] =
{
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddContentTypeHeader,
	&CHTTPMailTransport::AddCharsetLine,
    &CHTTPMailTransport::SendPostRequest,
    //&CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessPostResponse,
    &CHTTPMailTransport::FinalizeRequest
};

static const PFNHTTPMAILOPFUNC c_rgpfnPut[] = 
{
    &CHTTPMailTransport::OpenRequest,
	&CHTTPMailTransport::AddCharsetLine,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessPostResponse,
    &CHTTPMailTransport::FinalizeRequest
};

// ListContacts Data

#define LISTCONTACTS_MAXRESPONSES   10

XP_BEGIN_SCHEMA(HTTPCONTACTID)
    XP_SCHEMA_COL(HMELE_DAV_HREF, XPCF_PROPFINDHREF, XPCDT_STRA, HTTPCONTACTID, pszHref)
    XP_SCHEMA_COL(HMELE_DAV_ID, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTID, pszId)    
    XP_SCHEMA_COL(HMELE_CONTACTS_GROUP, XPFC_PROPFINDPROP, XPCDT_HTTPCONTACTTYPE, HTTPCONTACTID, tyContact)
    XP_SCHEMA_COL(HMELE_HOTMAIL_MODIFIED, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTID, pszModified)
XP_END_SCHEMA

static const PFNHTTPMAILOPFUNC c_rgpfnListContacts[] = 
{
    &CHTTPMailTransport::InitListContacts,
    &CHTTPMailTransport::GeneratePropFindXML,
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddDepthHeader,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessXMLResponse,
    &CHTTPMailTransport::RequireMultiStatus,
    &CHTTPMailTransport::FinalizeRequest
};

static const XMLPARSEFUNCS c_rgpfnListContactsParse[] =
{
    &CHTTPMailTransport::CreateElement,
    &CHTTPMailTransport::ListContacts_HandleText,
    &CHTTPMailTransport::ListContacts_EndChildren
};

// ContactInfo Data
#define CONTACTINFO_MAXRESPONSES   10

XP_BEGIN_SCHEMA(HTTPCONTACTINFO)
    XP_SCHEMA_COL(HMELE_DAV_HREF, XPCF_PROPFINDHREF, XPCDT_STRA, HTTPCONTACTINFO, pszHref)
    XP_SCHEMA_COL(HMELE_DAV_ID, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszId)    
    XP_SCHEMA_COL(HMELE_CONTACTS_GROUP, XPFC_PROPFINDPROP, XPCDT_HTTPCONTACTTYPE, HTTPCONTACTINFO, tyContact)
    XP_SCHEMA_COL(HMELE_HOTMAIL_MODIFIED, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszModified)
    XP_SCHEMA_COL(HMELE_CONTACTS_CN, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszDisplayName)
    XP_SCHEMA_COL(HMELE_CONTACTS_GIVENNAME, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszGivenName)
    XP_SCHEMA_COL(HMELE_CONTACTS_SN, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszSurname)
    XP_SCHEMA_COL(HMELE_CONTACTS_NICKNAME, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszNickname)
    XP_SCHEMA_COL(HMELE_CONTACTS_MAIL, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszEmail)
    XP_SCHEMA_COL(HMELE_CONTACTS_HOMESTREET, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszHomeStreet)
    XP_SCHEMA_COL(HMELE_CONTACTS_HOMECITY, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszHomeCity)
    XP_SCHEMA_COL(HMELE_CONTACTS_HOMESTATE, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszHomeState)
    XP_SCHEMA_COL(HMELE_CONTACTS_HOMEPOSTALCODE, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszHomePostalCode)
    XP_SCHEMA_COL(HMELE_CONTACTS_HOMECOUNTRY, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszHomeCountry)
    XP_SCHEMA_COL(HMELE_CONTACTS_O, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszCompany)
    XP_SCHEMA_COL(HMELE_CONTACTS_STREET, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszWorkStreet)
    XP_SCHEMA_COL(HMELE_CONTACTS_L, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszWorkCity)
    XP_SCHEMA_COL(HMELE_CONTACTS_ST, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszWorkState)
    XP_SCHEMA_COL(HMELE_CONTACTS_POSTALCODE, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszWorkPostalCode)
    XP_SCHEMA_COL(HMELE_CONTACTS_FRIENDLYCOUNTRYNAME, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszWorkCountry)
    XP_SCHEMA_COL(HMELE_CONTACTS_HOMEPHONE, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszHomePhone)
    XP_SCHEMA_COL(HMELE_CONTACTS_HOMEFAX, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszHomeFax)
    XP_SCHEMA_COL(HMELE_CONTACTS_TELEPHONENUMBER, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszWorkPhone)
    XP_SCHEMA_COL(HMELE_CONTACTS_FACSIMILETELEPHONENUMBER, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszWorkFax)
    XP_SCHEMA_COL(HMELE_CONTACTS_MOBILE, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszMobilePhone)
    XP_SCHEMA_COL(HMELE_CONTACTS_OTHERTELEPHONE, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszOtherPhone)
    XP_SCHEMA_COL(HMELE_CONTACTS_BDAY, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszBday)
    XP_SCHEMA_COL(HMELE_CONTACTS_PAGER, XPFC_PROPFINDPROP, XPCDT_STRA, HTTPCONTACTINFO, pszPager)
XP_END_SCHEMA

static const XMLPARSEFUNCS c_rgpfnContactInfoParse[] =
{
    &CHTTPMailTransport::CreateElement,
    &CHTTPMailTransport::ContactInfo_HandleText,
    &CHTTPMailTransport::ContactInfo_EndChildren
};

static const PFNHTTPMAILOPFUNC c_rgpfnContactInfo[] =
{
    &CHTTPMailTransport::InitContactInfo,
    &CHTTPMailTransport::GeneratePropFindXML,
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddDepthHeader,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessXMLResponse,
    &CHTTPMailTransport::FinalizeRequest
};

// PostContact Data
static const PFNHTTPMAILOPFUNC c_rgpfnPostContact[] =
{
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessPostContactResponse,
    &CHTTPMailTransport::InitListContacts,
    &CHTTPMailTransport::GeneratePropFindXML,
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddDepthHeader,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessXMLResponse,
    &CHTTPMailTransport::FinalizeRequest
};

static const XMLPARSEFUNCS c_rgpfnPostOrPatchContactParse[] =
{
    &CHTTPMailTransport::CreateElement,
    &CHTTPMailTransport::PostOrPatchContact_HandleText,
    &CHTTPMailTransport::PostOrPatchContact_EndChildren
};

// PatchContact data

static const PFNHTTPMAILOPFUNC c_rgpfnPatchContact[] =
{
    &CHTTPMailTransport::GeneratePropPatchXML,
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessPatchContactResponse,
    &CHTTPMailTransport::InitListContacts,
    &CHTTPMailTransport::GeneratePropFindXML,
    &CHTTPMailTransport::OpenRequest,
    &CHTTPMailTransport::AddDepthHeader,
    &CHTTPMailTransport::AddCommonHeaders,
    &CHTTPMailTransport::SendRequest,
    &CHTTPMailTransport::ProcessXMLResponse,
    &CHTTPMailTransport::FinalizeRequest
};

// special folders
typedef struct tagHTTPSPECIALFOLDER
{
    const WCHAR             *pwcName;
    ULONG                   ulLen;
    HTTPMAILSPECIALFOLDER   tyFolder;
} HTTPSPECIALFOLDER, *LPHTTPSPECIALFOLDER;

static const HTTPSPECIALFOLDER c_rgpfnSpecialFolder[] =
{
    { DAV_STR_LEN(InboxSpecialFolder), HTTPMAIL_SF_INBOX },
    { DAV_STR_LEN(DeletedItemsSpecialFolder), HTTPMAIL_SF_DELETEDITEMS },
    { DAV_STR_LEN(DraftsSpecialFolder), HTTPMAIL_SF_DRAFTS },
    { DAV_STR_LEN(OutboxSpecialFolder), HTTPMAIL_SF_OUTBOX },
    { DAV_STR_LEN(SentItemsSpecialFolder), HTTPMAIL_SF_SENTITEMS },
    { DAV_STR_LEN(ContactsSpecialFolder), HTTPMAIL_SF_CONTACTS },
    { DAV_STR_LEN(CalendarSpecialFolder), HTTPMAIL_SF_CALENDAR },
    { DAV_STR_LEN(MsnPromoSpecialFolder), HTTPMAIL_SF_MSNPROMO },
    { DAV_STR_LEN(BulkMailSpecialFolder), HTTPMAIL_SF_BULKMAIL },
};

#define VALIDSTACK(rg) ValidStack(rg, ARRAYSIZE(rg))

static const char s_szHTTPMailWndClass[] = "HTTPMailWndClass";

// Notification messages used to communicate between the async thread
// and the window proc
#define SPM_HTTPMAIL_STATECHANGED       (WM_USER + 1)
#define SPM_HTTPMAIL_SENDRESPONSE       (WM_USER + 2)
#define SPM_HTTPMAIL_LOGONPROMPT        (WM_USER + 3)
#define SPM_HTTPMAIL_GETPARENTWINDOW    (WM_USER + 4)

// --------------------------------------------------------------------------------
// static functions
// --------------------------------------------------------------------------------

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_HrCrackUrl
// --------------------------------------------------------------------------------

HRESULT HrCrackUrl(
                LPSTR pszUrl, 
                LPSTR *ppszHost, 
                LPSTR *ppszPath, 
                INTERNET_PORT *pPort)
{
    URL_COMPONENTS      uc;
    char                szHost[INTERNET_MAX_HOST_NAME_LENGTH];
    char                szPath[INTERNET_MAX_PATH_LENGTH];

    if (NULL == pszUrl)
        return E_INVALIDARG;

    if (ppszHost)
        *ppszHost = NULL;

    if (ppszPath)
        *ppszPath = NULL;

    if (pPort)
        *pPort = INTERNET_INVALID_PORT_NUMBER;

    ZeroMemory(&uc, sizeof(URL_COMPONENTS));
    uc.dwStructSize = sizeof(URL_COMPONENTS);
    uc.lpszHostName = szHost;
    uc.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
    uc.lpszUrlPath = szPath;
    uc.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
    
    if (!InternetCrackUrl(pszUrl, NULL, 0, &uc))
        return E_INVALIDARG;

    // validate the protocol
    if (INTERNET_SCHEME_HTTP != uc.nScheme && INTERNET_SCHEME_HTTPS != uc.nScheme)
        return E_INVALIDARG;

    // copy the response data
    if (ppszHost)
    {
        *ppszHost = PszDupA(uc.lpszHostName);
        if (!*ppszHost)
            return E_OUTOFMEMORY;
    }

    if (ppszPath)
    {
        *ppszPath = PszDupA(uc.lpszUrlPath);
        if (!*ppszPath)
        {
            SafeMemFree(*ppszHost);
            return E_OUTOFMEMORY;
        }
    }

    if (pPort)
        *pPort = uc.nPort;

    return S_OK;
}

// --------------------------------------------------------------------------------
// Utility functions
// --------------------------------------------------------------------------------

// --------------------------------------------------------------------------------
// HttpErrorToIxpResult
// --------------------------------------------------------------------------------
HRESULT HttpErrorToIxpResult(DWORD dwHttpError)
{
    for (DWORD dw = 0; dw < ARRAYSIZE(c_rgHttpErrorMap); dw++)
    {
        if (c_rgHttpErrorMap[dw].dwHttpError == dwHttpError)
            return c_rgHttpErrorMap[dw].ixpResult;
    }

    return E_FAIL;
}

// --------------------------------------------------------------------------------
// HrParseHTTPStatus
// --------------------------------------------------------------------------------
HRESULT HrParseHTTPStatus(LPSTR pszStatusStr, DWORD *pdwStatus)
{
    LPSTR   psz;
    LPSTR   pszEnd;
    char    chSaved;

    if (!pszStatusStr || !pdwStatus)
        return E_INVALIDARG;

    *pdwStatus = 0;

    // status is of the form "HTTP/1.1 200 OK"
    psz = PszSkipWhiteA(pszStatusStr);
    if ('\0' == *psz)
        return E_INVALIDARG;

    psz = PszScanToWhiteA(psz);
    if ('\0' == *psz)
        return E_INVALIDARG;

    psz = PszSkipWhiteA(psz);
    if ('\0' == *psz)
        return E_INVALIDARG;

    // psz now points at the numeric component
    pszEnd = PszScanToWhiteA(psz);
    if ('\0' == *psz)
        return E_INVALIDARG;
    
    // temporarily modify the string in place
    chSaved = *pszEnd;
    *pszEnd = '\0';
    
    *pdwStatus = StrToInt(psz);
    *pszEnd = chSaved;

    return S_OK;
}

// --------------------------------------------------------------------------------
// HrGetStreamSize
// --------------------------------------------------------------------------------
static HRESULT HrGetStreamSize(LPSTREAM pstm, ULONG *pcb)
{
    // Locals
    HRESULT hr=S_OK;
    ULARGE_INTEGER uliPos = {0,0};
    LARGE_INTEGER liOrigin = {0,0};

    // Seek
    hr = pstm->Seek(liOrigin, STREAM_SEEK_END, &uliPos);
    if (FAILED(hr))
        goto error;

    // set size
    *pcb = uliPos.LowPart;

error:
    // Done
    return hr;
}


// --------------------------------------------------------------------------------
// HrAddPropFindProps
// --------------------------------------------------------------------------------
HRESULT HrAddPropFindProps(IPropFindRequest *pRequest, const HMELE *rgEle, DWORD cEle)
{
    HRESULT     hr;
    HMELE       ele;

    for (DWORD i = 0; i < cEle; ++i)
    {
        ele = rgEle[i];
        hr = pRequest->AddProperty(
                        rgHTTPMailDictionary[ele].dwNamespaceID,
                        const_cast<char *>(rgHTTPMailDictionary[ele].pszName));
        if (FAILED(hr))
            goto exit;
    }

exit:

    return hr;
}

// --------------------------------------------------------------------------------
// HrAddPropFindSchemaProps
// --------------------------------------------------------------------------------
HRESULT HrAddPropFindSchemaProps(
                        IPropFindRequest *pRequest, 
                        const XPCOLUMN *prgCols, 
                        DWORD cCols)
{
    HRESULT     hr = S_OK;
    HMELE       ele;

    for (DWORD i = 0; i < cCols; i++)
    {
        if (!!(prgCols[i].dwFlags & XPCF_PFREQUEST))
        {
            hr = pRequest->AddProperty(
                            rgHTTPMailDictionary[prgCols[i].ele].dwNamespaceID,
                            rgHTTPMailDictionary[prgCols[i].ele].pszName);

            if (FAILED(hr))
                goto exit;
        }
    }

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// _HrGenerateRfc821Stream
// --------------------------------------------------------------------------------
HRESULT _HrGenerateRfc821Stream(LPCSTR pszFrom, 
                                LPHTTPTARGETLIST pTargets,
                                IStream **ppRfc821Stream)
{
    HRESULT     hr = S_OK;
    IStream     *pStream = NULL;
    DWORD       dw;
    DWORD       cbCloseTerm;
    DWORD       cbRcptTo;

    IxpAssert(pszFrom);
    IxpAssert(pTargets);
    IxpAssert(ppRfc821Stream);

    *ppRfc821Stream = NULL;

    cbCloseTerm = lstrlen(c_szXMLCloseElementCRLF);
    cbRcptTo = lstrlen(c_szRcptTo);

    pStream = new CVirtualStream();
    if (NULL == pStream)
    {
        hr = TraceResult(E_OUTOFMEMORY);
        goto exit;
    }

    // write out 'mail from'
    FAIL_EXIT_STREAM_WRITE((*pStream), c_szMailFrom);
    FAIL_EXIT_STREAM_WRITE((*pStream), pszFrom);
    FAIL_EXIT(hr = pStream->Write(c_szXMLCloseElementCRLF, cbCloseTerm, NULL));

    // write out the 'rcpt to' lines
    for (dw = 0; dw < pTargets->cTarget; ++dw)
    {
        FAIL_EXIT(hr = pStream->Write(c_szRcptTo, cbRcptTo, NULL));
        FAIL_EXIT_STREAM_WRITE((*pStream), pTargets->prgTarget[dw]);
        FAIL_EXIT(hr = pStream->Write(c_szXMLCloseElementCRLF, cbCloseTerm, NULL));
    }

    // append an extra crlf to the end of the stream
    FAIL_EXIT_STREAM_WRITE((*pStream), c_szCRLF);
    
    *ppRfc821Stream = pStream;
    pStream = NULL;

exit:
    SafeRelease(pStream);
    return hr;
}

// --------------------------------------------------------------------------------
// _EscapeString
// --------------------------------------------------------------------------------
LPSTR   _EscapeString(LPSTR pszIn)
{
    CByteStream     stream;
    DWORD           dwLen;
    LPSTR           pszLastNonEsc, pszNext, pszOut;    
    HRESULT         hr;
    
    if (NULL == pszIn)
        return NULL;

    pszLastNonEsc = pszIn;
    pszNext = pszIn;

    while (*pszNext)
    {
        switch (*pszNext)
        {
            case '<':
            case '>':
            case '&':
                if (FAILED(hr = stream.Write(pszLastNonEsc, (ULONG) (pszNext - pszLastNonEsc), NULL)))
                    goto exit;

                if (*pszNext == '<')
                {
                    if (FAILED(hr = stream.Write(c_szEscLessThan, lstrlen(c_szEscLessThan), NULL)))
                        goto exit;
                }
                else if (*pszNext == '>')
                {
                    if (FAILED(hr = stream.Write(c_szEscGreaterThan, lstrlen(c_szEscGreaterThan), NULL)))
                        goto exit;
                }
                else
                {
                    if (FAILED(hr = stream.Write(c_szEscAmp, lstrlen(c_szEscAmp), NULL)))
                        goto exit;
                }
                pszLastNonEsc = CharNextExA(CP_ACP, pszNext, 0);
                break;
        }
        pszNext = CharNextExA(CP_ACP, pszNext, 0);
    }

    if (FAILED(hr = stream.Write(pszLastNonEsc, (ULONG) (pszNext - pszLastNonEsc), NULL)))
        goto exit;

    FAIL_EXIT(hr = stream.HrAcquireStringA(&dwLen, (LPSTR *)&pszOut, ACQ_DISPLACE));
    
    return pszOut;

exit:
    return NULL;
}

const HMELE g_rgContactEle[] = {
    HMELE_UNKNOWN,
    HMELE_UNKNOWN,
    HMELE_UNKNOWN,
    HMELE_UNKNOWN,
    HMELE_UNKNOWN,
    HMELE_CONTACTS_GIVENNAME,
    HMELE_CONTACTS_SN,
    HMELE_CONTACTS_NICKNAME,
    HMELE_CONTACTS_MAIL,
    HMELE_CONTACTS_HOMESTREET,
    HMELE_CONTACTS_HOMECITY,
    HMELE_CONTACTS_HOMESTATE,
    HMELE_CONTACTS_HOMEPOSTALCODE,
    HMELE_CONTACTS_HOMECOUNTRY,
    HMELE_CONTACTS_O,
    HMELE_CONTACTS_STREET,
    HMELE_CONTACTS_L,
    HMELE_CONTACTS_ST,
    HMELE_CONTACTS_POSTALCODE,
    HMELE_CONTACTS_FRIENDLYCOUNTRYNAME,
    HMELE_CONTACTS_HOMEPHONE,
    HMELE_CONTACTS_HOMEFAX,
    HMELE_CONTACTS_TELEPHONENUMBER,
    HMELE_CONTACTS_FACSIMILETELEPHONENUMBER,
    HMELE_CONTACTS_MOBILE,
    HMELE_CONTACTS_OTHERTELEPHONE,
    HMELE_CONTACTS_BDAY,
    HMELE_CONTACTS_PAGER
};

#define CCHMAX_TAGBUFFER    128

HRESULT HrGeneratePostContactXML(LPHTTPCONTACTINFO pciInfo, LPVOID *ppvXML, DWORD *pdwLen)
{
    HRESULT                 hr = S_OK;
    CByteStream             stream;
    CDAVNamespaceArbiterImp dna;
    DWORD                   dwIndex, dwSize = ARRAYSIZE(g_rgContactEle);
    DWORD                   iBufferSize;
    TCHAR                   szTagBuffer[CCHMAX_TAGBUFFER+1];
    LPSTR                  *prgsz = (LPSTR*)pciInfo, pszEsc;
    LPCSTR                  pszPropName;
    *ppvXML = NULL;
    *pdwLen = 0;

    if (NULL == ppvXML)
        return E_INVALIDARG;

    // write the DAV header. we ALWAYS post using the windows-1252 code
    // page for this release.
    if (FAILED(hr = stream.Write(c_szXML1252Head, lstrlen(c_szXML1252Head), NULL)))
        goto exit;

    dna.m_rgbNsUsed[DAVNAMESPACE_CONTACTS] = TRUE;
    dna.m_rgbNsUsed[DAVNAMESPACE_DAV] = TRUE;

    // write out the contacts header
    if (FAILED(hr = stream.Write(c_szContactHead, lstrlen(c_szContactHead), NULL)))
        goto exit;

    if (FAILED(hr = dna.WriteNamespaces(&stream)))
        goto exit;

    if (FAILED(hr = stream.Write(c_szXMLCloseElement, lstrlen(c_szXMLCloseElement), NULL)))
        goto exit;

    // [PaulHi] 3/11/99  Implementing WAB/HM group contact syncing
    // Include the xml group tag if this is a group contact item
    if (pciInfo->tyContact == HTTPMAIL_CT_GROUP)
    {
        if (FAILED(hr = stream.Write(c_szCRLFTab, lstrlen(c_szCRLFTab), NULL)))
            goto exit;
        if (FAILED(hr = stream.Write(c_szGroupSwitch, lstrlen(c_szGroupSwitch), NULL)))
            goto exit;
    }

    for (dwIndex = 0; dwIndex < dwSize; dwIndex ++)
    {
        if (prgsz[dwIndex] && g_rgContactEle[dwIndex] != HMELE_UNKNOWN)
        {
            pszPropName = rgHTTPMailDictionary[g_rgContactEle[dwIndex]].pszName;

            if (FAILED(hr = stream.Write(c_szOpenContactNamespace, lstrlen(c_szOpenContactNamespace), NULL)))
                goto exit;
    
            if (FAILED(hr = stream.Write(pszPropName, lstrlen(pszPropName), NULL)))
                goto exit;

            if (FAILED(hr = stream.Write(c_szXMLCloseElement, lstrlen(c_szXMLCloseElement), NULL)))
                goto exit;

            pszEsc = _EscapeString(prgsz[dwIndex]);

            if (!pszEsc)
                goto exit;

            hr = stream.Write(pszEsc, lstrlen(pszEsc), NULL);

            SafeMemFree(pszEsc);

            if (FAILED(hr))
                goto exit;
            
            if (FAILED(hr = stream.Write(c_szCloseContactNamespace, lstrlen(c_szCloseContactNamespace), NULL)))
                goto exit;
    
            if (FAILED(hr = stream.Write(pszPropName, lstrlen(pszPropName), NULL)))
                goto exit;

            if (FAILED(hr = stream.Write(c_szXMLCloseElement, lstrlen(c_szXMLCloseElement), NULL)))
                goto exit;
        }
    }

    if (FAILED(hr = stream.Write(c_szContactTail, lstrlen(c_szContactTail), NULL)))
        goto exit;

    FAIL_EXIT(hr = stream.HrAcquireStringA(pdwLen, (LPSTR *)ppvXML, ACQ_DISPLACE));

exit:
    return hr;
}

HRESULT HrCreatePatchContactRequest(LPHTTPCONTACTINFO pciInfo, IPropPatchRequest **ppRequest)
{
    HRESULT             hr = S_OK;
    LPSTR              *prgsz = (LPSTR*)pciInfo, pszEsc;
    DWORD               dwIndex, dwSize = ARRAYSIZE(g_rgContactEle);
    CPropPatchRequest  *pRequest = NULL;
    
    *ppRequest = NULL;

    pRequest = new CPropPatchRequest();
    if (NULL == pRequest)
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    // always specify windows-1252 encoding for this release
    pRequest->SpecifyWindows1252Encoding(TRUE);

    for (dwIndex = 0; dwIndex < dwSize; dwIndex ++)
    {
        if (g_rgContactEle[dwIndex] != HMELE_UNKNOWN)
        {
            if (prgsz[dwIndex])
            {
                // values with content are added.  Empty strings are deleted.  Null values are ignored.
                if (*(prgsz[dwIndex]))
                {
                    pszEsc = _EscapeString(prgsz[dwIndex]);
                    if (!pszEsc)
                        goto exit;

                    hr = pRequest->SetProperty(DAVNAMESPACE_CONTACTS, const_cast<char *>(rgHTTPMailDictionary[g_rgContactEle[dwIndex]].pszName), pszEsc);
                    
                    SafeMemFree(pszEsc);

                    if (FAILED(hr))
                        goto exit;
                                
                }
                else
                {
                    if (FAILED(hr = pRequest->RemoveProperty(DAVNAMESPACE_CONTACTS, const_cast<char *>(rgHTTPMailDictionary[g_rgContactEle[dwIndex]].pszName))))
                        goto exit;
                }
            }
        }
    }
exit:
    if (FAILED(hr))
        SafeRelease(pRequest);
    else
        *ppRequest = pRequest;

    return hr;
}

HRESULT HrGenerateSimpleBatchXML(
                            LPCSTR pszRootName,
                            LPHTTPTARGETLIST pTargets,
                            LPVOID *ppvXML,
                            DWORD *pdwLen)
{
    HRESULT                 hr = S_OK;
    CByteStream             stream;
    DWORD                   dwIndex;
    DWORD                   dwHrefHeadLen, dwHrefTailLen;

    IxpAssert(NULL != pszRootName);
    IxpAssert(NULL != pTargets);
    IxpAssert(pTargets->cTarget >= 1);
    IxpAssert(NULL != pTargets->prgTarget);
    IxpAssert(NULL != ppvXML);
    IxpAssert(NULL != pdwLen);

    dwHrefHeadLen = lstrlen(c_szHrefHead);
    dwHrefTailLen = lstrlen(c_szHrefTail);

    // write the DAV header
    FAIL_EXIT_STREAM_WRITE(stream, c_szXMLHead);

    FAIL_EXIT_STREAM_WRITE(stream, c_szBatchHead1);
    FAIL_EXIT_STREAM_WRITE(stream, pszRootName);
    FAIL_EXIT_STREAM_WRITE(stream, c_szBatchHead2);

    FAIL_EXIT_STREAM_WRITE(stream, c_szTargetHead);

    // write out the targets
    for (dwIndex = 0; dwIndex < pTargets->cTarget; dwIndex++)
    {
        if (FAILED(hr = stream.Write(c_szHrefHead, dwHrefHeadLen, NULL)))
            goto exit;

        FAIL_EXIT_STREAM_WRITE(stream, pTargets->prgTarget[dwIndex]);

        if (FAILED(hr = stream.Write(c_szHrefTail, dwHrefTailLen, NULL)))
            goto exit;
    }

    FAIL_EXIT_STREAM_WRITE(stream, c_szTargetTail);

    FAIL_EXIT_STREAM_WRITE(stream, c_szBatchTail);
    FAIL_EXIT_STREAM_WRITE(stream, pszRootName);
    FAIL_EXIT_STREAM_WRITE(stream, c_szXMLCloseElement);

    // take ownership of the bytestream
    FAIL_EXIT(hr = stream.HrAcquireStringA(pdwLen, (LPSTR *)ppvXML, ACQ_DISPLACE));

exit:
    return hr;
}

HRESULT HrGenerateMultiDestBatchXML(
                        LPCSTR pszRootName,
                        LPHTTPTARGETLIST pTargets, 
                        LPHTTPTARGETLIST pDestinations,
                        LPVOID *ppvXML,
                        DWORD *pdwLen)
{
    HRESULT         hr = S_OK;
    CByteStream     stream;
    DWORD           dwIndex;

    IxpAssert(NULL != pszRootName);
    IxpAssert(NULL != pTargets);
    IxpAssert(NULL != pDestinations);
    IxpAssert(NULL != ppvXML);
    IxpAssert(NULL != pdwLen);

    // source and destination must have same count
    if (pTargets->cTarget != pDestinations->cTarget)
        return E_INVALIDARG;

    *ppvXML = NULL;
    *pdwLen = 0;

    // write the DAV header
    FAIL_EXIT_STREAM_WRITE(stream, c_szXMLHead);

    // write the command header
    FAIL_EXIT_STREAM_WRITE(stream, c_szBatchHead1);
    
    FAIL_EXIT_STREAM_WRITE(stream, pszRootName);
    FAIL_EXIT_STREAM_WRITE(stream, c_szBatchHead2);

    // write out the targets
    for (dwIndex = 0; dwIndex < pTargets->cTarget; dwIndex++)
    {
        IxpAssert(NULL != pTargets->prgTarget[dwIndex]);
        if (NULL != pTargets->prgTarget[dwIndex])
        {
            FAIL_EXIT_STREAM_WRITE(stream, c_szTargetHead);

            FAIL_EXIT_STREAM_WRITE(stream, c_szHrefHead);
            FAIL_EXIT_STREAM_WRITE(stream, pTargets->prgTarget[dwIndex]);
            FAIL_EXIT_STREAM_WRITE(stream, c_szHrefTail);

            if (NULL != pDestinations->prgTarget[dwIndex])
            {
                FAIL_EXIT_STREAM_WRITE(stream, c_szDestHead);
                FAIL_EXIT_STREAM_WRITE(stream, pDestinations->prgTarget[dwIndex]);
                FAIL_EXIT_STREAM_WRITE(stream, c_szDestTail);
            }

            FAIL_EXIT_STREAM_WRITE(stream, c_szTargetTail);
        }
    }

    FAIL_EXIT_STREAM_WRITE(stream, c_szBatchTail);
    FAIL_EXIT_STREAM_WRITE(stream, pszRootName);
    FAIL_EXIT_STREAM_WRITE(stream, c_szXMLCloseElement);

    // take ownership of the byte stream
    hr = stream.HrAcquireStringA(pdwLen, (LPSTR *)ppvXML, ACQ_DISPLACE);

exit:
    return hr;
}

HRESULT HrCopyStringList(LPCSTR *rgszInList, LPCSTR **prgszOutList)
{

    DWORD   cStrings = 0;
    HRESULT hr = S_OK;
    LPCSTR  pszCur;
    DWORD   i = 0;

    IxpAssert(NULL != rgszInList);
    IxpAssert(NULL != prgszOutList);

    *prgszOutList = NULL;

    // count the strings in the list
    while (NULL != rgszInList[i++])
        ++cStrings;

    // allocate the new list
    if (!MemAlloc((void **)prgszOutList, (cStrings + 1) * sizeof(LPCSTR)))
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    // copy the strings over. if an allocation fails,
    // stay in the loop and null out all of the slots
    // that haven't been filled
    for (i = 0; i <= cStrings; i++)
    {
        if (SUCCEEDED(hr) && NULL != rgszInList[i])
        {
            (*prgszOutList)[i] = PszDupA(rgszInList[i]);
            if (NULL == (*prgszOutList)[i])
                hr = E_OUTOFMEMORY;
        }
        else
            (*prgszOutList)[i] = NULL;
    }
    
    if (FAILED(hr))
    {
        FreeStringList(*prgszOutList);
        *prgszOutList = NULL;
    }

exit:
    return hr;
}

void FreeStringList(LPCSTR *rgszList)
{
    DWORD i = 0;

    IxpAssert(NULL != rgszList);
    
    if (rgszList)
    {
        while (NULL != rgszList[i])
            MemFree((void *)rgszList[i++]);

        MemFree(rgszList);
    }
}

// --------------------------------------------------------------------------------
// class CHTTPMailTransport
// --------------------------------------------------------------------------------

// --------------------------------------------------------------------------------
// CHTTPMailTransport::CHTTPMailTransport
// --------------------------------------------------------------------------------
CHTTPMailTransport::CHTTPMailTransport(void) :
    m_cRef(1),
    m_fHasServer(FALSE),
    m_fHasRootProps(FALSE),
    m_fTerminating(FALSE),
    m_status(IXP_DISCONNECTED),
    m_hInternet(NULL),
    m_hConnection(NULL),
    m_pszUserAgent(NULL),
    m_pLogFile(NULL),
    m_pCallback(NULL),
    m_pParser(NULL),
    m_hwnd(NULL),
    m_hevPendingCommand(NULL),
    m_opPendingHead(NULL),
    m_opPendingTail(NULL),
    m_pszCurrentHost(NULL),
    m_nCurrentPort(INTERNET_INVALID_PORT_NUMBER)
{
    DWORD dwTempID;
    HANDLE hThread = NULL;

    InitializeCriticalSection(&m_cs);
    ZeroMemory(&m_rServer, sizeof(INETSERVER));
    ZeroMemory(&m_op, sizeof(HTTPMAILOPERATION));
    ZeroMemory(&m_rootProps, sizeof(ROOTPROPS));

    m_op.rResponse.command = HTTPMAIL_NONE;

    m_hevPendingCommand = CreateEvent(NULL, TRUE, FALSE, NULL);

    // Create the IO thread
    hThread = CreateThread(NULL, 0, IOThreadFuncProxy, (LPVOID)this, 0, &dwTempID);

    // We do not need to manipulate the IO Thread through its handle, so close it
    // This will NOT terminate the thread
    if (NULL != hThread)
    {
        CloseHandle(hThread);
    }

    DllAddRef();
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::~CHTTPMailTransport
// --------------------------------------------------------------------------------
CHTTPMailTransport::~CHTTPMailTransport(void)
{
    IxpAssert(0 == m_cRef);
    
    // Shouldn't be pending commands
    IxpAssert(HTTPMAIL_NONE == m_op.rResponse.command);
    IxpAssert(!m_opPendingHead);
    IxpAssert(!m_opPendingTail);

    IxpAssert(m_fTerminating);


    // Destroy the critical sections
    DeleteCriticalSection(&m_cs);

    // Close the window    
    if ((NULL != m_hwnd) && (FALSE != IsWindow(m_hwnd)))
        ::SendMessage(m_hwnd, WM_CLOSE, 0, 0);

    SafeMemFree(m_pszUserAgent);

    CloseHandle(m_hevPendingCommand);

    SafeMemFree(m_rootProps.pszAdbar);
    SafeMemFree(m_rootProps.pszContacts);
    SafeMemFree(m_rootProps.pszInbox);
    SafeMemFree(m_rootProps.pszOutbox);
    SafeMemFree(m_rootProps.pszSendMsg);
    SafeMemFree(m_rootProps.pszSentItems);
    SafeMemFree(m_rootProps.pszDeletedItems);
    SafeMemFree(m_rootProps.pszDrafts);
    SafeMemFree(m_rootProps.pszMsgFolderRoot);
    SafeMemFree(m_rootProps.pszSig);

    SafeMemFree(m_pszCurrentHost);

    SafeRelease(m_pLogFile);
    SafeRelease(m_pCallback);
    SafeRelease(m_pParser);

    SafeInternetCloseHandle(m_hInternet);

    // BUGBUG: clean up window, thread and event, release buffers, etc
    DllRelease();
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::HrConnectToHost
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::HrConnectToHost(
                                LPSTR pszHostName, 
                                INTERNET_PORT nPort,
                                LPSTR pszUserName,
                                LPSTR pszPassword)
{
    IxpAssert(m_hInternet);

    // if a connection exists, determine if it is to the same host/port
    // that the caller is specifying.
    if (NULL != m_hConnection)
    {
        // if we are already connected to the correct host, return immediately
        if (m_nCurrentPort == nPort && m_pszCurrentHost && (lstrcmp(pszHostName, m_pszCurrentHost) == 0))
            return S_OK;

        // if we are connected to the wrong server, close the existing connection
        SafeInternetCloseHandle(m_hConnection);
        SafeMemFree(m_pszCurrentHost);
        m_nCurrentPort = INTERNET_INVALID_PORT_NUMBER;
    }

    // establish a connection to the specified server
    m_hConnection = InternetConnect(
                        m_hInternet,
                        pszHostName,
                        nPort,
                        NULL,                           // user name
                        NULL,                           // password
                        INTERNET_SERVICE_HTTP,          // service
                        0,                              // flags
                        reinterpret_cast<DWORD_PTR>(this)); // context

    // what can cause this?
    if (NULL == m_hConnection)
        return E_OUTOFMEMORY;

    // save the host name. don't bother checking for failure...we just won't reuse
    // the connection next time through.
    m_pszCurrentHost = PszDupA(pszHostName);
    m_nCurrentPort = nPort;

    return S_OK;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::DoLogonPrompt
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::DoLogonPrompt(void)
{
    HRESULT             hr = S_OK;
    IHTTPMailCallback   *pCallback = NULL;

    EnterCriticalSection(&m_cs);

    if (m_pCallback)
    {
        pCallback = m_pCallback;
        pCallback->AddRef();
    }
    else
        hr = E_FAIL;

    LeaveCriticalSection(&m_cs);

    if (pCallback)
    {
        hr = pCallback->OnLogonPrompt(&m_rServer, this);
        pCallback->Release();
    }

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::DoGetParentWindow
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::DoGetParentWindow(HWND *phwndParent)
{
    HRESULT             hr = S_OK;
    IHTTPMailCallback   *pCallback = NULL;

    EnterCriticalSection(&m_cs);

    if (m_pCallback)
    {
        pCallback = m_pCallback;
        pCallback->AddRef();
    }
    else
        hr = E_FAIL;

    LeaveCriticalSection(&m_cs);

    if (pCallback)
    {
        hr = pCallback->GetParentWindow(phwndParent);
        pCallback->Release();
    }

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::CreateWnd
// --------------------------------------------------------------------------------
BOOL CHTTPMailTransport::CreateWnd()
{
    WNDCLASS wc;

    IxpAssert(!m_hwnd);
    if (m_hwnd)
        return TRUE;

    if (!GetClassInfo(g_hLocRes, s_szHTTPMailWndClass, &wc))
    {
        wc.style                = 0;
        wc.lpfnWndProc         = CHTTPMailTransport::WndProc;
        wc.cbClsExtra           = 0;
        wc.cbWndExtra           = 0;
        wc.hInstance            = g_hLocRes;
        wc.hIcon                = NULL;
        wc.hCursor              = NULL;
        wc.hbrBackground        = NULL;
        wc.lpszMenuName         = NULL;
        wc.lpszClassName        = s_szHTTPMailWndClass;
        RegisterClass(&wc);
    }

    m_hwnd = CreateWindowEx(0,
                        s_szHTTPMailWndClass,
                        s_szHTTPMailWndClass,
                        WS_POPUP,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        NULL,
                        NULL,
                        g_hLocRes,
                        (LPVOID)this);

    return (NULL != m_hwnd);
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::DequeueNextOperation
// --------------------------------------------------------------------------------
BOOL CHTTPMailTransport::DequeueNextOperation(void)
{
    if (NULL == m_opPendingHead)
        return FALSE;

    IxpAssert(HTTPMAIL_NONE == m_op.rResponse.command);
    IxpAssert(HTTPMAIL_NONE != m_opPendingHead->command);

    m_op.rResponse.command = m_opPendingHead->command;

    m_op.pfnState = m_opPendingHead->pfnState;
    m_op.cState = m_opPendingHead->cState;    
     
    m_op.pszUrl                = m_opPendingHead->pszUrl;
    m_op.pszDestination        = m_opPendingHead->pszDestination;
    m_op.pszContentType        = m_opPendingHead->pszContentType;
    m_op.pvData                = m_opPendingHead->pvData;
    m_op.cbDataLen             = m_opPendingHead->cbDataLen;
    m_op.dwContext             = m_opPendingHead->dwContext;
    m_op.dwDepth               = m_opPendingHead->dwDepth;
    m_op.dwRHFlags             = m_opPendingHead->dwRHFlags;
    m_op.dwMIFlags             = m_opPendingHead->dwMIFlags;
    m_op.tyProp                = m_opPendingHead->tyProp;
    m_op.fBatch                = m_opPendingHead->fBatch;
    m_op.rgszAcceptTypes       = m_opPendingHead->rgszAcceptTypes;
    m_op.pPropFindRequest      = m_opPendingHead->pPropFindRequest;
    m_op.pPropPatchRequest     = m_opPendingHead->pPropPatchRequest;
    m_op.pParseFuncs           = m_opPendingHead->pParseFuncs;
    m_op.pHeaderStream         = m_opPendingHead->pHeaderStream;
    m_op.pBodyStream           = m_opPendingHead->pBodyStream;
    m_op.pszFolderTimeStamp    = m_opPendingHead->pszFolderTimeStamp;
    m_op.pszRootTimeStamp      = m_opPendingHead->pszRootTimeStamp;
    //m_op.pszFolderName         = m_opPendingHead->pszFolderName;

    LPHTTPQUEUEDOP pDelete = m_opPendingHead;

    m_opPendingHead = m_opPendingHead->pNext;
    if (NULL == m_opPendingHead)
        m_opPendingTail = NULL;

    MemFree(pDelete);

    return TRUE;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::FlushQueue
// --------------------------------------------------------------------------------
void CHTTPMailTransport::FlushQueue(void)
{
    // destroy any commands that are pending.
    // REVIEW: these commands need to notify their callers
    LPHTTPQUEUEDOP pOp = m_opPendingHead;
    LPHTTPQUEUEDOP pNext;

    while (pOp)
    {
        pNext = pOp->pNext;

        SafeMemFree(pOp->pszUrl);
        SafeMemFree(pOp->pszDestination);
        if (pOp->pszContentType)
            MemFree((void *)pOp->pszContentType);
        SafeMemFree(pOp->pvData);
        if (pOp->rgszAcceptTypes)
            FreeStringList(pOp->rgszAcceptTypes);
        SafeRelease(pOp->pPropFindRequest);
        SafeRelease(pOp->pPropPatchRequest);

        MemFree(pOp);
        pOp = pNext;
    }

    m_opPendingHead = m_opPendingTail = NULL;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::TerminateIOThread
// --------------------------------------------------------------------------------
void CHTTPMailTransport::TerminateIOThread(void)
{
    EnterCriticalSection(&m_cs);
    // acquire a reference to the transport that will be owned
    // by the io thread. the reference will be release when the
    // io thread exits. this reference is not acquired when the
    // thread is created, because it would prevent the transport
    // from going away.
    AddRef();

    m_fTerminating = TRUE;

    FlushQueue();

    // signal the io thread to wake it.
    SetEvent(m_hevPendingCommand);

    LeaveCriticalSection(&m_cs);
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::IOThreadFunc
// --------------------------------------------------------------------------------

DWORD CALLBACK CHTTPMailTransport::IOThreadFuncProxy(PVOID pv)
{
    CHTTPMailTransport  *pHTTPMail = (CHTTPMailTransport*)pv;
    DWORD               dwResult = S_OK;

    IxpAssert(pHTTPMail);

    // Initialize COM
    if(S_OK == (dwResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
    {
        dwResult = pHTTPMail->IOThreadFunc();
        //Bug #101165 -- MSXML needs notification to clean up per thread data.
        CoUninitialize();
    }

    return dwResult;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::IOThreadFunc
// Called by IOThreadProxy to transition into an object method
// --------------------------------------------------------------------------------
DWORD CHTTPMailTransport::IOThreadFunc()
{
    LPSTR       pszVerb = NULL;
    BOOL        bQueueEmpty = FALSE;
    
    // block until a command is pending.
    while (WAIT_OBJECT_0 == WaitForSingleObject(m_hevPendingCommand, INFINITE))
    {
        if (IsTerminating())
            break;

        // Reset the event
        ResetEvent(m_hevPendingCommand);

        // unhook commands from the queue one at a time, and process them until
        // the queue is empty

        while (TRUE)
        {
            // dequeue the next operation

            EnterCriticalSection(&m_cs);
            
            IxpAssert(HTTPMAIL_NONE == m_op.rResponse.command);
            
            bQueueEmpty = !DequeueNextOperation();

            // if no commands left, break out of the loop and block
           
            LeaveCriticalSection(&m_cs);

            if (bQueueEmpty)
                break;
            
            DoOperation();
        }

        if (IsTerminating())
            break;
    }

    IxpAssert(IsTerminating());

    // TerminateIOThread acquired a reference that gets released here
    Release();

    return S_OK;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::WndProc
// --------------------------------------------------------------------------------

LRESULT CALLBACK CHTTPMailTransport::WndProc(HWND hwnd,
                                             UINT msg,
                                             WPARAM wParam,
                                             LPARAM lParam)
{
    CHTTPMailTransport *pThis = (CHTTPMailTransport*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    LRESULT             lr = 0;

    switch (msg)
    {
    case WM_NCCREATE:
        IxpAssert(!pThis);
        pThis = (CHTTPMailTransport*)((LPCREATESTRUCT)lParam)->lpCreateParams;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pThis);
        lr = DefWindowProc(hwnd, msg, wParam, lParam);       
        break;
    
    case SPM_HTTPMAIL_SENDRESPONSE:
        IxpAssert(pThis);
        pThis->InvokeResponseCallback();
        break;

    case SPM_HTTPMAIL_LOGONPROMPT:
        IxpAssert(pThis);
        lr = pThis->DoLogonPrompt();
        break;

    case SPM_HTTPMAIL_GETPARENTWINDOW:
        IxpAssert(pThis);
        lr = pThis->DoGetParentWindow((HWND *)wParam);
        break;
        
    default:
        lr = DefWindowProc(hwnd, msg, wParam, lParam);
        break;
    }

    return lr;
}


// --------------------------------------------------------------------------------
// CHTTPMailTransport::Reset
// --------------------------------------------------------------------------------
void CHTTPMailTransport::Reset(void)
{
    // REVIEW: this is incomplete. Should we be aborting the current command?
    EnterCriticalSection(&m_cs);

    SafeRelease(m_pLogFile);
    
    SafeInternetCloseHandle(m_hConnection);
    SafeInternetCloseHandle(m_hInternet);

    SafeMemFree(m_pszUserAgent);

    SafeRelease(m_pCallback);
    m_status = IXP_DISCONNECTED;
    m_fHasServer = FALSE;
    ZeroMemory(&m_rServer, sizeof(INETSERVER));
    LeaveCriticalSection(&m_cs);
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::QueryInterface
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::QueryInterface(REFIID riid, LPVOID *ppv)
{
    // Locals
    HRESULT hr = S_OK;

    // Validate params
    if (NULL == ppv)
    {
        hr = TrapError(E_INVALIDARG);
        goto exit;
    }

    // Initialize params
    *ppv = NULL;

    // IID_IUnknown
    if (IID_IUnknown == riid)
        *ppv = ((IUnknown *)(IHTTPMailTransport *)this);

    // IID_IInternetTransport 
    else if (IID_IInternetTransport == riid)
        *ppv = (IInternetTransport *)this;

    // IID_IHTTPMailTransport
    else if (IID_IHTTPMailTransport == riid)
        *ppv = (IHTTPMailTransport *)this;

    // IID_IXMLNodeFactory
    else if (IID_IXMLNodeFactory == riid)
        *ppv = (IXMLNodeFactory *)this;

    else if (IID_IHTTPMailTransport2 == riid)
        *ppv = (IHTTPMailTransport2 *)this;

    // if not NULL, acquire a reference and return
    if (NULL != *ppv)
    {
        ((LPUNKNOWN)*ppv)->AddRef();
        goto exit;
    }

    hr = TrapError(E_NOINTERFACE);

exit:
    // Done
    return hr;
}
// --------------------------------------------------------------------------------
// CHTTPMailTransport::AddRef
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CHTTPMailTransport::AddRef(void) 
{
    return ++m_cRef;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::Release
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CHTTPMailTransport::Release(void) 
{
    if (0 != --m_cRef)
        return m_cRef;

    // our refcount dropped to zero, and we aren't terminating,
    // begin terminating
    if (!IsTerminating())
    {
        TerminateIOThread();
        return 1;
    }
    
    // if we were terminating, and our refCount dropped to zero,
    // then the iothread has been unwound and we can safely exit.
    delete this;
    return 0;
}

// ----------------------------------------------------------------------------
// IInternetTransport methods
// ----------------------------------------------------------------------------

// --------------------------------------------------------------------------------
// CHTTPMailTransport::Connect
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::Connect(LPINETSERVER pInetServer, 
                                        boolean fAuthenticate, 
                                        boolean fCommandLogging)
{
    HRESULT                     hr = S_OK; 

    if (NULL == pInetServer  || FIsEmptyA(pInetServer->szServerName))
        return TrapError(E_INVALIDARG);

    // Thread safety
    EnterCriticalSection(&m_cs);

    // not init
    if (NULL == m_hInternet || NULL == m_pCallback)
    {
        hr = TrapError(IXP_E_NOT_INIT);
        goto exit;
    }

    FAIL_CREATEWND;

    // busy
    if (IXP_DISCONNECTED != m_status || m_fHasServer)
    {
        hr = TrapError(IXP_E_ALREADY_CONNECTED);
        goto exit;
    }

    // copy the server struct
    CopyMemory(&m_rServer, pInetServer, sizeof(INETSERVER));
    m_fHasServer = TRUE;
    m_fHasRootProps = FALSE;
    
exit:
    // ThreadSafety
    LeaveCriticalSection(&m_cs);
    
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::DropConnection
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::DropConnection(void)
{
    // this function is called to stop any current and pending i/o

    // Locals
    HRESULT     hr = S_OK;
    BOOL        fSendResponse;

    EnterCriticalSection(&m_cs);
    
    // flush any pending i/o from the queue
    FlushQueue();
    
    // if a command is being processed, mark it aborted and
    // send a response if necessary. stay in the critical
    // section to prevent the io thread from sending any
    // notifications at the same time.
    if (m_op.rResponse.command != HTTPMAIL_NONE)
    {
        m_op.fAborted = TRUE;
        m_op.rResponse.fDone = TRUE; 
    }

    Disconnect();

    LeaveCriticalSection(&m_cs);

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::Disconnect
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::Disconnect(void)
{
    // Locals
    HRESULT     hr = S_OK;

    // Thread safety
    EnterCriticalSection(&m_cs);

    if (NULL == m_hConnection)
    {
        hr = TrapError(IXP_E_NOT_INIT);
        goto exit;
    }

    // Disconnecting
    if (m_pCallback)
        m_pCallback->OnStatus(IXP_DISCONNECTING, this);

    SafeInternetCloseHandle(m_hConnection);

    m_status = IXP_DISCONNECTED;
    ZeroMemory(&m_rServer, sizeof(INETSERVER));

    if (m_pCallback)
        m_pCallback->OnStatus(IXP_DISCONNECTED, this);

     // Close the window    
    if ((NULL != m_hwnd) && (FALSE != IsWindow(m_hwnd)))
        ::SendMessage(m_hwnd, WM_CLOSE, 0, 0);
    m_hwnd = NULL;
    m_fHasServer = FALSE;

exit:
    // Thread safety
    LeaveCriticalSection(&m_cs);

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::IsState
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::IsState(IXPISSTATE isstate)
{
    // Locals
    HRESULT     hr = S_OK;

    // Thread safety
    EnterCriticalSection(&m_cs);

    switch(isstate)
    {
        // are we connected?
        case IXP_IS_CONNECTED:
            hr = (NULL == m_hConnection) ? S_FALSE : S_OK;
            break;

        // are we busy?
        case IXP_IS_BUSY:
            hr = (HTTPMAIL_NONE != m_op.rResponse.command) ? S_OK : S_FALSE;
            break;

        // are we ready
        case IXP_IS_READY:
            hr = (HTTPMAIL_NONE == m_op.rResponse.command) ? S_OK : S_FALSE;
            break;

        case IXP_IS_AUTHENTICATED:
            // REVIEW
            hr = S_OK;
            break;

        default:
            IxpAssert(FALSE);
            break;
    }

    // Thread safety
    LeaveCriticalSection(&m_cs);

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::GetServerInfo
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::GetServerInfo(LPINETSERVER pInetServer)
{
    // check params
    if (NULL == pInetServer)
        return TrapError(E_INVALIDARG);

    // Thread safety
    EnterCriticalSection(&m_cs);

    // Copy server info
    CopyMemory(pInetServer, &m_rServer, sizeof(INETSERVER));

    // Thread safety
    LeaveCriticalSection(&m_cs);

    // Done
    return S_OK;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::GetIXPType
// --------------------------------------------------------------------------------
STDMETHODIMP_(IXPTYPE) CHTTPMailTransport::GetIXPType(void)
{
    return IXP_HTTPMail;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::InetServerFromAccount
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::InetServerFromAccount(IImnAccount *pAccount, LPINETSERVER pInetServer)
{
    HRESULT     hr = S_OK;
    DWORD       fAlwaysPromptPassword = FALSE;

    // check params
    if (NULL == pAccount || NULL == pInetServer)
        return TrapError(E_INVALIDARG);

    // ZeroInit
    ZeroMemory(pInetServer, sizeof(INETSERVER));

    // Get the account name
    if (FAILED(hr = pAccount->GetPropSz(AP_ACCOUNT_NAME, pInetServer->szAccount, ARRAYSIZE(pInetServer->szAccount))))
    {
        hr = TrapError(IXP_E_INVALID_ACCOUNT);
        goto exit;
    }

    // Get the RAS connectoid
    if (FAILED(pAccount->GetPropSz(AP_RAS_CONNECTOID, pInetServer->szConnectoid, ARRAYSIZE(pInetServer->szConnectoid))))
        *pInetServer->szConnectoid = '\0';

    // Connection Type
    Assert(sizeof(pInetServer->rasconntype) == sizeof(DWORD));
    if (FAILED(pAccount->GetPropDw(AP_RAS_CONNECTION_TYPE, (DWORD *)&pInetServer->rasconntype)))
        pInetServer->rasconntype = RAS_CONNECT_LAN;

           // Get Server Name
    hr = pAccount->GetPropSz(AP_HTTPMAIL_SERVER, pInetServer->szServerName, sizeof(pInetServer->szServerName));
    if (FAILED(hr))
    {
        hr = TrapError(IXP_E_INVALID_ACCOUNT);
        goto exit;
    }

    // Password
    if (FAILED(pAccount->GetPropDw(AP_HTTPMAIL_PROMPT_PASSWORD, &fAlwaysPromptPassword)) || FALSE == fAlwaysPromptPassword)
        pAccount->GetPropSz(AP_HTTPMAIL_PASSWORD, pInetServer->szPassword, sizeof(pInetServer->szPassword));

    if (fAlwaysPromptPassword)
        pInetServer->dwFlags |= ISF_ALWAYSPROMPTFORPASSWORD;

    // Sicily
    Assert(sizeof(pInetServer->fTrySicily) == sizeof(DWORD));
    pAccount->GetPropDw(AP_HTTPMAIL_USE_SICILY, (DWORD *)&pInetServer->fTrySicily);


    // User Name
    pAccount->GetPropSz(AP_HTTPMAIL_USERNAME, pInetServer->szUserName, sizeof(pInetServer->szUserName));

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::HandsOffCallback
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::HandsOffCallback(void)
{
    // Locals
    HRESULT hr = S_OK;

    // Thread safety
    EnterCriticalSection(&m_cs);

    // No current callback
    if (NULL == m_pCallback)
    {
        hr = TrapError(S_FALSE);
        goto exit;
    }

    // Release it
    SafeRelease(m_pCallback);

exit:
    // Thread safety
    LeaveCriticalSection(&m_cs);

    // Done
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::GetStatus
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::GetStatus(IXPSTATUS *pCurrentStatus)
{
    if (NULL == pCurrentStatus)
        return TrapError(E_INVALIDARG);

    *pCurrentStatus = m_status;
    return S_OK;
}

// ----------------------------------------------------------------------------
// IHTTPMailTransport methods
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
// CHTTPMailTransport::GetProperty
// ----------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::GetProperty(
                                        HTTPMAILPROPTYPE type, 
                                        LPSTR *ppszProp)
{
    HRESULT         hr = S_OK;

    if (type <= HTTPMAIL_PROP_INVALID || type >= HTTPMAIL_PROP_LAST)
        return E_INVALIDARG;

    if (ppszProp)
        *ppszProp = NULL;

    if (!m_fHasRootProps || NULL == ppszProp)
    {
        IF_FAILEXIT(hr = QueueGetPropOperation(type));
    }

    switch (type)
    {
    case HTTPMAIL_PROP_ADBAR:
        *ppszProp = PszDupA(m_rootProps.pszAdbar);
        break;

    case HTTPMAIL_PROP_CONTACTS:
        *ppszProp = PszDupA(m_rootProps.pszContacts);
        break;
        
    case HTTPMAIL_PROP_INBOX:
        *ppszProp = PszDupA(m_rootProps.pszInbox);
        break;

    case HTTPMAIL_PROP_OUTBOX:
        *ppszProp = PszDupA(m_rootProps.pszOutbox);
        break;

    case HTTPMAIL_PROP_SENDMSG:
        *ppszProp = PszDupA(m_rootProps.pszSendMsg);
        break;

    case HTTPMAIL_PROP_SENTITEMS:
        *ppszProp = PszDupA(m_rootProps.pszSentItems);
        break;

    case HTTPMAIL_PROP_DELETEDITEMS:
        *ppszProp = PszDupA(m_rootProps.pszDeletedItems);
        break;
    
    case HTTPMAIL_PROP_DRAFTS:
        *ppszProp = PszDupA(m_rootProps.pszDrafts);
        break;
    
    case HTTPMAIL_PROP_MSGFOLDERROOT:
        *ppszProp = PszDupA(m_rootProps.pszMsgFolderRoot);
        break;

    case HTTPMAIL_PROP_SIG:
        *ppszProp = PszDupA(m_rootProps.pszSig);
        break;

    default:
        hr = TrapError(E_INVALIDARG);
        break;
    }

    if (SUCCEEDED(hr) && !*ppszProp)
        hr = IXP_E_HTTP_ROOT_PROP_NOT_FOUND;

exit:
    return hr;
}

HRESULT CHTTPMailTransport::QueueGetPropOperation(HTTPMAILPROPTYPE type)
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp;

    if (!m_fHasServer || NULL == m_rServer.szServerName)
    {
        hr = E_FAIL;
        goto exit;
    }

    FAIL_CREATEWND;

    // queue the getprop operation
    if (FAILED(hr = AllocQueuedOperation(m_rServer.szServerName, NULL, 0, &pOp)))
        goto exit;

    pOp->command = HTTPMAIL_GETPROP;
    pOp->tyProp = type;

    pOp->pfnState = c_rgpfnRootProps;
    pOp->cState = ARRAYSIZE(c_rgpfnRootProps);
    pOp->pParseFuncs = c_rgpfnRootPropsParse;
    pOp->dwRHFlags = (RH_XMLCONTENTTYPE | RH_BRIEF);

    QueueOperation(pOp);

    hr = E_PENDING;

exit:
    return hr;
}

// ----------------------------------------------------------------------------
// CHTTPMailTransport::GetPropertyDw
// ----------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::GetPropertyDw(
                                        HTTPMAILPROPTYPE type, 
                                        LPDWORD          lpdwProp)
{
    HRESULT         hr = S_OK;

    if (type <= HTTPMAIL_PROP_INVALID || type >= HTTPMAIL_PROP_LAST)
        IF_FAILEXIT(hr = E_INVALIDARG);

    if (lpdwProp)
        *lpdwProp = 0;

    if (!m_fHasRootProps || NULL == lpdwProp)
    {
        IF_FAILEXIT(hr = QueueGetPropOperation(type));
        
    }

    switch (type)
    {
        case HTTPMAIL_PROP_MAXPOLLINGINTERVAL:
            *lpdwProp = m_rootProps.dwMaxPollingInterval;
            break;

        default:
            hr = TrapError(E_INVALIDARG);
            break;
    }

exit:
    return hr;
}

// ----------------------------------------------------------------------------
// CHTTPMailTransport::CommandGET
// ----------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::CommandGET(LPCSTR pszUrl, 
                                            LPCSTR *rgszAcceptTypes,
                                            BOOL fTranslate,
                                            DWORD dwContext)
{
    HRESULT         hr = S_OK;
    LPHTTPQUEUEDOP  pOp = NULL;
    LPCSTR          *rgszAcceptTypesCopy = NULL;

    if (NULL == pszUrl)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    if (NULL != rgszAcceptTypes)
    {
        hr = HrCopyStringList(rgszAcceptTypes, &rgszAcceptTypesCopy);
        if (FAILED(hr))
            goto exit;
    }

    if (FAILED(hr = AllocQueuedOperation(pszUrl, NULL, 0, &pOp)))
        goto exit;

    pOp->command = HTTPMAIL_GET;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfGet;
    pOp->cState = ARRAYSIZE(c_rgpfGet);
    pOp->rgszAcceptTypes = rgszAcceptTypesCopy;
    if (!fTranslate)
        pOp->dwRHFlags = RH_TRANSLATEFALSE;

    rgszAcceptTypesCopy = NULL;

    QueueOperation(pOp);

exit:        
    if (NULL != rgszAcceptTypesCopy)
        FreeStringList(rgszAcceptTypesCopy);

    return hr;
}

// ----------------------------------------------------------------------------
// CHTTPMailTransport::CommandPUT
// ----------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::CommandPUT(
                                        LPCSTR pszPath, 
                                        LPVOID lpvData,
                                        ULONG cbSize,
                                        DWORD dwContext)
{
    HRESULT         hr = S_OK;
    LPHTTPQUEUEDOP  pOp = NULL;
    LPCSTR          pszLocalContentType = NULL;
    LPVOID          lpvCopy = NULL;

    if (NULL == pszPath || NULL == lpvData || 0 == cbSize)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    if (!MemAlloc(&lpvCopy, cbSize))
    {
        hr = TrapError(E_OUTOFMEMORY);
        goto exit;
    }
    
    CopyMemory(lpvCopy, lpvData, cbSize);

    if (FAILED(hr = AllocQueuedOperation(pszPath, NULL, 0, &pOp)))
        goto exit;

    pOp->command = HTTPMAIL_PUT;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnPut;
    pOp->cState = ARRAYSIZE(c_rgpfnPut);
    pOp->pvData = lpvCopy;
    lpvCopy = NULL;
    pOp->cbDataLen = cbSize;

    QueueOperation(pOp);

exit:
    SafeMemFree(lpvCopy);
    return hr;
}
                                    
// ----------------------------------------------------------------------------
// CHTTPMailTransport::CommandPOST
// ----------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::CommandPOST(
                                    LPCSTR pszPath,
                                    IStream *pStream,
                                    LPCSTR pszContentType,
                                    DWORD dwContext)
{
    HRESULT         hr = S_OK;
    LPHTTPQUEUEDOP  pOp = NULL;
    LPCSTR          pszLocalContentType = NULL;

    if (NULL == pszPath || NULL == pStream)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    if (pszContentType)
    {
        pszLocalContentType = PszDupA(pszContentType);
        if (NULL == pszLocalContentType)
        {
            hr = TrapError(E_OUTOFMEMORY);
            goto exit;
        }
    }

    if (FAILED(hr = AllocQueuedOperation(pszPath, NULL, 0, &pOp)))
        goto exit;

    pOp->command = HTTPMAIL_POST;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnPost;
    pOp->cState = ARRAYSIZE(c_rgpfnPost);
    pOp->pBodyStream = pStream;
    pOp->pBodyStream->AddRef();
    pOp->pszContentType = pszLocalContentType;
    pszLocalContentType = NULL;

    QueueOperation(pOp);

exit:
    if (pszLocalContentType)
        MemFree((void *)pszLocalContentType);

    return hr;
}

// ----------------------------------------------------------------------------
// CHTTPMailTransport::CommandDELETE
// ----------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::CommandDELETE(
                                    LPCSTR pszPath,
                                    DWORD dwContext)
{    
    HRESULT         hr = S_OK;
    LPHTTPQUEUEDOP  pOp = NULL;

    if (NULL == pszPath)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    if (FAILED(hr = AllocQueuedOperation(pszPath, NULL, 0, &pOp)))
        goto exit;
    
    pOp->command = HTTPMAIL_DELETE;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfDelete;
    pOp->cState = ARRAYSIZE(c_rgpfDelete);

    QueueOperation(pOp);

exit:
    return hr;
}

// ----------------------------------------------------------------------------
// CHTTPMailTransport::CommandBDELETE
// ----------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::CommandBDELETE(
                                    LPCSTR pszPath,
                                    LPHTTPTARGETLIST pBatchTargets,
                                    DWORD dwContext)
{    
    HRESULT         hr = S_OK;
    LPHTTPQUEUEDOP  pOp = NULL;
    LPVOID          pvXML = NULL;
    DWORD           dwXMLLen = 0;

    if (NULL == pszPath || NULL == pBatchTargets)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    if (FAILED(hr = HrGenerateSimpleBatchXML(c_szDelete, pBatchTargets, &pvXML, &dwXMLLen)))
        goto exit;

    if (FAILED(hr = AllocQueuedOperation(pszPath, pvXML, dwXMLLen, &pOp, TRUE)))
        goto exit;
    
    pvXML = NULL;

    pOp->command = HTTPMAIL_BDELETE;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfDelete;
    pOp->cState = ARRAYSIZE(c_rgpfDelete);
    pOp->dwRHFlags = RH_XMLCONTENTTYPE;

    QueueOperation(pOp);

exit:
    SafeMemFree(pvXML);

    return hr;
}
// ----------------------------------------------------------------------------
// CHTTPMailTransport::CommandPROPFIND
// ----------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::CommandPROPFIND(
                                    LPCSTR pszPath,
                                    IPropFindRequest *pRequest,
                                    DWORD dwDepth,
                                    DWORD dwContext)
{
    if (NULL == pszPath || NULL == pRequest)
        return TrapError(E_INVALIDARG);

    return E_NOTIMPL;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::CommandPROPPATCH
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::CommandPROPPATCH(
                                        LPCSTR pszUrl, 
                                        IPropPatchRequest *pRequest, 
                                        DWORD dwContext)
{
    if (NULL == pszUrl || NULL == pRequest)
        return TrapError(E_INVALIDARG);

    HRESULT         hr = S_OK;
    LPHTTPQUEUEDOP  pOp = NULL;

    FAIL_CREATEWND;

    if (FAILED(hr = AllocQueuedOperation(pszUrl, NULL, 0, &pOp)))
        goto exit;

    pOp->command = HTTPMAIL_PROPPATCH;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnPropPatch;
    pOp->cState = ARRAYSIZE(c_rgpfnPropPatch);
    pOp->pPropPatchRequest = pRequest;
    pRequest->AddRef();
    pOp->dwRHFlags = RH_XMLCONTENTTYPE;

    QueueOperation(pOp);

exit:
    return hr;
}
    
// --------------------------------------------------------------------------------
// CHTTPMailTransport::CommandMKCOL
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::CommandMKCOL(LPCSTR pszUrl, DWORD dwContext)
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp = NULL;

    if (NULL == pszUrl)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    if (FAILED(hr = AllocQueuedOperation(pszUrl, NULL, 0, &pOp)))
        goto exit;

    pOp->command   = HTTPMAIL_MKCOL;
    pOp->dwContext = dwContext;
    pOp->pfnState  = c_rgpfnMkCol;
    pOp->cState    = ARRAYSIZE(c_rgpfnMkCol);
    QueueOperation(pOp);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::CommandCOPY
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::CommandCOPY(
                                    LPCSTR pszPath, 
                                    LPCSTR pszDestination, 
                                    BOOL fAllowRename,
                                    DWORD dwContext)
{
    HRESULT         hr = S_OK;
    LPHTTPQUEUEDOP  pOp;
    LPSTR           pszDupDestination = NULL;

    if (NULL == pszPath || NULL == pszDestination)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    pszDupDestination = PszDupA(pszDestination);
    if (NULL == pszDupDestination)
        return TrapError(E_OUTOFMEMORY);

    if (FAILED(hr = AllocQueuedOperation(pszPath, NULL, 0, &pOp)))
        goto exit;

    pOp->command = HTTPMAIL_COPY;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnCopy;
    pOp->cState = ARRAYSIZE(c_rgpfnCopy);

    pOp->pszDestination = pszDupDestination;
    pszDupDestination = NULL;

    if (fAllowRename)
        pOp->dwRHFlags = RH_ALLOWRENAME;

    QueueOperation(pOp);

exit:
    SafeMemFree(pszDupDestination);

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::CommandBCOPY
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::CommandBCOPY(
                                    LPCSTR pszSourceCollection, 
                                    LPHTTPTARGETLIST pTargets, 
                                    LPCSTR pszDestCollection, 
                                    LPHTTPTARGETLIST pDestinations,
                                    BOOL fAllowRename,
                                    DWORD dwContext)
{
    HRESULT         hr = S_OK;
    LPHTTPQUEUEDOP  pOp = NULL;
    LPVOID          pvXML = NULL;
    DWORD           dwXMLLen = 0;
    LPSTR           pszDupDestination = NULL;

    if (NULL == pszSourceCollection || NULL == pTargets || NULL == pszDestCollection)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    pszDupDestination = PszDupA(pszDestCollection);
    if (NULL == pszDupDestination)
    {
        hr = TrapError(E_OUTOFMEMORY);
        goto exit;
    }

    if (NULL == pDestinations)
        hr = HrGenerateSimpleBatchXML(c_szCopy, pTargets, &pvXML, &dwXMLLen);
    else
        hr = HrGenerateMultiDestBatchXML(c_szCopy, pTargets, pDestinations, &pvXML, &dwXMLLen);

    if (FAILED(hr))
        goto exit;

    if (FAILED(hr = AllocQueuedOperation(pszSourceCollection, pvXML, dwXMLLen, &pOp, TRUE)))
        goto exit;

    pvXML = NULL;

    pOp->command = HTTPMAIL_BCOPY;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnBMove;
    pOp->cState = ARRAYSIZE(c_rgpfnBMove);
    pOp->pParseFuncs = c_rgpfnBCopyMoveParse;

    pOp->pszDestination = pszDupDestination;
    pszDupDestination = NULL;
    
    pOp->dwRHFlags = RH_XMLCONTENTTYPE;
    if (fAllowRename)
        pOp->dwRHFlags |= RH_ALLOWRENAME;

    QueueOperation(pOp);

exit:
    SafeMemFree(pvXML);
    SafeMemFree(pszDupDestination);
    return hr;
}


// --------------------------------------------------------------------------------
// CHTTPMailTransport::CommandMOVE
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::CommandMOVE(
                                    LPCSTR pszPath, 
                                    LPCSTR pszDestination,
                                    BOOL fAllowRename,
                                    DWORD dwContext)
{
    HRESULT         hr = S_OK;
    LPHTTPQUEUEDOP  pOp;
    LPSTR           pszDupDestination = NULL;

    if (NULL == pszPath || NULL == pszDestination)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    pszDupDestination = PszDupA(pszDestination);
    if (NULL == pszDupDestination)
        return TrapError(E_OUTOFMEMORY);

    if (FAILED(hr = AllocQueuedOperation(pszPath, NULL, 0, &pOp)))
        goto exit;

    pOp->command = HTTPMAIL_MOVE;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnMove;
    pOp->cState = ARRAYSIZE(c_rgpfnMove);

    pOp->pszDestination = pszDupDestination;
    pszDupDestination = NULL;
    
    if (fAllowRename)
        pOp->dwRHFlags = RH_ALLOWRENAME;
    
    QueueOperation(pOp);

exit:
    SafeMemFree(pszDupDestination);

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::CommandBMOVE
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::CommandBMOVE(
                                    LPCSTR pszSourceCollection, 
                                    LPHTTPTARGETLIST pTargets, 
                                    LPCSTR pszDestCollection, 
                                    LPHTTPTARGETLIST pDestinations,
                                    BOOL fAllowRename,
                                    DWORD dwContext)
{
    HRESULT         hr = S_OK;
    LPHTTPQUEUEDOP  pOp = NULL;
    LPVOID          pvXML = NULL;
    DWORD           dwXMLLen = 0;
    LPSTR           pszDupDestination = NULL;

    if (NULL == pszSourceCollection || NULL == pTargets || NULL == pszDestCollection)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    pszDupDestination = PszDupA(pszDestCollection);
    if (NULL == pszDupDestination)
    {
        hr = TrapError(E_OUTOFMEMORY);
        goto exit;
    }

    if (NULL == pDestinations)
        hr = HrGenerateSimpleBatchXML(c_szMove, pTargets, &pvXML, &dwXMLLen);
    else
        hr = HrGenerateMultiDestBatchXML(c_szMove, pTargets, pDestinations, &pvXML, &dwXMLLen);

    if (FAILED(hr))
        goto exit;

    if (FAILED(hr = AllocQueuedOperation(pszSourceCollection, pvXML, dwXMLLen, &pOp, TRUE)))
        goto exit;

    pvXML = NULL;

    pOp->command = HTTPMAIL_BMOVE;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnBMove;
    pOp->cState = ARRAYSIZE(c_rgpfnBMove);
    pOp->pParseFuncs = c_rgpfnBCopyMoveParse;

    pOp->pszDestination = pszDupDestination;
    pszDupDestination = NULL;

    pOp->dwRHFlags = RH_XMLCONTENTTYPE;
    if (fAllowRename)
        pOp->dwRHFlags |= RH_ALLOWRENAME;

    QueueOperation(pOp);

exit:
    SafeMemFree(pvXML);
    SafeMemFree(pszDupDestination);
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::MemberInfo
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::MemberInfo(
                                        LPCSTR pszPath, 
                                        MEMBERINFOFLAGS flags, 
                                        DWORD dwDepth,
                                        BOOL fIncludeRoot,
                                        DWORD dwContext)
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp = NULL;

    if (NULL == pszPath)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    if (FAILED(hr = AllocQueuedOperation(pszPath, NULL, 0, &pOp)))
        goto exit;

    pOp->command = HTTPMAIL_MEMBERINFO;
    pOp->dwMIFlags = flags;
    pOp->dwDepth = dwDepth;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnMemberInfo;
    pOp->cState = ARRAYSIZE(c_rgpfnMemberInfo);
    pOp->pParseFuncs = c_rgpfnMemberInfoParse;

    pOp->dwRHFlags = (RH_BRIEF | RH_XMLCONTENTTYPE);
    if (!fIncludeRoot)
        pOp->dwRHFlags |= RH_NOROOT;

    QueueOperation(pOp);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::FindFolders
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::FindFolders(LPCSTR pszPath, DWORD dwContext)
{
    return E_NOTIMPL;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::MarkRead
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::MarkRead(
                                LPCSTR                  pszPath,
                                LPHTTPTARGETLIST        pTargets,
                                BOOL                    fMarkRead,
                                DWORD                   dwContext)
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp = NULL;
    CPropPatchRequest   *pRequest = NULL;
    LPSTR               pszXML = NULL;

    if (NULL == pszPath)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    pRequest = new CPropPatchRequest();
    if (NULL == pRequest)
    {
        hr = TrapError(E_OUTOFMEMORY);
        goto exit;
    }
    
    if (fMarkRead)
        FAIL_EXIT(hr = pRequest->SetProperty(DAVNAMESPACE_HTTPMAIL, "read", "1"));
    else
        FAIL_EXIT(hr = pRequest->SetProperty(DAVNAMESPACE_HTTPMAIL, "read", "0"));
    
    FAIL_EXIT(hr = pRequest->GenerateXML(pTargets, &pszXML));

    FAIL_EXIT(hr = AllocQueuedOperation(pszPath, pszXML, lstrlen(pszXML), &pOp, TRUE));
    pszXML = NULL;
    
    pOp->command = HTTPMAIL_MARKREAD;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnMarkRead;
    pOp->cState = ARRAYSIZE(c_rgpfnMarkRead);
    pOp->pParseFuncs = c_rgpfnMemberErrorParse;

    pOp->dwRHFlags = (RH_BRIEF | RH_XMLCONTENTTYPE);
    if (pTargets && pTargets->cTarget > 0)
        pOp->fBatch = TRUE;

    QueueOperation(pOp);

exit:
    SafeRelease(pRequest);
    SafeMemFree(pszXML);
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::SendMessage
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::SendMessage(LPCSTR pszPath, 
                                        LPCSTR pszFrom, 
                                        LPHTTPTARGETLIST pTargets, 
                                        BOOL fSaveInSent, 
                                        IStream *pMessageStream, 
                                        DWORD dwContext)
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp = NULL;
    IStream             *pRfc821Stream = NULL;

    if (NULL == pszPath || 
            NULL == pszFrom || 
            NULL == pTargets || pTargets->cTarget < 1 ||
            NULL == pMessageStream)
        return E_INVALIDARG;

    FAIL_CREATEWND;

    // build the rfc821 stream that will precede the mime message
    FAIL_EXIT(hr = _HrGenerateRfc821Stream(pszFrom, pTargets, &pRfc821Stream));

    FAIL_EXIT(hr = AllocQueuedOperation(pszPath, NULL, 0, &pOp));

    pOp->command = HTTPMAIL_SENDMESSAGE;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnSendMessage;
    pOp->cState = ARRAYSIZE(c_rgpfnSendMessage);
    pOp->pHeaderStream = pRfc821Stream;
    pRfc821Stream = NULL;
    pOp->pBodyStream = pMessageStream;
    if (NULL != pOp->pBodyStream)
        pOp->pBodyStream->AddRef();
    pOp->dwRHFlags = (RH_TRANSLATETRUE | RH_SMTPMESSAGECONTENTTYPE);
    pOp->dwRHFlags |= (fSaveInSent ? RH_SAVEINSENTTRUE : RH_SAVEINSENTFALSE);

    QueueOperation(pOp);

exit:
    SafeRelease(pRfc821Stream);

    return hr;

}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ListContacts
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ListContacts(LPCSTR pszPath, DWORD dwContext)
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp = NULL;

    if (NULL == pszPath)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    if (FAILED(hr = AllocQueuedOperation(pszPath, NULL, 0, &pOp)))
        goto exit;

    pOp->command = HTTPMAIL_LISTCONTACTS;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnListContacts;
    pOp->cState = ARRAYSIZE(c_rgpfnListContacts);
    pOp->pParseFuncs = c_rgpfnListContactsParse;

    pOp->dwDepth = 1;
    pOp->dwRHFlags = (RH_NOROOT | RH_XMLCONTENTTYPE);

    QueueOperation(pOp);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ListContactInfos
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ListContactInfos(LPCSTR pszCollectionPath, DWORD dwContext)
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp = NULL;

    if (NULL == pszCollectionPath)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    if (FAILED(hr = AllocQueuedOperation(pszCollectionPath, NULL, 0, &pOp)))
        goto exit;

    pOp->command = HTTPMAIL_CONTACTINFO;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnContactInfo;
    pOp->cState = ARRAYSIZE(c_rgpfnContactInfo);
    pOp->pParseFuncs = c_rgpfnContactInfoParse;

    pOp->dwDepth = 1;
    pOp->dwRHFlags = (RH_NOROOT | RH_XMLCONTENTTYPE);

    QueueOperation(pOp);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ContactInfo
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ContactInfo(LPCSTR pszPath, DWORD dwContext)
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp = NULL;

    if (NULL == pszPath)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    if (FAILED(hr = AllocQueuedOperation(pszPath, NULL, 0, &pOp)))
        goto exit;

    pOp->command = HTTPMAIL_CONTACTINFO;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnContactInfo;
    pOp->cState = ARRAYSIZE(c_rgpfnContactInfo);
    pOp->pParseFuncs = c_rgpfnContactInfoParse;

    pOp->dwDepth = 0;
    pOp->dwRHFlags = RH_XMLCONTENTTYPE;

    QueueOperation(pOp);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::PostContact
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::PostContact(LPCSTR pszPath, 
                                        LPHTTPCONTACTINFO pciInfo, 
                                        DWORD dwContext)
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp = NULL;
    LPVOID              pvXML = NULL;
    DWORD               cb;

    if (NULL == pciInfo)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    if (FAILED(hr = HrGeneratePostContactXML(pciInfo, &pvXML, &cb)))
        goto exit;

    if (FAILED(hr = AllocQueuedOperation(pszPath, NULL, 0, &pOp)))
        goto exit;

    pOp->pvData = pvXML;
    pOp->cbDataLen = cb;
    pvXML = NULL;

    pOp->command = HTTPMAIL_POSTCONTACT;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnPostContact;
    pOp->cState = ARRAYSIZE(c_rgpfnPostContact);

    pOp->dwDepth = 0;
    pOp->dwRHFlags = (RH_XMLCONTENTTYPE | RH_TRANSLATEFALSE);

    QueueOperation(pOp);

exit:
    SafeMemFree(pvXML);

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::PatchContact
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::PatchContact(LPCSTR pszPath, 
                                         LPHTTPCONTACTINFO pciInfo, 
                                         DWORD dwContext)
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp = NULL;
    IPropPatchRequest   *pRequest = NULL;

    if (NULL == pciInfo)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

    if (FAILED(hr = HrCreatePatchContactRequest(pciInfo, &pRequest)))
        goto exit;

    if (FAILED(hr = AllocQueuedOperation(pszPath, NULL, 0, &pOp)))
        goto exit;

    pOp->command = HTTPMAIL_PATCHCONTACT;
    pOp->dwContext = dwContext;
    pOp->pfnState = c_rgpfnPatchContact;
    pOp->cState = ARRAYSIZE(c_rgpfnPatchContact);

    pOp->pPropPatchRequest = pRequest;
    pRequest = NULL;

    pOp->dwRHFlags = RH_XMLCONTENTTYPE;

    QueueOperation(pOp);

exit:
    SafeRelease(pRequest);

    return hr;
}

// --------------------------------------------------------------------------------
// INodeFactory Methods
// --------------------------------------------------------------------------------
 
// --------------------------------------------------------------------------------
// CHTTPMailTransport::NotifyEvent
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::NotifyEvent(IXMLNodeSource* pSource, 
                                             XML_NODEFACTORY_EVENT iEvt)
{
    return S_OK;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::BeginChildren
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::BeginChildren(IXMLNodeSource* pSource, XML_NODE_INFO *pNodeInfo)
{
    if (m_op.dwStackDepth <= ELE_STACK_CAPACITY)
        m_op.rgEleStack[m_op.dwStackDepth - 1].fBeganChildren = TRUE;

    return S_OK;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::EndChildren
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::EndChildren(
                                    IXMLNodeSource* pSource, 
                                    BOOL fEmpty, 
                                    XML_NODE_INFO *pNodeInfo)
{
    HRESULT hr = S_OK;

    IxpAssert(HTTPMAIL_NONE != m_op.rResponse.command);
    IxpAssert(NULL != m_op.pParseFuncs);

    if (HTTPMAIL_NONE == m_op.rResponse.command || NULL == m_op.pParseFuncs)
    {
        hr = E_FAIL;
        goto exit;
    }

    if (XML_ELEMENT == pNodeInfo->dwType)
        hr = (this->*(m_op.pParseFuncs->pfnEndChildren))();

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::Error
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::Error(IXMLNodeSource* pSource, 
                                       HRESULT hrErrorCode, 
                                       USHORT cNumRecs, 
                                       XML_NODE_INFO** apNodeInfo)
{
    BSTR  bstr = NULL;

    if (NULL == m_op.rResponse.rIxpResult.pszResponse)
    {
        if (FAILED(pSource->GetErrorInfo(&bstr)))
            goto exit;
    
        HrBSTRToLPSZ(CP_ACP, bstr, &m_op.rResponse.rIxpResult.pszResponse);
    }

exit:
    if (NULL != bstr)
        SysFreeString(bstr);

    return S_OK;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::CreateNode
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::CreateNode(
                        IXMLNodeSource* pSource, 
                        PVOID pNodeParent,
                        USHORT cNumRecs,
                        XML_NODE_INFO** apNodeInfo)
{
    HRESULT         hr = S_OK;
    LPPCDATABUFFER  pTextBuffer = NULL;
    CXMLNamespace   *pBaseNamespace = m_op.pTopNamespace;
    XML_NODE_INFO   *pNodeInfo;

    IxpAssert(HTTPMAIL_NONE != m_op.rResponse.command);
    IxpAssert(NULL != m_op.pParseFuncs);

    if (HTTPMAIL_NONE == m_op.rResponse.command || NULL == m_op.pParseFuncs)
    {
        hr = E_FAIL;
        goto exit;
    }

    if (NULL == apNodeInfo || 0 == cNumRecs)
    {
        hr = E_INVALIDARG;
        goto exit;
    }

    pNodeInfo = apNodeInfo[0];

    switch (pNodeInfo->dwType)
    {
        case XML_ELEMENT:
            if (cNumRecs > 1 && FAILED(hr = PushNamespaces(apNodeInfo, cNumRecs)))
                goto exit;
            hr = (this->*(m_op.pParseFuncs->pfnCreateElement))(pBaseNamespace, pNodeInfo->pwcText, pNodeInfo->ulLen, pNodeInfo->ulNsPrefixLen, pNodeInfo->fTerminal);
            break;

        case XML_PCDATA:
            // we only parse element content...we don't care about attributes
            if (InValidElementChildren())
            {
                // get the buffer
                pTextBuffer = m_op.rgEleStack[m_op.dwStackDepth - 1].pTextBuffer;

                // request one if we don't already have one
                if (NULL == pTextBuffer)
                {
                    if (FAILED(hr = _GetTextBuffer(&pTextBuffer)))
                        goto exit;
                    m_op.rgEleStack[m_op.dwStackDepth - 1].pTextBuffer = pTextBuffer;
                }
                
                hr = _AppendTextToBuffer(pTextBuffer, pNodeInfo->pwcText, pNodeInfo->ulLen);
                    goto exit;
            }
            break;
            
        default:
            break;
    }

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_HrThunkConnectionError
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::_HrThunkConnectionError(void)
{
    return _HrThunkConnectionError(m_op.dwHttpStatus);
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_HrThunkConnectionError
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::_HrThunkConnectionError(DWORD dwStatus)
{
    IxpAssert(NULL == m_op.rResponse.rIxpResult.pszResponse);
    IxpAssert(NULL == m_op.rResponse.rIxpResult.pszProblem);

    if (m_pLogFile && !m_op.fLoggedResponse)
        _LogResponse(NULL, 0);

    m_op.rResponse.rIxpResult.hrResult = HttpErrorToIxpResult(dwStatus);
    _GetRequestHeader(&m_op.rResponse.rIxpResult.pszResponse, HTTP_QUERY_STATUS_TEXT);
    m_op.rResponse.rIxpResult.dwSocketError = GetLastError();

    return _HrThunkResponse(TRUE);
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_HrThunkResponse
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::_HrThunkResponse(BOOL fDone)
{
    HRESULT     hr = S_OK;
    BOOL        fSendResponse;

    // Thread safety
    EnterCriticalSection(&m_cs);

    IxpAssert(HTTPMAIL_NONE != m_op.rResponse.command);

    if (m_op.rResponse.fDone)
    {
        fSendResponse = FALSE;
    }
    else
    {
        fSendResponse = TRUE;

        if (!fDone && WasAborted())
        {
            m_op.rResponse.rIxpResult.hrResult = IXP_E_USER_CANCEL;
            m_op.rResponse.fDone = TRUE;
        }
        else
            m_op.rResponse.fDone = fDone;
    }

    LeaveCriticalSection(&m_cs);

    if (fSendResponse)
        hr = (HRESULT) ::SendMessage(m_hwnd, SPM_HTTPMAIL_SENDRESPONSE, 0, NULL);

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::InvokeResponseCallback
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::InvokeResponseCallback(void)
{
    HRESULT             hr = S_OK;
    IHTTPMailCallback   *pCallback = NULL;

    EnterCriticalSection(&m_cs);

    if (m_pCallback)
    {
        pCallback = m_pCallback;
        pCallback->AddRef();
    }

    LeaveCriticalSection(&m_cs);

    if (pCallback)
    {
        hr = pCallback->OnResponse(&m_op.rResponse);
        pCallback->Release();
    }

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::InitNew
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::InitNew(
                    LPCSTR pszUserAgent,
                    LPCSTR pszLogFilePath, 
                    IHTTPMailCallback *pCallback)
{   
    HRESULT hr = S_OK;

    if (NULL == pszUserAgent || NULL == pCallback)
        return TrapError(E_INVALIDARG);

    IxpAssert(NULL == m_hInternet);

    // Thread Safety
    EnterCriticalSection(&m_cs);

    if (IXP_DISCONNECTED != m_status)
    {
        hr = TrapError(IXP_E_ALREADY_CONNECTED);
        goto exit;
    }

    Reset();

    m_pszUserAgent = PszDupA(pszUserAgent);
    if (NULL == m_pszUserAgent)
    {
        hr = TrapError(E_OUTOFMEMORY);
        goto exit;
    }

    // open log file
    if (pszLogFilePath)
        CreateLogFile(g_hInst, pszLogFilePath, "HTTPMAIL", DONT_TRUNCATE, &m_pLogFile,
            FILE_SHARE_READ | FILE_SHARE_WRITE);
    
    m_pCallback = pCallback;
    m_pCallback->AddRef();

    m_hInternet = InternetOpen(m_pszUserAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    if (NULL == m_hInternet)
    {
        hr = TrapError(IXP_E_SOCKET_INIT_ERROR);
        goto exit;
    }
        
    // Install the callback ptr for the internet handle and all of its derived handles
    //InternetSetStatusCallbackA(m_hInternet, StatusCallbackProxy);

exit:
    LeaveCriticalSection(&m_cs);
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::OnStatusCallback
// --------------------------------------------------------------------------------
void CHTTPMailTransport::OnStatusCallback(
                    HINTERNET hInternet,
                    DWORD dwInternetStatus,
                    LPVOID pvStatusInformation,
                    DWORD dwStatusInformationLength)
{
#if 0
    // Locals
    IXPSTATUS           ixps;

    EnterCriticalSection(&m_cs);

    // if the status message is one of the defined IXPSTATUS messages,
    // notify the callback.
    if ((NULL != m_pCallback) && TranslateWinInetMsg(dwInternetStatus, &ixps))
        m_pCallback->OnStatus(ixps, (IHTTPMailTransport *)this);

    // for now, we just handle the request_complete message
    if (INTERNET_STATUS_REQUEST_COMPLETE == dwInternetStatus)
        HrCommandCompleted();

    LeaveCriticalSection(&m_cs);
#endif
}

// ----------------------------------------------------------------------------
// CHTTPMailTransport::AllocQueuedOperation
// ----------------------------------------------------------------------------
HRESULT CHTTPMailTransport::AllocQueuedOperation(
                                    LPCSTR pszUrl, 
                                    LPVOID pvData, 
                                    ULONG cbDataLen,
                                    LPHTTPQUEUEDOP *ppOp,
                                    BOOL fAdoptData)
{
    HRESULT hr = S_OK;
    LPHTTPQUEUEDOP pTempOp = NULL;

    if (!MemAlloc((void **)&pTempOp , sizeof(HTTPQUEUEDOP)))
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    ZeroMemory(pTempOp, sizeof(HTTPQUEUEDOP));
    
    if (NULL != pszUrl)
    {
        pTempOp->pszUrl = PszDupA(pszUrl);
        if (NULL == pTempOp->pszUrl)
        {
            hr = E_OUTOFMEMORY;
            goto exit;
        }
    }
    
    // can't have a length if data ptr is null
    IxpAssert(!pvData || cbDataLen);
    if (pvData)
    {
        if (!fAdoptData)
        {
            if (!MemAlloc((LPVOID*)&pTempOp->pvData, cbDataLen + 1))
            {
                hr = E_OUTOFMEMORY;
                goto exit;
            }

            CopyMemory(pTempOp->pvData, pvData, cbDataLen);
            ((char *)pTempOp->pvData)[cbDataLen] = '\0';
        }
        else
            pTempOp->pvData = pvData;

        pTempOp->cbDataLen = cbDataLen;
    }

    *ppOp = pTempOp;
    pTempOp = NULL;

exit:
    if (pTempOp)
    {
        SafeMemFree(pTempOp->pszUrl);
        if (!fAdoptData)
            SafeMemFree(pTempOp->pvData);

        MemFree(pTempOp);
    }

    return hr;
}

// ----------------------------------------------------------------------------
// CHTTPMailTransport::QueueOperation
// ----------------------------------------------------------------------------
void CHTTPMailTransport::QueueOperation(LPHTTPQUEUEDOP pOp)
{
    // Thread safety
    EnterCriticalSection(&m_cs);

    if (m_opPendingTail)
        m_opPendingTail->pNext = pOp;
    else
    {
        // if there is no tail, there shouldn't be a head
        IxpAssert(!m_opPendingHead);
        m_opPendingHead = m_opPendingTail = pOp;
    }

    // signal the io thread
    SetEvent(m_hevPendingCommand);

    // Thread Safety
    LeaveCriticalSection(&m_cs);
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::StatusCallbackProxy
// --------------------------------------------------------------------------------
void CHTTPMailTransport::StatusCallbackProxy(
                    HINTERNET hInternet, 
                    DWORD dwContext, 
                    DWORD dwInternetStatus, 
                    LPVOID pvStatusInformation,
                    DWORD dwStatusInformationLength)
{
    // Locals
    CHTTPMailTransport  *pHTTPMail = reinterpret_cast<CHTTPMailTransport *>(IntToPtr(dwContext));

    IxpAssert(NULL != pHTTPMail);
    
    if (NULL != pHTTPMail)
        pHTTPMail->OnStatusCallback(hInternet, dwInternetStatus, pvStatusInformation, dwStatusInformationLength);
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::DoOperation
// --------------------------------------------------------------------------------
void CHTTPMailTransport::DoOperation(void)
{
    HRESULT hr = S_OK;

    while (m_op.iState < m_op.cState)
    {
        hr = (this->*(m_op.pfnState[m_op.iState]))();

        if (FAILED(hr))
            break;

        m_op.iState++;
    }

    if (!m_op.rResponse.fDone && FAILED(hr))
    {
        m_op.rResponse.rIxpResult.hrResult = hr;
        _HrThunkResponse(TRUE);
    }

    FreeOperation();
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::FreeOperation
// --------------------------------------------------------------------------------
void CHTTPMailTransport::FreeOperation(void)
{
    // Thread Safety
    EnterCriticalSection(&m_cs);

    SafeMemFree(m_op.pszUrl);
    SafeMemFree(m_op.pszDestination);
    if (m_op.pszContentType)
    {
        MemFree((void *)m_op.pszContentType);
        m_op.pszContentType = NULL;

    }
    SafeMemFree(m_op.pvData);
    SafeInternetCloseHandle(m_op.hRequest);
    SafeRelease(m_op.pPropFindRequest);
    SafeRelease(m_op.pPropPatchRequest);
    if (NULL != m_op.rgszAcceptTypes)
        FreeStringList(m_op.rgszAcceptTypes);
    SafeRelease(m_op.pHeaderStream);
    SafeRelease(m_op.pBodyStream);

    if (m_op.pTextBuffer)
        _FreeTextBuffer(m_op.pTextBuffer);

    // Free the response
    SafeMemFree(m_op.rResponse.rIxpResult.pszResponse);
    SafeMemFree(m_op.rResponse.rIxpResult.pszProblem);

    PopNamespaces(NULL);

    // in the case of an error, the element stack can
    // contain text buffers that need to be freed
    for (DWORD i = 0; i < m_op.dwStackDepth; ++i)
    {
        if (NULL != m_op.rgEleStack[i].pTextBuffer)
            _FreeTextBuffer(m_op.rgEleStack[i].pTextBuffer);
    }

    SafeMemFree(m_op.rResponse.rIxpResult.pszResponse);

    switch (m_op.rResponse.command)
    {
        case HTTPMAIL_GET:
            SafeMemFree(m_op.rResponse.rGetInfo.pvBody);
            SafeMemFree(m_op.rResponse.rGetInfo.pszContentType);
            break;

        case HTTPMAIL_POST:
        case HTTPMAIL_SENDMESSAGE:
            SafeMemFree(m_op.rResponse.rPostInfo.pszLocation);
            break;

        case HTTPMAIL_COPY:
        case HTTPMAIL_MOVE:
        case HTTPMAIL_MKCOL:
            SafeMemFree(m_op.rResponse.rCopyMoveInfo.pszLocation);
            break;

        case HTTPMAIL_BCOPY:
        case HTTPMAIL_BMOVE:
            SafeMemFree(m_op.rResponse.rBCopyMoveList.prgBCopyMove);
            break;
        
        case HTTPMAIL_MEMBERINFO:
            FreeMemberInfoList();
            SafeMemFree(m_op.rResponse.rMemberInfoList.prgMemberInfo);
            SafeMemFree(m_op.pszRootTimeStamp);
            SafeMemFree(m_op.pszFolderTimeStamp);
            SafeMemFree(m_op.rResponse.rMemberInfoList.pszRootTimeStamp);
            SafeMemFree(m_op.rResponse.rMemberInfoList.pszFolderTimeStamp);
            break;

        case HTTPMAIL_MARKREAD:
            FreeMemberErrorList();
            SafeMemFree(m_op.rResponse.rMemberErrorList.prgMemberError);
            break;


        case HTTPMAIL_LISTCONTACTS:
            FreeContactIdList();
            SafeMemFree(m_op.rResponse.rContactIdList.prgContactId);
            break;

        case HTTPMAIL_CONTACTINFO:
            FreeContactInfoList();
            SafeMemFree(m_op.rResponse.rContactInfoList.prgContactInfo);
            break;

        case HTTPMAIL_POSTCONTACT:
            XP_FREE_STRUCT(HTTPCONTACTID, &m_op.rResponse.rPostContactInfo, NULL);
            break;
           
        case HTTPMAIL_PATCHCONTACT:
            XP_FREE_STRUCT(HTTPCONTACTID, &m_op.rResponse.rPatchContactInfo, NULL);
            break;

        default:
            break;
    }

    ZeroMemory(&m_op, sizeof(HTTPMAILOPERATION));
    m_op.rResponse.command = HTTPMAIL_NONE;
    
    // Thread Safety
    LeaveCriticalSection(&m_cs);
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_BindToStruct
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::_BindToStruct(const WCHAR *pwcText,
                                          ULONG ulLen,
                                          const XPCOLUMN *prgCols,
                                          DWORD cCols,
                                          LPVOID pTarget,
                                          BOOL *pfWasBound)
{
    HRESULT                 hr = S_OK;
    DWORD                   dwColIndex;
    DWORD                   dwColFlags;
    LPSTR                   *ppsz;
    DWORD                   *pdw;
    BOOL                    *pb;
    HTTPMAILSPECIALFOLDER   *ptySpecial;
    HTTPMAILCONTACTTYPE     *ptyContact;
    HMELE                   ele;
    HRESULT                 *phr;

    if (pfWasBound)
        *pfWasBound = FALSE;

    // if the stack is overflowed, we definitely won't do anything with the text
    if (m_op.dwStackDepth >= ELE_STACK_CAPACITY)
        goto exit;

    ele = m_op.rgEleStack[m_op.dwStackDepth - 1].ele;

    for (dwColIndex = 0; dwColIndex < cCols; dwColIndex++)
    {
        if (ele == prgCols[dwColIndex].ele)
            break;
    }

    if (dwColIndex >= cCols)
        goto exit;

    dwColFlags = prgCols[dwColIndex].dwFlags;

    // the column may require validation of the element stack
    if (!!(dwColFlags & XPCF_MSVALIDPROP))
    {
        if (!VALIDSTACK(c_rgPropFindPropValueStack))
            goto exit;
    }
    else if (!!(dwColFlags & XPCF_MSVALIDMSRESPONSECHILD))
    {
        if (!VALIDSTACK(c_rgMultiStatusResponseChildStack))
            goto exit;
    }

    if (dwColIndex < cCols)
    {
        switch (prgCols[dwColIndex].cdt)
        {
            case XPCDT_STRA:
                ppsz = (LPSTR *)(((char *)pTarget) + prgCols[dwColIndex].offset);
                SafeMemFree(*ppsz);
                hr = AllocStrFromStrNW(pwcText, ulLen, ppsz);
                break;

            case XPCDT_DWORD:
                pdw = (DWORD *)(((char *)pTarget) + prgCols[dwColIndex].offset);
                *pdw = 0;
                hr = StrNToDwordW(pwcText, ulLen, pdw);
                break;

            case XPCDT_BOOL:
                pb = (BOOL *)(((char *)pTarget) + prgCols[dwColIndex].offset);
                *pb = FALSE;
                hr = StrNToBoolW(pwcText, ulLen, pb);
                break;

            case XPCDT_IXPHRESULT:
                phr = (HRESULT *)(((char *)pTarget) + prgCols[dwColIndex].offset);
                *phr = S_OK;
                hr = StatusStrNToIxpHr(pwcText, ulLen, phr);
                break;

            case XPCDT_HTTPSPECIALFOLDER:
                ptySpecial = (HTTPMAILSPECIALFOLDER *)(((char *)pTarget) + prgCols[dwColIndex].offset);
                *ptySpecial = HTTPMAIL_SF_NONE;
                hr = StrNToSpecialFolderW(pwcText, ulLen, ptySpecial);
                break;

            case XPCDT_HTTPCONTACTTYPE:
                ptyContact = (HTTPMAILCONTACTTYPE *)(((char *)pTarget) + prgCols[dwColIndex].offset);
                *ptyContact = HTTPMAIL_CT_CONTACT;
                hr = StrNToContactTypeW(pwcText, ulLen, ptyContact);
                break;

            default:
                IxpAssert(FALSE);
                break;
        }

        if (FAILED(hr))
            goto exit;

        // set the bit in the flag word to indicate that this field
        // has been set.
        if (!(dwColFlags & XPCF_DONTSETFLAG))
            m_op.dwPropFlags |= (1 << dwColIndex);

        if (pfWasBound)
            *pfWasBound = TRUE;
    }

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_FreeStruct
// --------------------------------------------------------------------------------
void CHTTPMailTransport::_FreeStruct(const XPCOLUMN *prgCols,
                                     DWORD cCols,
                                     LPVOID pTarget,
                                     DWORD *pdwFlags)
{
    DWORD                   dwFlags;
    DWORD                   dwIndex = 0;
    LPSTR                   *ppsz;
    DWORD                   *pdw;
    BOOL                    *pb;
    HTTPMAILSPECIALFOLDER   *ptySpecial;
    HTTPMAILCONTACTTYPE     *ptyContact;
    HRESULT                 *phr;

    if (NULL != pdwFlags)
    {   
        dwFlags = *pdwFlags;
        *pdwFlags = NOFLAGS;
    }   
    else
        dwFlags = 0xFFFFFFFF;

    while (0 != dwFlags && dwIndex < cCols)
    {
        // test the low bit
        if (!!(dwFlags & 0x00000001))
        {
            switch (prgCols[dwIndex].cdt)
            {
                case XPCDT_STRA:
                    ppsz = (LPSTR *)(((char *)pTarget) + prgCols[dwIndex].offset);
                    SafeMemFree(*ppsz);
                    break;

                case XPCDT_DWORD:
                    pdw = (DWORD *)(((char *)pTarget) + prgCols[dwIndex].offset);
                    *pdw = 0;
                    break;

                case XPCDT_BOOL:
                    pb = (BOOL *)(((char *)pTarget) + prgCols[dwIndex].offset);
                    *pb = FALSE;
                    break;

                case XPCDT_IXPHRESULT:
                    phr = (HRESULT *)(((char *)pTarget) + prgCols[dwIndex].offset);
                    *phr = S_OK;
                    break;

                case XPCDT_HTTPSPECIALFOLDER:
                    ptySpecial = (HTTPMAILSPECIALFOLDER *)(((char *)pTarget) + prgCols[dwIndex].offset);
                    *ptySpecial = HTTPMAIL_SF_NONE;
                    break;

                case XPCDT_HTTPCONTACTTYPE:
                    ptyContact = (HTTPMAILCONTACTTYPE *)(((char *)pTarget) + prgCols[dwIndex].offset);
                    *ptyContact = HTTPMAIL_CT_CONTACT;
                    break;

                default:
                    IxpAssert(FALSE);
                    break;
            }
        }
        
        dwFlags >>= 1;
        dwIndex++;
    }
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_AppendTextToBuffer
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::_AppendTextToBuffer(LPPCDATABUFFER pTextBuffer, 
                                                const WCHAR *pwcText, 
                                                ULONG ulLen)
{
    HRESULT hr = S_OK;
    ULONG ulNewCapacity = pTextBuffer->ulLen + ulLen;

    IxpAssert(ulLen > 0);

    // grow the buffer if necessary, and append the text
    if (pTextBuffer->ulCapacity < ulNewCapacity)
    {
        if (!MemRealloc((void **)&(pTextBuffer->pwcText), sizeof(WCHAR) * ulNewCapacity))
        {
            hr = E_OUTOFMEMORY;
            goto exit;
        }

        pTextBuffer->ulCapacity = ulNewCapacity;
    }

    // copy the new text over. special case the one-byte case to avoid
    // calls to CopyMemory when we see one character entities
    if (1 == ulLen)
    {
        pTextBuffer->pwcText[pTextBuffer->ulLen++] = *pwcText;
    }
    else
    {
        CopyMemory(&pTextBuffer->pwcText[pTextBuffer->ulLen], pwcText, sizeof(WCHAR) * ulLen);
        pTextBuffer->ulLen += ulLen;
    }

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_AllocTextBuffer
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::_AllocTextBuffer(LPPCDATABUFFER *ppTextBuffer)
{
    HRESULT hr = S_OK;

    IxpAssert(NULL != ppTextBuffer);

    *ppTextBuffer = NULL;

    if (!MemAlloc((void **)ppTextBuffer, sizeof(PCDATABUFFER)))
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    // allocate the buffer
    if (!MemAlloc((void **)(&((*ppTextBuffer)->pwcText)), PCDATA_BUFSIZE * sizeof(WCHAR)))
    {
        MemFree(*ppTextBuffer);
        *ppTextBuffer = NULL;

        hr = E_OUTOFMEMORY;
        goto exit;
    }

    (*ppTextBuffer)->ulLen = 0;
    (*ppTextBuffer)->ulCapacity = PCDATA_BUFSIZE ;

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::FreeTextBuffer
// --------------------------------------------------------------------------------
void CHTTPMailTransport::_FreeTextBuffer(LPPCDATABUFFER pTextBuffer)
{
    if (pTextBuffer)
    {
        if (pTextBuffer->pwcText)
            MemFree(pTextBuffer->pwcText);

        MemFree(pTextBuffer);
    }
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::FreeMemberInfoList
// --------------------------------------------------------------------------------
void CHTTPMailTransport::FreeMemberInfoList(void)
{
    DWORD               cInfo = m_op.rResponse.rMemberInfoList.cMemberInfo;
    LPHTTPMEMBERINFO    rgInfo = m_op.rResponse.rMemberInfoList.prgMemberInfo;

    // free the completed infos
    for (DWORD i = 0; i < cInfo; i++)
        XP_FREE_STRUCT(HTTPMEMBERINFO, &rgInfo[i], NULL);

    // free the partial info
    if (m_op.dwPropFlags)
    {
        IxpAssert(cInfo < MEMBERINFO_MAXRESPONSES);
        XP_FREE_STRUCT(HTTPMEMBERINFO, &rgInfo[cInfo], &m_op.dwPropFlags);
    }

    m_op.rResponse.rMemberInfoList.cMemberInfo= 0;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::FreeMemberErrorList
// --------------------------------------------------------------------------------
void CHTTPMailTransport::FreeMemberErrorList(void)
{
    DWORD               cInfo = m_op.rResponse.rMemberErrorList.cMemberError;
    LPHTTPMEMBERERROR   rgInfo = m_op.rResponse.rMemberErrorList.prgMemberError;

    // free the completed infos
    for (DWORD i = 0; i < cInfo; i++)
        XP_FREE_STRUCT(HTTPMEMBERERROR, &rgInfo[i], NULL);

    // free the partial info
    if (m_op.dwPropFlags)
    {
        IxpAssert(cInfo < MEMBERERROR_MAXRESPONSES);
        XP_FREE_STRUCT(HTTPMEMBERERROR, &rgInfo[cInfo], &m_op.dwPropFlags);
    }

    m_op.rResponse.rMemberErrorList.cMemberError = 0;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::FreeContactIdList
// --------------------------------------------------------------------------------
void CHTTPMailTransport::FreeContactIdList(void)
{
    DWORD cId = m_op.rResponse.rContactIdList.cContactId;
    LPHTTPCONTACTID rgId = m_op.rResponse.rContactIdList.prgContactId;

    // free the completed ids
    for (DWORD i = 0; i < cId; ++i)
        XP_FREE_STRUCT(HTTPCONTACTID, &rgId[i], NULL);

    // free the partial id
    if (m_op.dwPropFlags)
    {
        IxpAssert(cId < LISTCONTACTS_MAXRESPONSES);
        XP_FREE_STRUCT(HTTPCONTACTID, &rgId[cId], &m_op.dwPropFlags);

        m_op.dwPropFlags = NOFLAGS;
    }

    m_op.rResponse.rContactIdList.cContactId = 0;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::FreeContactInfoList
// --------------------------------------------------------------------------------
void CHTTPMailTransport::FreeContactInfoList(void)
{
    DWORD cInfo = m_op.rResponse.rContactInfoList.cContactInfo;
    LPHTTPCONTACTINFO rgInfo = m_op.rResponse.rContactInfoList.prgContactInfo;

    // free the completed ids
    for (DWORD i = 0; i < cInfo; ++i)
        XP_FREE_STRUCT(HTTPCONTACTINFO, &rgInfo[i], NULL);

    // free the partial info
    if (m_op.dwPropFlags)
    {
        IxpAssert(cInfo < CONTACTINFO_MAXRESPONSES);
        XP_FREE_STRUCT(HTTPCONTACTINFO, &rgInfo[cInfo], &m_op.dwPropFlags);
    }

    m_op.rResponse.rContactInfoList.cContactInfo = 0;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::FreeBCopyMoveList
// --------------------------------------------------------------------------------
void CHTTPMailTransport::FreeBCopyMoveList(void)
{
    DWORD cInfo = m_op.rResponse.rBCopyMoveList.cBCopyMove;
    LPHTTPMAILBCOPYMOVE rgInfo = m_op.rResponse.rBCopyMoveList.prgBCopyMove;

    // free the completed records
    for (DWORD i = 0; i < cInfo; ++i)
        XP_FREE_STRUCT(HTTPMAILBCOPYMOVE, &rgInfo[i], NULL);

    // free the partial info
    if (m_op.dwPropFlags)
    {
        IxpAssert(cInfo < BCOPYMOVE_MAXRESPONSES);
        XP_FREE_STRUCT(HTTPMAILBCOPYMOVE, &rgInfo[cInfo], &m_op.dwPropFlags);
    }

    m_op.rResponse.rBCopyMoveList.cBCopyMove = 0;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ValidStack
// --------------------------------------------------------------------------------
BOOL CHTTPMailTransport::ValidStack(const HMELE *prgEle, DWORD cEle)
{
    BOOL bResult = TRUE;
    DWORD dw;

    if (cEle != m_op.dwStackDepth)
    {
        bResult = FALSE;
        goto exit;
    }

    IxpAssert(cEle <= ELE_STACK_CAPACITY);

    for (dw = 0; dw < cEle; ++dw)
    {
        if (prgEle[dw] != HMELE_UNKNOWN && prgEle[dw] != m_op.rgEleStack[dw].ele)
        {
            bResult = FALSE;
            goto exit;
        }
    }

exit:
    return bResult;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::PopNamespaces
// --------------------------------------------------------------------------------
void CHTTPMailTransport::PopNamespaces(CXMLNamespace *pBaseNamespace)
{
    CXMLNamespace *pTemp;

    while (pBaseNamespace != m_op.pTopNamespace)
    {
        IxpAssert(m_op.pTopNamespace);
        pTemp = m_op.pTopNamespace->GetParent();
        m_op.pTopNamespace->Release();
        m_op.pTopNamespace = pTemp;
    }
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::PushNamespaces
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::PushNamespaces(XML_NODE_INFO** apNodeInfo, USHORT cNumRecs)
{
    HRESULT         hr = S_OK;
    CXMLNamespace   *pNamespace = NULL;

    for (USHORT i = 0; i < cNumRecs; ++i)
    {
        if (apNodeInfo[i]->dwType == XML_ATTRIBUTE && apNodeInfo[i]->dwSubType == XML_NS)
        {
            // better have at least one more record
            IxpAssert(i < (cNumRecs - 1));
            if (i < (cNumRecs - 1) && apNodeInfo[i + 1]->dwType == XML_PCDATA)
            {
                pNamespace = new CXMLNamespace();
                if (!pNamespace)
                {
                    hr = E_OUTOFMEMORY;
                    goto exit;
                }

                if (apNodeInfo[i]->ulLen != apNodeInfo[i]->ulNsPrefixLen)
                {
                    if (FAILED(hr = pNamespace->SetPrefix(
                                        &apNodeInfo[i]->pwcText[apNodeInfo[i]->ulNsPrefixLen + 1],
                                        apNodeInfo[i]->ulLen - (apNodeInfo[i]->ulNsPrefixLen + 1))))
                        goto exit;
                }

                if (FAILED(hr = pNamespace->SetNamespace(apNodeInfo[i + 1]->pwcText, apNodeInfo[i + 1]->ulLen)))
                    goto exit;

                pNamespace->SetParent(m_op.pTopNamespace);
                if (m_op.pTopNamespace)
                    m_op.pTopNamespace->Release();
                m_op.pTopNamespace = pNamespace;
                pNamespace = NULL;
            }
        }
    }

exit:
    SafeRelease(pNamespace);
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::AllocStrFromStrW
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::AllocStrFromStrNW(
                                const WCHAR *pwcText, 
                                ULONG ulLen, 
                                LPSTR *ppszAlloc)
{
    HRESULT     hr = S_OK;
    DWORD       iBufferSize;
    DWORD       iConvertedChars;

    IxpAssert(NULL != ppszAlloc);

    if (NULL == ppszAlloc)
        return E_INVALIDARG;

    *ppszAlloc = NULL;

    // if pwcText is NULL, the result is null, but not an error
    if (NULL == pwcText)
        goto exit;

    iBufferSize = WideCharToMultiByte(CP_ACP, 0, pwcText, ulLen, NULL, 0, NULL, NULL);
    if (0 == iBufferSize)
    {
        m_op.rResponse.rIxpResult.uiServerError = GetLastError();
        hr = TrapError(E_FAIL);
        goto exit;
    }

    // allocate the buffer (add 1 to the size to allow for eos)
    if (!MemAlloc((void **)ppszAlloc, iBufferSize + 1))
    {
        hr = TrapError(E_OUTOFMEMORY);
        goto exit;
    }

    // convert the string
    iConvertedChars = WideCharToMultiByte(CP_ACP, 0, pwcText, ulLen, *ppszAlloc, iBufferSize, NULL, NULL);
    if (0 == iConvertedChars)
    {
        m_op.rResponse.rIxpResult.uiServerError = GetLastError();
        hr = TrapError(E_FAIL);
        goto exit;
    }

    IxpAssert(iConvertedChars == iBufferSize);
    // terminate the new string
    (*ppszAlloc)[iConvertedChars] = 0;

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::StrNToDwordW
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::StrNToDwordW(const WCHAR *pwcText, ULONG ulLen, DWORD *pdw)
{
    HRESULT     hr = S_OK;
    int         i;
    WCHAR       wcBuf[32];
    WCHAR       *pwcUseBuf;
    BOOL        fFreeBuf = FALSE;

    IxpAssert(NULL != pdw);
    
    if (NULL == pdw)
        return E_INVALIDARG;

    *pdw = 0;

    if (NULL == pwcText)
        goto exit;

    // decide whether to use a local buffer or an allocated buffer
    if (ulLen < 32)
        pwcUseBuf = wcBuf;
    else
    {
        if (!MemAlloc((void **)&pwcUseBuf, (ulLen + 1) * sizeof(WCHAR)))
        {
            hr = E_OUTOFMEMORY;
            goto exit;
        }
        fFreeBuf = TRUE;
    }

    // copy the string over
    CopyMemory(pwcUseBuf, pwcText, ulLen * sizeof(WCHAR));
    pwcUseBuf[ulLen] = 0;

    *pdw = StrToIntW(pwcUseBuf);

exit:
    if (fFreeBuf)
        MemFree(pwcUseBuf);

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::StrNToSpecialFolderW
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::StrNToSpecialFolderW(const WCHAR *pwcText, 
                                                 ULONG ulLen, 
                                                 HTTPMAILSPECIALFOLDER *ptySpecial)
{
    HRESULT     hr = S_OK;

    if (NULL == ptySpecial)
        return E_INVALIDARG;

    *ptySpecial = HTTPMAIL_SF_UNRECOGNIZED;

    if (NULL != pwcText && ulLen > 0)
    {
        for (DWORD dw = 0; dw < ARRAYSIZE(c_rgpfnSpecialFolder); dw++)
        {
            if (ulLen == c_rgpfnSpecialFolder[dw].ulLen)
            {
                if (0 == StrCmpNW(c_rgpfnSpecialFolder[dw].pwcName, pwcText, ulLen))
                {
                    *ptySpecial = c_rgpfnSpecialFolder[dw].tyFolder;
                    break;
                }
            }
        }
    }

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::StrNToContactTypeW
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::StrNToContactTypeW(const WCHAR *pwcText, 
                                                 ULONG ulLen, 
                                                 HTTPMAILCONTACTTYPE *ptyContact)
{
    HRESULT hr = S_OK;
    BOOL    fGroup = FALSE;

    IxpAssert(NULL != ptyContact);

    if (NULL == ptyContact)
        return E_INVALIDARG;

    // for now, we treat the presence of the <group> element as an indication that
    // the contact is a group
    *ptyContact = HTTPMAIL_CT_GROUP;

#if 0

    // for now, we treat the value as an integer-based bool
    hr = StrNToBoolW(pwcText, ulLen, &fGroup);

    // default is contact
    *ptyContact = fGroup ? HTTPMAIL_CT_GROUP : HTTPMAIL_CT_CONTACT;
#endif

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::StrNToBoolW
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::StrNToBoolW(const WCHAR *pwcText, DWORD ulLen, BOOL *pb)
{
    HRESULT     hr = S_OK;
    DWORD       dw;

    IxpAssert(NULL != pb);

    if (NULL == pb)
        return E_INVALIDARG;

    *pb = FALSE;

    if (NULL == pwcText)
        goto exit;

    if (FAILED(hr = StrNToDwordW(pwcText, ulLen, &dw)))
        goto exit;

    if (dw != 0)
        *pb = TRUE;

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::StatusStrNToIxpHr
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::StatusStrNToIxpHr(const WCHAR *pwcText, DWORD ulLen, HRESULT *phr)
{
    HRESULT     hr = S_OK;
    DWORD       dw;
    LPSTR       pszStatus = NULL;
    DWORD       dwStatus = 0;

    IxpAssert(NULL != phr);

    if (NULL == phr)
        return E_INVALIDARG;

    *phr = S_OK;

    if (NULL == pwcText)
        goto exit;

    if (FAILED(hr = AllocStrFromStrNW(pwcText, ulLen, &pszStatus)) || NULL == pszStatus)
        goto exit;

    HrParseHTTPStatus(pszStatus, &dwStatus);

    if (dwStatus < 200 || dwStatus > 299)
        *phr = HttpErrorToIxpResult(dwStatus);

exit:
    SafeMemFree(pszStatus);
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::CommandToVerb
// --------------------------------------------------------------------------------
LPSTR CHTTPMailTransport::CommandToVerb(HTTPMAILCOMMAND command)
{
    LPSTR pszVerb = NULL;

   // convert the command to a string
    switch (command)
    {
    case HTTPMAIL_GET:
        pszVerb = "GET";
        break;

    case HTTPMAIL_POST:
    case HTTPMAIL_SENDMESSAGE:
        pszVerb = "POST";
        break;

    case HTTPMAIL_PUT:
        pszVerb = "PUT";
        break;

    case HTTPMAIL_GETPROP:
    case HTTPMAIL_PROPFIND:
    case HTTPMAIL_MEMBERINFO:
    case HTTPMAIL_LISTCONTACTS:
    case HTTPMAIL_CONTACTINFO:
        pszVerb = "PROPFIND";
        break;

    case HTTPMAIL_MARKREAD:
        if (m_op.fBatch)
            pszVerb = "BPROPPATCH";
        else
            pszVerb = "PROPPATCH";
        break;

    case HTTPMAIL_MKCOL:
        pszVerb = "MKCOL";
        break;

    case HTTPMAIL_COPY:
        pszVerb = "COPY";
        break;

    case HTTPMAIL_BCOPY:
        pszVerb = "BCOPY";
        break;

    case HTTPMAIL_MOVE:
        pszVerb = "MOVE";
        break;

    case HTTPMAIL_BMOVE:
        pszVerb = "BMOVE";
        break;

    case HTTPMAIL_PROPPATCH:
        pszVerb = "PROPPATCH";
        break;

    case HTTPMAIL_DELETE:
        pszVerb = "DELETE";
        break;

    case HTTPMAIL_BDELETE:
        pszVerb = "BDELETE";
        break;

    case HTTPMAIL_POSTCONTACT:
        // first post the contact, then do a propfind
        if (NULL == m_op.rResponse.rPostContactInfo.pszHref)
            pszVerb = "POST";
        else
            pszVerb = "PROPFIND";
        break;

    case HTTPMAIL_PATCHCONTACT:
        // first patch the contact, then do a propfind
        if (NULL == m_op.rResponse.rPatchContactInfo.pszHref)
            pszVerb = "PROPPATCH";
        else
            pszVerb = "PROPFIND";
        break;


    default:
        pszVerb = "";
        IxpAssert(FALSE);
        break;
    }

    return pszVerb;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::UpdateLogonInfo
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::UpdateLogonInfo(void)
{
    // send the message synchronously
    return (HRESULT) (::SendMessage(m_hwnd, SPM_HTTPMAIL_LOGONPROMPT, NULL, NULL));
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::GetParentWindow
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::GetParentWindow(HWND *phwndParent)
{
    // send the message synchronously
    return (HRESULT) (::SendMessage(m_hwnd, SPM_HTTPMAIL_GETPARENTWINDOW, (WPARAM)phwndParent, NULL));
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ReadBytes
// --------------------------------------------------------------------------------
BOOL CHTTPMailTransport::ReadBytes(LPSTR pszBuffer, DWORD cbBufferSize, DWORD *pcbBytesRead)
{
    return InternetReadFile(m_op.hRequest, pszBuffer, cbBufferSize, pcbBytesRead);
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_GetStatusCode
// --------------------------------------------------------------------------------
BOOL CHTTPMailTransport::_GetStatusCode(DWORD *pdw)
{
    IxpAssert(NULL != pdw);

    DWORD dwStatusSize = sizeof(DWORD);
    *pdw = 0;
    return HttpQueryInfo(m_op.hRequest, HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE, pdw, &dwStatusSize, NULL);
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_GetContentLength
// --------------------------------------------------------------------------------
BOOL CHTTPMailTransport::_GetContentLength(DWORD *pdw)
{
    IxpAssert(NULL != pdw);

    DWORD dwLengthSize = sizeof(DWORD);
    *pdw = 0;
    return HttpQueryInfo(m_op.hRequest, HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_CONTENT_LENGTH, pdw, &dwLengthSize, NULL);
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_GetRequestHeader
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::_GetRequestHeader(LPSTR *ppszHeader, DWORD dwHeader)
{
    HRESULT     hr = S_OK;
    DWORD       dwSize = MAX_PATH;
    LPSTR       pszHeader = NULL;

    Assert(NULL != ppszHeader);
    *ppszHeader = NULL;

retry:
    if (!MemAlloc((void **)&pszHeader, dwSize))
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    if (!HttpQueryInfo(m_op.hRequest, dwHeader, pszHeader, &dwSize, NULL))
    {
        if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
            goto exit;
        
        SafeMemFree(pszHeader);        
        goto retry;
    }

    *ppszHeader = pszHeader;
    pszHeader = NULL;

exit:
    SafeMemFree(pszHeader);
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_AddRequestHeader
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::_AddRequestHeader(LPCSTR pszHeader)
{
    HRESULT     hr = S_OK;

    if (!HttpAddRequestHeaders(m_op.hRequest, pszHeader, lstrlen(pszHeader), HTTP_ADDREQ_FLAG_ADD))
        hr = GetLastError();

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_AuthCurrentRequest
// --------------------------------------------------------------------------------
BOOL CHTTPMailTransport::_AuthCurrentRequest(DWORD dwStatus, BOOL fRetryAuth)
{
    BOOL        fResult = FALSE;
    HRESULT     hr;

    // unused code to let wininet do the ui
    #if 0
    if (HTTP_STATUS_PROXY_AUTH_REQ == dwStatus || HTTP_STATUS_DENIED == dwStatus)
    {
        if (!fRequestedParent)
        {
            GetParentWindow(&hwndParent);
            fRequestedParent = TRUE;
        }

        hr = InternetErrorDlg(hwndParent, m_op.hRequest, hr, 
                           FLAGS_ERROR_UI_FILTER_FOR_ERRORS | 
                           FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS |
                           FLAGS_ERROR_UI_FLAGS_GENERATE_DATA,
                           NULL);
        if (ERROR_INTERNET_FORCE_RETRY == hr)
            goto resend;
    }
#endif

        // TODO: should probably let wininet handle proxy auth errors
#if 0
    case HTTP_STATUS_PROXY_AUTH_REQ:    //Proxy Authentication Required
        InternetSetOption(m_op.hRequest, INTERNET_OPTION_PROXY_USERNAME, 
                        GetUserName(), strlen(GetUserName())+1);
        InternetSetOption(m_op.hRequest, INTERNET_OPTION_PROXY_PASSWORD, 
                        GetPassword(), strlen(GetPassword())+1);
        break;
#endif

    if (HTTP_STATUS_DENIED == dwStatus)     //Server Authentication Required
    {
        if (fRetryAuth || (SUCCEEDED(hr = UpdateLogonInfo()) && S_FALSE != hr))
        {
            InternetSetOption(m_op.hRequest, INTERNET_OPTION_USERNAME, 
                        GetUserName(), strlen(GetUserName())+1);
            InternetSetOption(m_op.hRequest, INTERNET_OPTION_PASSWORD, 
                        GetPassword(), strlen(GetPassword())+1);
            fResult = TRUE;
        }
    }

    return fResult;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_LogRequest
// --------------------------------------------------------------------------------
void CHTTPMailTransport::_LogRequest(LPVOID pvData, DWORD cbData)
{
    HRESULT     hr = S_OK;
    CByteStream bs;
    LPSTR       pszCommand = CommandToVerb(m_op.rResponse.command);
    LPSTR       pszLogData = NULL;

    Assert(NULL != m_pLogFile);
    if (NULL == m_pLogFile)
        return;

    FAIL_EXIT_STREAM_WRITE(bs, c_szCRLF);
    FAIL_EXIT_STREAM_WRITE(bs, pszCommand);
    FAIL_EXIT_STREAM_WRITE(bs, c_szSpace);
    FAIL_EXIT_STREAM_WRITE(bs, m_op.pszUrl);

    if (pvData && cbData)
    {
        FAIL_EXIT_STREAM_WRITE(bs, c_szCRLF);
        if (FAILED(hr = bs.Write(pvData, cbData, NULL)))
            goto exit;
    }

    FAIL_EXIT_STREAM_WRITE(bs, c_szCRLF);
    FAIL_EXIT_STREAM_WRITE(bs, c_szCRLF);

    FAIL_EXIT(hr = bs.HrAcquireStringA(NULL, &pszLogData, ACQ_DISPLACE));

    m_pLogFile->WriteLog(LOGFILE_TX, pszLogData);

exit:
    SafeMemFree(pszLogData);

    return;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_LogResponse
// --------------------------------------------------------------------------------
void CHTTPMailTransport::_LogResponse(LPVOID pvData, DWORD cbData)
{
    HRESULT     hr = S_OK;
    CByteStream bs;
    LPSTR       pszHeaders = NULL;
    LPSTR       pszLogData = NULL;

    Assert(NULL != m_pLogFile);
    if (NULL == m_pLogFile || m_op.fLoggedResponse)
        return;
    
    FAIL_EXIT(_GetRequestHeader(&pszHeaders, HTTP_QUERY_RAW_HEADERS_CRLF));

    if (pszHeaders)
    {
        // prefix with a CRLF
        if ('\r' != pszHeaders[0])
            FAIL_EXIT_STREAM_WRITE(bs, c_szCRLF);

        FAIL_EXIT_STREAM_WRITE(bs, pszHeaders);
    }

    if (pvData && cbData)
    {
        if (FAILED(hr = bs.Write(pvData, cbData, NULL)))
            goto exit;
    }

    FAIL_EXIT_STREAM_WRITE(bs, c_szCRLF);
    FAIL_EXIT_STREAM_WRITE(bs, c_szCRLF);

    FAIL_EXIT(hr = bs.HrAcquireStringA(NULL, &pszLogData, ACQ_DISPLACE));

    m_pLogFile->WriteLog(LOGFILE_RX, pszLogData);

exit:
    m_op.fLoggedResponse = TRUE;

    SafeMemFree(pszHeaders);
    SafeMemFree(pszLogData);

    return;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::TranslateWinInetMsg
// --------------------------------------------------------------------------------
BOOL CHTTPMailTransport::TranslateWinInetMsg(
                                DWORD dwInternetStatus,
                                IXPSTATUS *pIxpStatus)
{
    IxpAssert(NULL != pIxpStatus);

    switch (dwInternetStatus)
    {
    case INTERNET_STATUS_RESOLVING_NAME:
        *pIxpStatus = IXP_FINDINGHOST;
        break;

    case  INTERNET_STATUS_CONNECTING_TO_SERVER:
        *pIxpStatus = IXP_CONNECTING;
        break;
    
    case INTERNET_STATUS_CONNECTED_TO_SERVER:
        *pIxpStatus = IXP_CONNECTED;
        break;

    case INTERNET_STATUS_CLOSING_CONNECTION:
        *pIxpStatus = IXP_DISCONNECTING;
        break;

    case INTERNET_STATUS_CONNECTION_CLOSED:
        *pIxpStatus = IXP_DISCONNECTED;
        break;

    case INTERNET_STATUS_REQUEST_COMPLETE:
        *pIxpStatus = IXP_LAST;
        break;

    default:
        // status codes that are not translated:
        //      INTERNET_STATUS_NAME_RESOLVED 
        //      INTERNET_STATUS_SENDING_REQUEST 
        //      INTERNET_STATUS_ REQUEST_SENT
        //      INTERNET_STATUS_RECEIVING_RESPONSE
        //      INTERNET_STATUS_RESPONSE_RECEIVED
        //      INTERNET_STATUS_REDIRECT
        //      INTERNET_STATUS_HANDLE_CREATED
        //      INTERNET_STATUS_HANDLE_CLOSING

        return FALSE;
    }

    return TRUE;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_CreateXMLParser
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::_CreateXMLParser()
{
    HRESULT                 hr = S_OK;
    
    if (NULL == m_pParser)
    {
            // instantiate the xml document
        hr = ::CoCreateInstance(CLSID_XMLParser, 
                                 NULL, 
                                 CLSCTX_INPROC_SERVER, 
                                 IID_IXMLParser, 
                                 reinterpret_cast<void **>(&m_pParser));

        if (FAILED(hr))
            goto exit;


        if (FAILED(hr = m_pParser->SetFlags(XMLFLAG_FLOATINGAMP)))
            goto exit;
    }

    hr = m_pParser->SetFactory(this);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::OpenRequest
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::OpenRequest(void)
{
    LPSTR           pszVerb = NULL;
    HRESULT         hr = S_OK;
    LPSTR           pszHostName = NULL;
    LPSTR           pszUrlPath = NULL;
    INTERNET_PORT   nPort;
    LPSTR           pszUserName = GetUserName();
    LPSTR           pszPassword = GetPassword();

    if (NULL == pszUserName)
        pszUserName = "";

    if (NULL == pszPassword)
        pszPassword = "";

    // crack the url into component parts
    if (FAILED(hr = HrCrackUrl(m_op.pszUrl, &pszHostName, &pszUrlPath, &nPort)))
    {
        TrapError(hr);
        goto exit;
    }

    if (FAILED(hr = HrConnectToHost(pszHostName, nPort, pszUserName, NULL)))
    {
        TrapError(hr);
        goto exit;
    }

    // We have to set the password and username on every connection. If we don't,
    // and and incorrect password or username forces us to prompt the user, the
    // newly entered data won't get used on subsequent requests
    InternetSetOption(GetConnection(), INTERNET_OPTION_USERNAME, pszUserName, lstrlen(pszUserName) + 1);
    InternetSetOption(GetConnection(), INTERNET_OPTION_PASSWORD, pszPassword, lstrlen(pszPassword) + 1);

    FAIL_ABORT;

    // convert the command to a verb string
    pszVerb = CommandToVerb(m_op.rResponse.command);

    // Open the HTTP request
    m_op.hRequest = HttpOpenRequest(
                        GetConnection(), 
                        pszVerb, 
                        pszUrlPath,
                        NULL,
                        NULL,
                        m_op.rgszAcceptTypes,
                        INTERNET_FLAG_EXISTING_CONNECT | 
                        INTERNET_FLAG_RELOAD |
                        INTERNET_FLAG_KEEP_CONNECTION,
                        0);
    
    if (NULL == m_op.hRequest)
    {
        DWORD dwErr = GetLastError();
        hr = E_FAIL;
    }

exit:
    SafeMemFree(pszHostName);
    SafeMemFree(pszUrlPath);

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::SendPostRequest
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::SendPostRequest(void)
{
    HRESULT             hr = S_OK;
    INTERNET_BUFFERS    buffers;
    DWORD               cbData;
    ULONG               cbRead;
    ULONG               cbWritten;
    BOOL                fResult;
    CHAR                localBuffer[HTTPMAIL_BUFSIZE];
    LARGE_INTEGER       liOrigin = {0,0};
    DWORD               dwBufferLength;
    BOOL                fSentData = FALSE;
    BOOL                fWillSend;
    DWORD               dwWinInetErr = 0;
    BOOL                fRetryAuth = FALSE;

    // log the request, but don't log the request body
    if (m_pLogFile)
        _LogRequest(NULL, 0);

    if (m_op.pHeaderStream)
    {
        if (FAILED(hr = HrGetStreamSize(m_op.pHeaderStream, &m_op.rResponse.rPostInfo.cbTotal)))
            goto exit;
    }

    if (m_op.pBodyStream)
    {
        if (FAILED(hr = HrGetStreamSize(m_op.pBodyStream, &cbData)))
            goto exit;

        m_op.rResponse.rPostInfo.cbTotal += cbData;
    }

    buffers.dwStructSize = sizeof(INTERNET_BUFFERSA);
    buffers.Next = NULL;
    buffers.lpcszHeader = NULL;
    buffers.dwHeadersLength = 0;
    buffers.dwHeadersTotal = 0;
    buffers.lpvBuffer = NULL;
    buffers.dwBufferLength = 0;
    buffers.dwBufferTotal = m_op.rResponse.rPostInfo.cbTotal;
    buffers.dwOffsetLow = 0;
    buffers.dwOffsetHigh = 0;

resend:
    if (fSentData)
    {
        m_op.rResponse.rPostInfo.fResend = TRUE;
        m_op.rResponse.rPostInfo.cbCurrent = 0;
        
        _HrThunkResponse(FALSE);

        m_op.rResponse.rPostInfo.fResend = FALSE;

        FAIL_ABORT;
    }

    fResult = HttpSendRequestEx(m_op.hRequest, &buffers, NULL, 0, 0);
    if (!fResult)
    {
        dwWinInetErr = GetLastError();
        if (ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION == dwWinInetErr)
        {
            fRetryAuth = TRUE;
            goto resend;
        }

        _HrThunkConnectionError(dwWinInetErr);
        hr = E_FAIL;
        goto exit;
    }
    
    // with some auth methods (e.g., NTLM), wininet will send a post request
    // with a content length of 0, and will ignore calls to InternetWriteFile
    // until the server sends a 100 (continue) response. wininet will then
    // return ERROR_INTERNET_FORCE_RETRY to force a resend. we detect this
    // case here so that we don't send a bunch of OnResponse progress notifications
    // when no data is actually going out over the wire

    // this constant isn't in the wininet headers as of 10/6/98!!
#ifndef INTERNET_OPTION_DETECT_POST_SEND
#define INTERNET_OPTION_DETECT_POST_SEND        71
#endif

    dwBufferLength = sizeof(fWillSend);
    if (!InternetQueryOption(m_op.hRequest, INTERNET_OPTION_DETECT_POST_SEND, &fWillSend, &dwBufferLength))
        fWillSend = TRUE;
    else
    {
        Assert(dwBufferLength == sizeof(BOOL));
    }

    if (fWillSend)
    {
        fSentData = TRUE;

        if (m_op.pHeaderStream)
        {
            // rewind the stream
            if (FAILED(hr = m_op.pHeaderStream->Seek(liOrigin, STREAM_SEEK_SET, NULL)))
                goto exit;

            while (TRUE)
            {
                if (FAILED(hr = m_op.pHeaderStream->Read(localBuffer, sizeof(localBuffer), &cbRead)))
                    goto exit;

                if (0 == cbRead)
                    break;
            
                fResult = InternetWriteFile(m_op.hRequest, localBuffer, cbRead, &cbWritten);
                IxpAssert(!fResult || (cbRead == cbWritten));
                if (!fResult)
                {
                    _HrThunkConnectionError(GetLastError());
                    hr = E_FAIL;
                    goto exit;
                }
            
                m_op.rResponse.rPostInfo.cbIncrement = cbWritten;
                m_op.rResponse.rPostInfo.cbCurrent += cbWritten;

                _HrThunkResponse(FALSE);

                m_op.rResponse.rPostInfo.cbIncrement = 0;

                FAIL_ABORT;
            }
        }

        if (m_op.pBodyStream)
        {
            // rewind the stream
            if (FAILED(hr = m_op.pBodyStream->Seek(liOrigin, STREAM_SEEK_SET, NULL)))
                goto exit;

            while (TRUE)
            {
                if (FAILED(hr = m_op.pBodyStream->Read(localBuffer, sizeof(localBuffer), &cbRead)))
                    goto exit;

                if (0 == cbRead)
                    break;

                fResult = InternetWriteFile(m_op.hRequest, localBuffer, cbRead, &cbWritten);
                IxpAssert(!fResult || (cbRead == cbWritten));
                if (!fResult)
                {
                    _HrThunkConnectionError(GetLastError());
                    hr = E_FAIL;
                    goto exit;
                }
            
                m_op.rResponse.rPostInfo.cbIncrement = cbWritten;
                m_op.rResponse.rPostInfo.cbCurrent += cbWritten;

                _HrThunkResponse(FALSE);

                m_op.rResponse.rPostInfo.cbIncrement = 0;

                FAIL_ABORT;
 
            }
        }
    }

    fResult = HttpEndRequest(m_op.hRequest, NULL, 0, 0);
    if (!fResult)
    {
        if (ERROR_INTERNET_FORCE_RETRY == GetLastError())
            goto resend;

        _HrThunkConnectionError(GetLastError());
    }

    if (!_GetStatusCode(&m_op.dwHttpStatus))
    {
        _HrThunkConnectionError(GetLastError());
        hr = E_FAIL;
        goto exit;
    }

    if (_AuthCurrentRequest(m_op.dwHttpStatus, fRetryAuth))
    {
        fRetryAuth = FALSE;
        goto resend;
    }

    if (!fResult)
    {
        _HrThunkConnectionError();
        hr = E_FAIL;
        goto exit;
    }

    // status codes not in the 200-299 range indicate an error
    if (200 > m_op.dwHttpStatus || 299 < m_op.dwHttpStatus)
    {
        _HrThunkConnectionError();
        hr = E_FAIL;
    }

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::SendRequest
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::SendRequest(void)
{
    HRESULT     hr = S_OK;
    DWORD       dwError;
    BOOL        bResult;
    HWND        hwndParent = NULL;
    BOOL        fRequestedParent = FALSE;
    DWORD       dwWinInetErr = 0;
    BOOL        fRetryAuth = FALSE;

    // log the request, including the requets body
    if (m_pLogFile)
        _LogRequest(m_op.pvData, m_op.cbDataLen);

resend:
    hr = S_OK;
    bResult = HttpSendRequest(m_op.hRequest, NULL, 0L, m_op.pvData, m_op.cbDataLen);
    if (!bResult)
    {
        dwWinInetErr = GetLastError();
        if (ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION == dwWinInetErr)
        {
            fRetryAuth = TRUE;
            goto resend;
        }

        _HrThunkConnectionError(dwWinInetErr);
        hr = E_FAIL;
        goto exit;
    }

    if (!_GetStatusCode(&m_op.dwHttpStatus))
    {
        _HrThunkConnectionError(GetLastError());
        hr = E_FAIL;
        goto exit;
    }

    if (_AuthCurrentRequest(m_op.dwHttpStatus, fRetryAuth))
    {
        fRetryAuth = FALSE;
        goto resend;
    }

    // status codes not in the 200-299 range indicate an error
    if (200 > m_op.dwHttpStatus|| 299 < m_op.dwHttpStatus)
    {
        _HrThunkConnectionError();
        hr = E_FAIL;
        goto exit;
    }

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::RequireMultiStatus
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::RequireMultiStatus(void)
{
    HRESULT hr = S_OK;

    if (207 != m_op.dwHttpStatus)
    {
        _HrThunkConnectionError(ERROR_INTERNET_CANNOT_CONNECT);
        hr = E_FAIL;
    }

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::FinalizeRequest
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::FinalizeRequest(void)
{
    HRESULT     hr                  = S_OK;
    LPSTR       pszTimestampHeader  = NULL;

    // log the response if it hasn't already been logged
    if (m_pLogFile && !m_op.fLoggedResponse)
        _LogResponse(NULL, 0);

    if (HTTPMAIL_MEMBERINFO == m_op.rResponse.command)
    {
        // Get the headers and copy them. If we don't get timestamp header, its not a big deal. We don't report an error.
        hr = _HrGetTimestampHeader(&pszTimestampHeader);
        if (SUCCEEDED(hr))
        {
            // Get the Active timestamp
            FAIL_EXIT(hr = _HrParseAndCopy(c_szActive, &m_op.rResponse.rMemberInfoList.pszFolderTimeStamp, pszTimestampHeader));
            
            // Get RootTimeStamp which for some strange reason comes as Folders TimeStamp
            // This call might fail for legitimate reasons. For Inbox list headers we do not get a RootTimeStamp. 
            // Hence we do not exit if we can't get root time stamp.
            _HrParseAndCopy(c_szFolders, &m_op.rResponse.rMemberInfoList.pszRootTimeStamp, pszTimestampHeader);

            SafeMemFree(pszTimestampHeader);
             
        }
    }

    hr =  _HrThunkResponse(TRUE);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ProcessGetResponse
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ProcessGetResponse(void)
{
    HRESULT     hr = S_OK;
    BOOL        bRead;
    DWORD       cbReadBytes = 0;

    // log the respnse, but don't log the response body
    if (m_pLogFile && !m_op.fLoggedResponse)
        _LogResponse(NULL, 0);

    Assert(NULL == m_op.rResponse.rGetInfo.pszContentType);

    // extract the content type header
    FAIL_EXIT(hr  = _GetRequestHeader(&m_op.rResponse.rGetInfo.pszContentType, HTTP_QUERY_CONTENT_TYPE));

    // try to get the content length
    m_op.rResponse.rGetInfo.fTotalKnown = _GetContentLength(&m_op.rResponse.rGetInfo.cbTotal);

    do
    {
        // The buffer is owned by this object, but the client
        // has the option of taking ownership of the buffer
        // whenever a read completes. We reallocate the buffer
        // here if necessary
        FAIL_ABORT;
        
        if (!m_op.rResponse.rGetInfo.pvBody && !MemAlloc((void**)&m_op.rResponse.rGetInfo.pvBody, HTTPMAIL_BUFSIZE + 1))
        {    
            hr = E_OUTOFMEMORY;
            break;
        }


        bRead = ReadBytes((char *)m_op.rResponse.rGetInfo.pvBody, HTTPMAIL_BUFSIZE, &cbReadBytes);
        
        m_op.rResponse.rGetInfo.cbIncrement = cbReadBytes;
        m_op.rResponse.rGetInfo.cbCurrent += cbReadBytes;

        // we guarantee space for the terminating null by allocating
        // a buffer one larger than bufsize
        static_cast<char *>(m_op.rResponse.rGetInfo.pvBody)[cbReadBytes] = '\0';

        // Send a message to the window that lives in the client's thread
        _HrThunkResponse(0 == cbReadBytes);

    } while (0 < cbReadBytes);

exit:
    SafeMemFree(m_op.rResponse.rGetInfo.pvBody);

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ProcessPostResponse
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ProcessPostResponse(void)
{
    HRESULT     hr = S_OK;

    // log the response
    if (m_pLogFile && !m_op.fLoggedResponse)
        _LogResponse(NULL, 0);

    if (m_op.dwHttpStatus < 200 || m_op.dwHttpStatus > 299)
    {
        _HrThunkConnectionError();
        hr = E_FAIL;
        goto exit;
    }

    hr = _GetRequestHeader(&m_op.rResponse.rPostInfo.pszLocation, HTTP_QUERY_LOCATION);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ProcessXMLResponse
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ProcessXMLResponse(void)
{
    HRESULT         hr = S_OK;
    BOOL            bRead;
    LPSTR           pszBody = NULL;
    DWORD           cbLength = 0;
    CByteStream     *pLogStream = NULL;
    BOOL            fFoundBytes = FALSE;

    if (m_pLogFile && !m_op.fLoggedResponse)
        pLogStream = new CByteStream();

    // we only parse xml if the response is a 207 (multistatus)
    if (m_op.dwHttpStatus != 207)
        goto exit;

    // create the xml parser
    if (FAILED(hr = _CreateXMLParser()))
        goto exit;

    if (!MemAlloc((void **)&pszBody, HTTPMAIL_BUFSIZE))
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    do
    {
        FAIL_ABORT;

        bRead = ReadBytes(pszBody, HTTPMAIL_BUFSIZE, &cbLength);
        if (0 == cbLength)
        {
            if (fFoundBytes)
            {
                // parse any remaining bytes in the parser's buffer
                if (FAILED(hr = m_pParser->PushData(NULL, 0, TRUE)))
                    goto exit;

                if (FAILED(hr = m_pParser->Run(-1)))
                    goto exit;
            }

            break;
        }

        fFoundBytes = TRUE;

        // if logging, write the block into the log stream
        if (pLogStream)
            pLogStream->Write(pszBody, cbLength, NULL);

        if (FAILED(hr = m_pParser->PushData(pszBody, cbLength, FALSE)))
            goto exit;

        if (FAILED(hr = m_pParser->Run(-1)))
        {
            if (hr == E_PENDING)
                hr = S_OK;
            else
                goto exit;
        }

    } while (TRUE);

exit:
    SafeMemFree(pszBody);

    if (pLogStream)
    {
        LPSTR pszLog = NULL;
        DWORD dwLog = 0;
        
        pLogStream->HrAcquireStringA(&dwLog, &pszLog, ACQ_DISPLACE);
    
        _LogResponse(pszLog, dwLog);
        SafeMemFree(pszLog);

        delete pLogStream;
    }

    if (m_pParser)
        m_pParser->Reset();

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::GeneratePropFindXML
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::GeneratePropFindXML(void)
{
    HRESULT hr = S_OK;
    LPSTR   pszXML = NULL;

    IxpAssert(NULL == m_op.pvData && 0 == m_op.cbDataLen);
    IxpAssert(NULL != m_op.pPropFindRequest);

    if (FAILED(hr = m_op.pPropFindRequest->GenerateXML(&pszXML)))
        goto exit;

    m_op.pvData = pszXML;
    m_op.cbDataLen = lstrlen(pszXML);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::AddDepthHeader
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::AddDepthHeader(void)
{
    HRESULT hr = S_OK;
    char szDepthHeader[64];
    
    if (0 != m_op.dwDepth && !!(m_op.dwRHFlags & RH_NOROOT))
    {
        if (DEPTH_INFINITY == m_op.dwDepth)
            lstrcpy(szDepthHeader, c_szDepthInfinityNoRootHeader);
        else
            wsprintf(szDepthHeader, c_szDepthNoRootHeader, m_op.dwDepth);
    }
    else
    {
        if (DEPTH_INFINITY == m_op.dwDepth)
            lstrcpy(szDepthHeader, c_szDepthInfinityHeader);
        else
            wsprintf(szDepthHeader, c_szDepthHeader, m_op.dwDepth);
    }
    
    if (!HttpAddRequestHeaders(m_op.hRequest, szDepthHeader, lstrlen(szDepthHeader), HTTP_ADDREQ_FLAG_ADD))
    {
        _HrThunkConnectionError();
        hr = E_FAIL;
    }

    return hr;    
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::GeneratePropPatchXML
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::GeneratePropPatchXML(void)
{
    HRESULT hr = S_OK;
    LPSTR pszXML = NULL;

    IxpAssert(NULL == m_op.pvData && 0 == m_op.cbDataLen);
    IxpAssert(NULL != m_op.pPropPatchRequest);

    if (FAILED(hr = m_op.pPropPatchRequest->GenerateXML(&pszXML)))
        goto exit;

    m_op.pvData = pszXML;
    m_op.cbDataLen = lstrlen(pszXML);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ProcessCreatedResponse
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ProcessCreatedResponse(void)
{
    HRESULT     hr = S_OK;
    
    if (m_pLogFile && !m_op.fLoggedResponse)
        _LogResponse(NULL, 0);

    if (HTTP_STATUS_CREATED != m_op.dwHttpStatus)
    {
        _HrThunkConnectionError();
        hr = E_FAIL;
    }

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::AddCommonHeaders
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::AddCommonHeaders(void)
{
    HRESULT     hr = S_OK;
    CHAR        szHeader[CCHMAX_RES];

    if (!!(RH_ALLOWRENAME & m_op.dwRHFlags))
    {
        if (FAILED(hr = _AddRequestHeader(c_szAllowRenameHeader)))
            goto exit;
    }

    if (!!(RH_TRANSLATEFALSE & m_op.dwRHFlags))
    {
        if (FAILED(hr = _AddRequestHeader(c_szTranslateFalseHeader)))
            goto exit;
    }

    if (!!(RH_TRANSLATETRUE & m_op.dwRHFlags))
    {
        if (FAILED(hr = _AddRequestHeader(c_szTranslateTrueHeader)))
            goto exit;
    }

    if (!!(RH_XMLCONTENTTYPE & m_op.dwRHFlags))
    {
        IxpAssert(NULL == m_op.pszContentType);
        m_op.pszContentType = PszDupA(c_szXmlContentType);
        if (NULL == m_op.pszContentType)
        {
            hr = TrapError(E_OUTOFMEMORY);
            goto exit;
        }
        if (FAILED(hr = AddContentTypeHeader()))
            goto exit;
    }

    if (!!(RH_MESSAGECONTENTTYPE & m_op.dwRHFlags))
    {
        IxpAssert(NULL == m_op.pszContentType);
        m_op.pszContentType = PszDupA(c_szMailContentType);
        if (NULL == m_op.pszContentType)
        {
            hr = TrapError(E_OUTOFMEMORY);
            goto exit;
        }
        if (FAILED(hr = AddContentTypeHeader()))
            goto exit;
    }

    if (!!(RH_SMTPMESSAGECONTENTTYPE & m_op.dwRHFlags))
    {
        IxpAssert(NULL == m_op.pszContentType);
        m_op.pszContentType = PszDupA(c_szSmtpMessageContentType);
        if (NULL == m_op.pszContentType)
        {
            hr = TrapError(E_OUTOFMEMORY);
            goto exit;
        }
        if (FAILED(hr = AddContentTypeHeader()))
            goto exit;
    }

    if (!!(RH_BRIEF & m_op.dwRHFlags))
    {
        if (FAILED(hr = _AddRequestHeader(c_szBriefHeader)))
            goto exit;
    }

    if (!!(RH_SAVEINSENTTRUE & m_op.dwRHFlags))
    {
        FAIL_EXIT(hr = _AddRequestHeader(c_szSaveInSentTrue));
    }

    if (!!(RH_SAVEINSENTFALSE & m_op.dwRHFlags))
    {
        FAIL_EXIT(hr = _AddRequestHeader(c_szSaveInSentFalse));
    }

    if (!!(RH_ROOTTIMESTAMP & m_op.dwRHFlags))
    {
        wnsprintf(szHeader, ARRAYSIZE(szHeader), c_szRootTimeStampHeader, m_op.pszRootTimeStamp, m_op.pszFolderTimeStamp);

        FAIL_EXIT(hr = _AddRequestHeader(szHeader));
    }

    if (!!(RH_FOLDERTIMESTAMP & m_op.dwRHFlags))
    {
        wnsprintf(szHeader, ARRAYSIZE(szHeader), c_szFolderTimeStampHeader, m_op.pszFolderTimeStamp);

        FAIL_EXIT(hr = _AddRequestHeader(szHeader));

    }


    // Fix for 88820
    if (!!(RH_ADDCHARSET & m_op.dwRHFlags))
    {
        CODEPAGEINFO CodePageInfo;

        MimeOleGetCodePageInfo(CP_ACP, &CodePageInfo);

        *szHeader = 0;
        wnsprintf(szHeader, ARRAYSIZE(szHeader), c_szAcceptCharset, CodePageInfo.szWebCset);

        FAIL_EXIT(hr = _AddRequestHeader(szHeader));
    }
    // end  of fix

exit:
    return hr;
}
// CHTTPMailTransport::AddCharsetLine
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::AddCharsetLine(void)
{
    HRESULT     hr = S_OK;
    CHAR        szHeader[CCHMAX_RES];
	
	CODEPAGEINFO CodePageInfo;
	
	MimeOleGetCodePageInfo(CP_ACP, &CodePageInfo);
	
	*szHeader = 0;
	wnsprintf(szHeader, ARRAYSIZE(szHeader), c_szAcceptCharset, CodePageInfo.szWebCset);
	
	hr = _AddRequestHeader(szHeader);

    return hr;
}
// --------------------------------------------------------------------------------
// CHTTPMailTransport::AddDestinationHeader
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::AddDestinationHeader(void)
{
    HRESULT hr = S_OK;
    ULONG   cb = lstrlen(c_szDestinationHeader) + lstrlen(m_op.pszDestination) + sizeof(char);
    LPSTR   pszDestHeader = NULL;

    if (!MemAlloc((void **)&pszDestHeader, cb))
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    wsprintf(pszDestHeader, "%s%s", c_szDestinationHeader, m_op.pszDestination);
    hr = _AddRequestHeader(pszDestHeader);

exit:
    SafeMemFree(pszDestHeader);
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::AddContentTypeHeader
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::AddContentTypeHeader(void)
{
    HRESULT     hr = S_OK;
    ULONG       cb;
    LPSTR       pszContentTypeHeader = NULL;

    if (NULL == m_op.pszContentType)
        goto exit;

    cb = lstrlen(c_szContentTypeHeader) + lstrlen(m_op.pszContentType) + sizeof(CHAR);
    if (!MemAlloc((void **)&pszContentTypeHeader, cb))
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    wsprintf(pszContentTypeHeader, "%s%s", c_szContentTypeHeader, m_op.pszContentType);
    hr = _AddRequestHeader(pszContentTypeHeader);

exit:
    SafeMemFree(pszContentTypeHeader);
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ProcessLocationResponse
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ProcessLocationResponse(void)
{
    _GetRequestHeader(&m_op.rResponse.rCopyMoveInfo.pszLocation, HTTP_QUERY_LOCATION);
    return S_OK;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::InitBCopyMove
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::InitBCopyMove(void)
{
    HRESULT     hr = S_OK;

    // allocate a buffer to contain the response list
    if (!MemAlloc((void **)&m_op.rResponse.rBCopyMoveList.prgBCopyMove, 
        BCOPYMOVE_MAXRESPONSES * sizeof(HTTPMAILBCOPYMOVE)))
    {
        hr = TrapError(E_OUTOFMEMORY);
        goto exit;
    }

    ZeroMemory(m_op.rResponse.rBCopyMoveList.prgBCopyMove, BCOPYMOVE_MAXRESPONSES * sizeof(HTTPMAILBCOPYMOVE));

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::InitRootProps
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::InitRootProps(void)
{
    HRESULT     hr = S_OK;

    // it is possible to end up here, and have the root props
    // if the caller either forced the request to go async,
    // or queued up multiple requests for root props.
    if (GetHasRootProps())
    {
        // finalize the root props, and return an error.
        // this will generate the response to the caller,
        // and fall out of the fsm.
        FinalizeRootProps();
        hr = E_FAIL;
    }
    else
    {
        IxpAssert(NULL == m_op.pPropFindRequest);

        m_op.pPropFindRequest = new CPropFindRequest();
        if (NULL == m_op.pPropFindRequest)
        {
            hr = E_OUTOFMEMORY;
            goto exit;
        }

        hr = XP_CREATE_PROPFIND_REQUEST(ROOTPROPS, m_op.pPropFindRequest);
    }

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::FinalizeRootProps
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::FinalizeRootProps(void)
{
    HRESULT     hr = S_OK;

    m_fHasRootProps = TRUE;

    m_op.rResponse.rGetPropInfo.type = m_op.tyProp;

    if (m_op.tyProp != HTTPMAIL_PROP_MAXPOLLINGINTERVAL)
    {
        m_op.rResponse.rIxpResult.hrResult = GetProperty(m_op.tyProp, &m_op.rResponse.rGetPropInfo.pszProp);
    }
    else
    {
        m_op.rResponse.rIxpResult.hrResult = GetPropertyDw(m_op.tyProp, &m_op.rResponse.rGetPropInfo.dwProp);
    }

    hr = _HrThunkResponse(TRUE);
    SafeMemFree(m_op.rResponse.rGetPropInfo.pszProp);

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::InitMemberInfo
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::InitMemberInfo(void)
{
    HRESULT     hr = S_OK;

    IxpAssert(NULL == m_op.pPropFindRequest);

    // create the propfind request
    m_op.pPropFindRequest = new CPropFindRequest();
    if (NULL == m_op.pPropFindRequest)
    {
        hr = TrapError(E_OUTOFMEMORY);
        goto exit;
    }

    // always add the common properties
    FAIL_EXIT(hr = XP_CREATE_PROPFIND_REQUEST(HTTPMEMBERINFO_COMMON, m_op.pPropFindRequest));

    // if the client requested folder props, add that schema
    if (!!(m_op.dwMIFlags & HTTP_MEMBERINFO_FOLDERPROPS))
        FAIL_EXIT(hr = XP_CREATE_PROPFIND_REQUEST(HTTPMEMBERINFO_FOLDER, m_op.pPropFindRequest));

    // if the client requested message props, add that schema
    if (!!(m_op.dwMIFlags & HTTP_MEMBERINFO_MESSAGEPROPS))
        FAIL_EXIT(hr = XP_CREATE_PROPFIND_REQUEST(HTTPMEMBERINFO_MESSAGE, m_op.pPropFindRequest));

    // allocate a buffer to contain the response list
    if (!MemAlloc((void **)&m_op.rResponse.rMemberInfoList.prgMemberInfo,
        MEMBERINFO_MAXRESPONSES * sizeof(HTTPMEMBERINFO)))
    {
        hr = TrapError(E_OUTOFMEMORY);
        goto exit;
    }
    
    ZeroMemory(m_op.rResponse.rMemberInfoList.prgMemberInfo, MEMBERINFO_MAXRESPONSES * sizeof(HTTPMEMBERINFO));

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::InitMemberError
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::InitMemberError(void)
{
    HRESULT     hr = S_OK;

    // allocate a buffer to contain the response list
    if (!MemAlloc((void **)&m_op.rResponse.rMemberErrorList.prgMemberError,
        MEMBERERROR_MAXRESPONSES * sizeof(HTTPMEMBERERROR)))
    {
        hr = TrapError(E_OUTOFMEMORY);
        goto exit;
    }
    
    ZeroMemory(m_op.rResponse.rMemberErrorList.prgMemberError, MEMBERERROR_MAXRESPONSES * sizeof(HTTPMEMBERERROR));

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// InitListContacts
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::InitListContacts(void)
{
    HRESULT     hr = S_OK;

    IxpAssert(NULL == m_op.pPropFindRequest);

    m_op.pPropFindRequest = new CPropFindRequest();
    if (NULL == m_op.pPropFindRequest)
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    hr = XP_CREATE_PROPFIND_REQUEST(HTTPCONTACTID, m_op.pPropFindRequest);
    if (FAILED(hr))
        goto exit;

    // allocate a buffer to contain the response list
    if (!MemAlloc((void **)&m_op.rResponse.rContactIdList.prgContactId,
        LISTCONTACTS_MAXRESPONSES * sizeof(HTTPCONTACTID)))
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    ZeroMemory(m_op.rResponse.rContactIdList.prgContactId, LISTCONTACTS_MAXRESPONSES * sizeof(HTTPCONTACTID));

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::InitContactInfo
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::InitContactInfo(void)
{
    HRESULT     hr = S_OK;

    IxpAssert(NULL == m_op.pPropFindRequest);

    m_op.pPropFindRequest = new CPropFindRequest();
    if (NULL == m_op.pPropFindRequest)
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    hr = XP_CREATE_PROPFIND_REQUEST(HTTPCONTACTINFO, m_op.pPropFindRequest);
    if (FAILED(hr))
        goto exit;

    // allocate a buffer to contain the response list
    if (!MemAlloc((void **)&m_op.rResponse.rContactInfoList.prgContactInfo,
        CONTACTINFO_MAXRESPONSES * sizeof(HTTPCONTACTINFO)))
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    ZeroMemory(m_op.rResponse.rContactInfoList.prgContactInfo, CONTACTINFO_MAXRESPONSES * sizeof(HTTPCONTACTINFO));

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ProcessPostContactResponse
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ProcessPostContactResponse(void)
{
    HRESULT                 hr = S_OK;
    DWORD                   dwSize = MAX_PATH;
    LPSTR                   pszLocation = NULL;
    DWORD                   dwContext;
    int                     iState;

    if (m_pLogFile && !m_op.fLoggedResponse)
        _LogResponse(NULL, 0);

    if (HTTP_STATUS_CREATED != m_op.dwHttpStatus)
    {
        _HrThunkConnectionError();
        hr = E_FAIL;
        goto exit;
    }

    if (FAILED(hr = _GetRequestHeader(&pszLocation, HTTP_QUERY_LOCATION)))
        goto exit;

    // Prepare for the next phase

    // save the context and the state
    dwContext = m_op.dwContext;
    iState = m_op.iState;

    FreeOperation();

    // restore context, state, parsing funcs, etc.

    m_op.rResponse.command = HTTPMAIL_POSTCONTACT;
    m_op.pszUrl = pszLocation;
    pszLocation = NULL;
    m_op.dwContext = dwContext;

    m_op.pfnState = c_rgpfnPostContact;
    m_op.cState = ARRAYSIZE(c_rgpfnPostContact);
    m_op.iState = iState;
    m_op.pParseFuncs = c_rgpfnPostOrPatchContactParse;

    m_op.dwDepth = 0;
    m_op.dwRHFlags = (RH_XMLCONTENTTYPE | RH_BRIEF);

    m_op.rResponse.rPostContactInfo.pszHref = PszDupA(m_op.pszUrl);
    if (NULL == m_op.rResponse.rPostContactInfo.pszHref)
        hr = E_OUTOFMEMORY;

exit:
    SafeMemFree(pszLocation);
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ProcessPatchContactResponse
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ProcessPatchContactResponse(void)
{
    HRESULT             hr = S_OK;
    LPSTR               pszUrl = NULL;
    DWORD               dwContext;
    int                 iState;
    IHTTPMailCallback   *pCallback = NULL;

    // REVIEW: we should be handling multistatus responses
    if (200 > m_op.dwHttpStatus || 300 < m_op.dwHttpStatus)
    {
        _HrThunkConnectionError();
        hr = E_FAIL;
        goto exit;
    }

    // prepare for the next phase
    
    // save the context and the state
    pszUrl = m_op.pszUrl;
    m_op.pszUrl = NULL;
    dwContext = m_op.dwContext;
    iState = m_op.iState;

    FreeOperation();

    // restore context, etc.
    m_op.rResponse.command = HTTPMAIL_PATCHCONTACT;
    m_op.pszUrl = pszUrl;
    m_op.dwContext = dwContext;
    m_op.pfnState = c_rgpfnPatchContact;
    m_op.cState = ARRAYSIZE(c_rgpfnPatchContact);
    m_op.iState = iState;
    m_op.pParseFuncs = c_rgpfnPostOrPatchContactParse; // share the post contact parse funcs

    m_op.dwDepth = 0;
    m_op.rResponse.rPatchContactInfo.pszHref = PszDupA(m_op.pszUrl);

    m_op.dwRHFlags = (RH_BRIEF | RH_XMLCONTENTTYPE);

    pszUrl = NULL;

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// XML Parsing Callbacks
// --------------------------------------------------------------------------------

// --------------------------------------------------------------------------------
// CHTTPMailTransport::CreateElement
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::CreateElement(
                                        CXMLNamespace *pBaseNamespace,
                                        const WCHAR *pwcText, 
                                        ULONG ulLen, 
                                        ULONG ulNamespaceLen,
                                        BOOL fTerminal)
{
    // increment the stack pointer and, if there is room on the stack,
    // push the element type
    if (!fTerminal)
    {
        if (m_op.dwStackDepth < ELE_STACK_CAPACITY)
        {
            m_op.rgEleStack[m_op.dwStackDepth].ele = XMLElementToID(pwcText, ulLen, ulNamespaceLen, m_op.pTopNamespace);
            m_op.rgEleStack[m_op.dwStackDepth].pBaseNamespace = pBaseNamespace;
            m_op.rgEleStack[m_op.dwStackDepth].fBeganChildren = FALSE;
            m_op.rgEleStack[m_op.dwStackDepth].pTextBuffer = NULL;
        }

        ++m_op.dwStackDepth;
    }
    
    return S_OK;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::EndChildren
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::EndChildren(void)
{
    HRESULT hr = S_OK;

    // decrement the stack pointer
    if (m_op.dwStackDepth <= ELE_STACK_CAPACITY)
    {
        LPPCDATABUFFER pTextBuffer = m_op.rgEleStack[m_op.dwStackDepth - 1].pTextBuffer;

        if (pTextBuffer)
        {
            hr = (this->*(m_op.pParseFuncs->pfnHandleText))(pTextBuffer->pwcText, pTextBuffer->ulLen);
            _ReleaseTextBuffer(pTextBuffer);
        }
        else
            hr = (this->*(m_op.pParseFuncs->pfnHandleText))(NULL, 0);

        // unroll the namespace
        PopNamespaces(m_op.rgEleStack[m_op.dwStackDepth - 1].pBaseNamespace);
    }

    --m_op.dwStackDepth;

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::BCopyMove_HandleText
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::BCopyMove_HandleText(const WCHAR *pwcText, ULONG ulLen)
{
    HRESULT                 hr = S_OK;
    LPHTTPMAILBCOPYMOVE     pInfo = &m_op.rResponse.rBCopyMoveList.prgBCopyMove[m_op.rResponse.rBCopyMoveList.cBCopyMove];
    
    return XP_BIND_TO_STRUCT(HTTPMAILBCOPYMOVE, pwcText, ulLen, pInfo, NULL);
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::BCopyMove_EndChildren
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::BCopyMove_EndChildren(void)
{
    HRESULT     hr = S_OK;

    if (StackTop(HMELE_DAV_RESPONSE) && VALIDSTACK(c_rgPropFindResponseStack))
    {
        // clear the prop flags, since we are about to increment the count
        m_op.dwPropFlags = NOFLAGS;

        // increment the list count and, if we've hit the max, send the notification
        if (BCOPYMOVE_MAXRESPONSES == ++m_op.rResponse.rBCopyMoveList.cBCopyMove)
        {
            if (FAILED(hr = _HrThunkResponse(FALSE)))
                goto exit;
            FreeBCopyMoveList();
        }
    }

    hr = EndChildren();

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::PropFind_HandleText
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::PropFind_HandleText(const WCHAR *pwcText, ULONG ulLen)
{
    HRESULT         hr = S_OK;
    LPSTR           pszStatus = NULL;

    // the only element that is handled here is <status>
    if (StackTop(HMELE_DAV_STATUS) && VALIDSTACK(c_rgPropFindStatusStack))
    {
        m_op.fFoundStatus = TRUE;
        m_op.dwStatus = 0;

        if (SUCCEEDED(hr = AllocStrFromStrNW(pwcText, ulLen, &pszStatus)) && NULL != pszStatus)
        {        
            // ignore errors parsing the status...we treat malformed status
            // as status 0, which is an error
            HrParseHTTPStatus(pszStatus, &m_op.dwStatus);
        }
    }

    SafeMemFree(pszStatus);
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::RootProps_HandleText
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::RootProps_HandleText(const WCHAR *pwcText, ULONG ulLen)
{
    HRESULT hr = S_OK;
    BOOL    fWasBound = FALSE;

    hr = XP_BIND_TO_STRUCT(ROOTPROPS, pwcText, ulLen, &m_rootProps, &fWasBound);
    if (FAILED(hr))
        goto exit;

    if (!fWasBound)
        hr = PropFind_HandleText(pwcText, ulLen);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::RootProps_EndChildren
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::RootProps_EndChildren(void)
{
    // if we are popping a prop node with a bad status code,
    // free any data associated with the node
    if (StackTop(HMELE_DAV_PROPSTAT) && VALIDSTACK(c_rgPropFindPropStatStack))
    {
        if (!m_op.fFoundStatus || m_op.dwStatus != 200)
            XP_FREE_STRUCT(ROOTPROPS, &m_rootProps, &m_op.dwPropFlags);

        m_op.fFoundStatus = FALSE;
        m_op.dwPropFlags = NOFLAGS;
    }

    return EndChildren();
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::MemberInfo_HandleText
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::MemberInfo_HandleText(const WCHAR *pwcText, ULONG ulLen)
{
    HRESULT             hr = S_OK;
    LPHTTPMEMBERINFO    pInfo = &m_op.rResponse.rMemberInfoList.prgMemberInfo[m_op.rResponse.rMemberInfoList.cMemberInfo];
    BOOL                fWasBound = FALSE;

    FAIL_EXIT(hr = XP_BIND_TO_STRUCT(HTTPMEMBERINFO, pwcText, ulLen, pInfo, &fWasBound));

    if (!fWasBound)
        hr = PropFind_HandleText(pwcText, ulLen);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::MemberInfo_EndChildren
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::MemberInfo_EndChildren(void)
{
    HRESULT     hr = S_OK;

    // if we are popping a propstat node with a bad status code,
    // free any data associated with the node
    if (StackTop(HMELE_DAV_PROPSTAT) && VALIDSTACK(c_rgPropFindPropStatStack))
    {
        // grab a pointer to the folder info we are accumulating
        LPHTTPMEMBERINFO pInfo = 
                &m_op.rResponse.rMemberInfoList.prgMemberInfo[m_op.rResponse.rMemberInfoList.cMemberInfo];

        if (!m_op.fFoundStatus || m_op.dwStatus != 200)
            XP_FREE_STRUCT(HTTPMEMBERINFO, pInfo, &m_op.dwPropFlags);

        m_op.fFoundStatus = FALSE;
        m_op.dwPropFlags = NOFLAGS;
    }
    else if (StackTop(HMELE_DAV_RESPONSE) && VALIDSTACK(c_rgPropFindResponseStack))
    {
        // increment the list count and, if we've hit the max, send the notification
        if (MEMBERINFO_MAXRESPONSES == ++m_op.rResponse.rMemberInfoList.cMemberInfo)
        {
            if (FAILED(hr = _HrThunkResponse(FALSE)))
                goto exit;
            FreeMemberInfoList();
        }
    }

    hr = EndChildren();

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::MemberError_HandleText
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::MemberError_HandleText(const WCHAR *pwcText, ULONG ulLen)
{
    HRESULT             hr = S_OK;
    LPHTTPMEMBERERROR   pInfo = &m_op.rResponse.rMemberErrorList.prgMemberError[m_op.rResponse.rMemberErrorList.cMemberError];
    BOOL                fWasBound = FALSE;

    FAIL_EXIT(hr = XP_BIND_TO_STRUCT(HTTPMEMBERERROR, pwcText, ulLen, pInfo, &fWasBound));

    if (!fWasBound)
        hr = PropFind_HandleText(pwcText, ulLen);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::MemberError_EndChildren
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::MemberError_EndChildren(void)
{
    HRESULT     hr = S_OK;

    // if we are popping a propstat node with a bad status code,
    // free any data associated with the node
    if (StackTop(HMELE_DAV_PROPSTAT) && VALIDSTACK(c_rgPropFindPropStatStack))
    {
        // grab a pointer to the folder info we are accumulating
        LPHTTPMEMBERERROR pInfo = 
                &m_op.rResponse.rMemberErrorList.prgMemberError[m_op.rResponse.rMemberErrorList.cMemberError];

        if (!m_op.fFoundStatus || m_op.dwStatus != 200)
            XP_FREE_STRUCT(HTTPMEMBERERROR, pInfo, &m_op.dwPropFlags);

        m_op.fFoundStatus = FALSE;
        m_op.dwPropFlags = NOFLAGS;
    }
    else if (StackTop(HMELE_DAV_RESPONSE) && VALIDSTACK(c_rgPropFindResponseStack))
    {
        // increment the list count and, if we've hit the max, send the notification
        if (MEMBERERROR_MAXRESPONSES == ++m_op.rResponse.rMemberErrorList.cMemberError)
        {
            if (FAILED(hr = _HrThunkResponse(FALSE)))
                goto exit;
            FreeMemberErrorList();
        }
    }

    hr = EndChildren();

exit:
    return hr;
}


// --------------------------------------------------------------------------------
// CHTTPMailTransport::ListContacts_HandleText
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ListContacts_HandleText(const WCHAR *pwcText, ULONG ulLen)
{
    HRESULT         hr = S_OK;
    LPHTTPCONTACTID pId = &m_op.rResponse.rContactIdList.prgContactId[m_op.rResponse.rContactIdList.cContactId];
    BOOL            fWasBound = FALSE;

    hr = XP_BIND_TO_STRUCT(HTTPCONTACTID, pwcText, ulLen, pId, &fWasBound);
    if (FAILED(hr))
        goto exit;

    if (!fWasBound)
        hr = PropFind_HandleText(pwcText, ulLen);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ListContacts_EndChildren
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ListContacts_EndChildren(void)
{
    HRESULT hr = S_OK;

    // if we are popping a propstat node with a bad status code,
    // free any data associated with the node
    if (StackTop(HMELE_DAV_PROPSTAT) && VALIDSTACK(c_rgPropFindPropStatStack))
    {
        // grab a pointer to the contact id we are accumulating
        LPHTTPCONTACTID pId = &m_op.rResponse.rContactIdList.prgContactId[m_op.rResponse.rContactIdList.cContactId];

        if (!m_op.fFoundStatus || m_op.dwStatus != 200)
            XP_FREE_STRUCT(HTTPCONTACTID, pId, &m_op.dwPropFlags);

        m_op.fFoundStatus = FALSE;
        m_op.dwPropFlags = NOFLAGS;
    }
    else if (StackTop(HMELE_DAV_RESPONSE) && VALIDSTACK(c_rgPropFindResponseStack))
    {
        // increment the list count and, if we've hit the max, send the notification
        if (LISTCONTACTS_MAXRESPONSES == ++m_op.rResponse.rContactIdList.cContactId)
        {
            if (FAILED(hr = _HrThunkResponse(FALSE)))
                goto exit;
            FreeContactIdList();
        }
    }

    hr = EndChildren();

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ContactInfo_HandleText
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ContactInfo_HandleText(const WCHAR *pwcText, ULONG ulLen)
{
    HRESULT             hr = S_OK;
    LPHTTPCONTACTINFO   pInfo = &m_op.rResponse.rContactInfoList.prgContactInfo[m_op.rResponse.rContactInfoList.cContactInfo];
    BOOL                fWasBound = FALSE;

    hr = XP_BIND_TO_STRUCT(HTTPCONTACTINFO, pwcText, ulLen, pInfo, &fWasBound);
    if (FAILED(hr))
        goto exit;

    if (!fWasBound)
        hr = PropFind_HandleText(pwcText, ulLen);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::ContactInfo_EndChildren
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::ContactInfo_EndChildren()
{
    HRESULT hr = S_OK;

    // if we are popping a propstat node with a bad status code,
    // free any data associated with the node
    if (StackTop(HMELE_DAV_PROPSTAT) && VALIDSTACK(c_rgPropFindPropStatStack))
    {
        // grab a pointer to the contact id we are accumulating
        LPHTTPCONTACTINFO pInfo = &m_op.rResponse.rContactInfoList.prgContactInfo[m_op.rResponse.rContactInfoList.cContactInfo];

        if (!m_op.fFoundStatus || m_op.dwStatus != 200)
            XP_FREE_STRUCT(HTTPCONTACTINFO, pInfo, &m_op.dwPropFlags);
 
        m_op.fFoundStatus = FALSE;
        m_op.dwPropFlags = NOFLAGS;
    }
    else if (StackTop(HMELE_DAV_RESPONSE) && VALIDSTACK(c_rgPropFindResponseStack))
    {
        // increment the list count and, if we've hit the max, send the notification
        if (CONTACTINFO_MAXRESPONSES == ++m_op.rResponse.rContactInfoList.cContactInfo)
        {
            if (FAILED(hr = _HrThunkResponse(FALSE)))
                goto exit;
            FreeContactInfoList();
        }
    }

    hr = EndChildren();

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::PostOrPatchContact_HandleText
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::PostOrPatchContact_HandleText(const WCHAR *pwcText, ULONG ulLen)
{
    HRESULT         hr = S_OK;
    LPHTTPCONTACTID pId = NULL;
    BOOL            fWasBound = FALSE;

    if (HTTPMAIL_POSTCONTACT == m_op.rResponse.command)
        pId = &m_op.rResponse.rPostContactInfo;
    else if (HTTPMAIL_PATCHCONTACT == m_op.rResponse.command)
        pId = &m_op.rResponse.rPatchContactInfo;

    IxpAssert(pId);

    hr = XP_BIND_TO_STRUCT(HTTPCONTACTID, pwcText, ulLen, pId, &fWasBound);
    if (FAILED(hr))
        goto exit;

    if (!fWasBound)
        hr = PropFind_HandleText(pwcText, ulLen);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::PostOrPatchContact_EndChildren
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::PostOrPatchContact_EndChildren(void)
{
    HRESULT hr = S_OK;

    // if we are popping a propstat node with a bad status code,
    // free any data associated with the node
    if (StackTop(HMELE_DAV_PROPSTAT) && VALIDSTACK(c_rgPropFindPropStatStack))
    {
        // grab a pointer to the contact id we are accumulating
        LPHTTPCONTACTID pId = NULL;
        
        if (HTTPMAIL_POSTCONTACT == m_op.rResponse.command)
            pId = &m_op.rResponse.rPostContactInfo;
        else if (HTTPMAIL_PATCHCONTACT == m_op.rResponse.command)
            pId = &m_op.rResponse.rPatchContactInfo;

        IxpAssert(pId);

        if (!m_op.fFoundStatus || m_op.dwStatus != 200)
            XP_FREE_STRUCT(HTTPCONTACTID, pId, &m_op.dwPropFlags);

        m_op.fFoundStatus = FALSE;
        m_op.dwPropFlags = NOFLAGS;
    }

    hr = EndChildren();

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_MemberInfo2
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::_MemberInfo2(LPCSTR            pszPath, 
                                         MEMBERINFOFLAGS   flags, 
                                         DWORD             dwDepth,
                                         BOOL              fIncludeRoot,
                                         DWORD             dwContext,
                                         LPHTTPQUEUEDOP    *ppOp)
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp = NULL;

    if (!ppOp)
    {
        IF_FAILEXIT(hr = E_INVALIDARG);
    }

    if (NULL == pszPath)
        return TrapError(E_INVALIDARG);

    FAIL_CREATEWND;

#pragma prefast(suppress:11, "noise")
    *ppOp = NULL;

    if (FAILED(hr = AllocQueuedOperation(pszPath, NULL, 0, &pOp)))
        goto exit;

    pOp->command        = HTTPMAIL_MEMBERINFO;
    pOp->dwMIFlags      = flags;
    pOp->dwDepth        = dwDepth;
    pOp->dwContext      = dwContext;
    pOp->pfnState       = c_rgpfnMemberInfo;
    pOp->cState         = ARRAYSIZE(c_rgpfnMemberInfo);
    pOp->pParseFuncs    = c_rgpfnMemberInfoParse;

    pOp->dwRHFlags = (RH_BRIEF | RH_XMLCONTENTTYPE);
    if (!fIncludeRoot)
        pOp->dwRHFlags |= RH_NOROOT;


exit:
    if (SUCCEEDED(hr))
    {
#pragma prefast(suppress:11, "noise")
        *ppOp = pOp;
    }

    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::RootMemberInfo
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::RootMemberInfo(LPCSTR                  pszPath,
                                                MEMBERINFOFLAGS         flags,
                                                DWORD                   dwDepth,
                                                BOOL                    fIncludeRoot,
                                                DWORD                   dwContext,
                                                LPSTR                   pszRootTimeStamp,
                                                LPSTR                   pszInboxTimeStamp)                  
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp;

    IF_FAILEXIT(hr = _MemberInfo2(pszPath, flags, dwDepth, fIncludeRoot, dwContext, &pOp));

    pOp->dwRHFlags |= RH_ROOTTIMESTAMP | RH_ADDCHARSET;
    pOp->pszRootTimeStamp   = PszDupA(pszRootTimeStamp);
    pOp->pszFolderTimeStamp = PszDupA(pszInboxTimeStamp);

    QueueOperation(pOp);

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::FolderMemberInfo
// --------------------------------------------------------------------------------
STDMETHODIMP CHTTPMailTransport::FolderMemberInfo(LPCSTR                  pszPath,
                                                  MEMBERINFOFLAGS         flags,
                                                  DWORD                   dwDepth,
                                                  BOOL                    fIncludeRoot,
                                                  DWORD                   dwContext,
                                                  LPSTR                   pszFolderTimeStamp,
                                                  LPSTR                   pszFolderName)
{
    HRESULT             hr = S_OK;
    LPHTTPQUEUEDOP      pOp;

    IF_FAILEXIT(hr = _MemberInfo2(pszPath, flags, dwDepth, fIncludeRoot, dwContext, &pOp));

    pOp->dwRHFlags              |= RH_FOLDERTIMESTAMP | RH_ADDCHARSET;
    pOp->pszFolderTimeStamp      = PszDupA(pszFolderTimeStamp);

    // To be used when we do timestamping on every folder. Right now the default is inbox
    //pOp->pszFolderName           = PszDupA(pszFolderName);

    QueueOperation(pOp);

exit:
    return hr;
}

HRESULT CHTTPMailTransport::_HrParseAndCopy(LPCSTR pszToken, LPSTR *ppszDest, LPSTR lpszSrc)
{
    LPSTR   lpszBeginning = lpszSrc;
    LPSTR   lpszEnd;
    DWORD   dwCount = 0;
    HRESULT hr = E_FAIL;    
    int cchSize;

    lpszBeginning = StrStr(lpszSrc, pszToken);
    if (!lpszBeginning)
        goto exit;

    lpszBeginning = StrChr(lpszBeginning, '=');
    if (!lpszBeginning)
        goto exit;

    // Skip the equal sign
    ++lpszBeginning;

    SkipWhitespace(lpszBeginning, &dwCount);
    lpszBeginning += dwCount;

    lpszEnd = StrChr(lpszBeginning, ',');

    if (!lpszEnd)
    {
        //Its possible that this token is at the end. So use the remaining string.
        //Lets take a look at the length and make sure that it doesn't fall off the deep end.
        lpszEnd = lpszBeginning + strlen(lpszBeginning);
    }

    AssertSz(((lpszEnd - lpszBeginning + 1) < 20), "This number looks awfully long, please make sure that this is correct")

    cchSize = (int)(lpszEnd - lpszBeginning + 2);
    if (!MemAlloc((void**)ppszDest, cchSize))
        goto exit;

    cchSize = (int)(lpszEnd - lpszBeginning + 1);
    StrCpyN(*ppszDest, lpszBeginning, cchSize);

    // Null terminate it
    *(*ppszDest + (lpszEnd - lpszBeginning + 1)) = 0;

    hr = S_OK;

exit:
    return hr;
}

// --------------------------------------------------------------------------------
// CHTTPMailTransport::_GetTimestampHeader
// --------------------------------------------------------------------------------
HRESULT CHTTPMailTransport::_HrGetTimestampHeader(LPSTR *ppszHeader)
{
    HRESULT     hr        = S_OK;
    DWORD       dwSize    = MAX_PATH;
    LPSTR       pszHeader = NULL;

    Assert(NULL != ppszHeader);
    *ppszHeader = NULL;

retry:
    if (!MemAlloc((void **)&pszHeader, dwSize))
    {
        hr = E_OUTOFMEMORY;
        goto exit;
    }

    StrCpyN(pszHeader, c_szXTimestamp, dwSize);

    if (!HttpQueryInfo(m_op.hRequest, HTTP_QUERY_RAW_HEADERS | HTTP_QUERY_CUSTOM, pszHeader, &dwSize, NULL))
    {
        if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
        {
            hr = E_FAIL;
            goto exit;
        }

        SafeMemFree(pszHeader);        
        goto retry;
    }

    *ppszHeader = pszHeader;
    pszHeader   = NULL;

exit:
    SafeMemFree(pszHeader);
    return hr;
}