// -------------------------------------------------------------------------------- // 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 #include #include "oleutil.h" #include "ixputil.h" #include extern HRESULT HrGetLastError(void); 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(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(rgHTTPMailDictionary[g_rgContactEle[dwIndex]].pszName), pszEsc); SafeMemFree(pszEsc); if (FAILED(hr)) goto exit; } else { if (FAILED(hr = pRequest->RemoveProperty(DAVNAMESPACE_CONTACTS, const_cast(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(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(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 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: pszHeader = (LPSTR)ZeroAllocate(dwSize); if (!pszHeader) { 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 = HrGetLastError(); 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(&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(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) StrCpyN(szDepthHeader, c_szDepthInfinityNoRootHeader, ARRAYSIZE(szDepthHeader)); else wnsprintf(szDepthHeader, ARRAYSIZE(szDepthHeader), c_szDepthNoRootHeader, m_op.dwDepth); } else { if (DEPTH_INFINITY == m_op.dwDepth) StrCpyN(szDepthHeader, c_szDepthInfinityHeader, ARRAYSIZE(szDepthHeader)); else wnsprintf(szDepthHeader, ARRAYSIZE(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 cchSize = (lstrlen(c_szDestinationHeader) + lstrlen(m_op.pszDestination) + 1); LPSTR pszDestHeader = NULL; if (!MemAlloc((void **)&pszDestHeader, cchSize * sizeof(pszDestHeader[0]))) { hr = E_OUTOFMEMORY; goto exit; } wnsprintf(pszDestHeader, cchSize, "%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 cchSize; LPSTR pszContentTypeHeader = NULL; if (NULL == m_op.pszContentType) goto exit; cchSize = lstrlen(c_szContentTypeHeader) + lstrlen(m_op.pszContentType) + 1; if (!MemAlloc((void **)&pszContentTypeHeader, cchSize * sizeof(pszContentTypeHeader[0]))) { hr = E_OUTOFMEMORY; goto exit; } wnsprintf(pszContentTypeHeader, cchSize, "%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 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; }