You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1399 lines
32 KiB
1399 lines
32 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
xplogon.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains the XPLOGON class implementation.
|
|
|
|
Author:
|
|
|
|
Wesley Witt (wesw) 13-Aug-1996
|
|
|
|
--*/
|
|
|
|
#include "faxxp.h"
|
|
#include "debugex.h"
|
|
#pragma hdrstop
|
|
|
|
#include <mbstring.h>
|
|
|
|
LPSTR gszFAXAddressType = FAX_ADDRESS_TYPE_A;
|
|
LPSTR *gpszXPAddressTypes;
|
|
|
|
LPSTR ConvertTStringToAString(LPCTSTR lpctstrSource);
|
|
|
|
|
|
CXPLogon::CXPLogon(
|
|
HINSTANCE hInstance,
|
|
LPMAPISUP pSupObj,
|
|
LPTSTR ProfileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor of the object. Parameters are passed to initialize the
|
|
data members with the appropiate values.
|
|
|
|
Arguments:
|
|
|
|
hInstance - Instance of the provider DLL
|
|
pSupObj - Pointer to IMAPISupport object used in
|
|
CXPLogon methods
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DBG_ENTER(TEXT("CXPLogon::CXPLogon"));
|
|
|
|
m_cRef = 1;
|
|
m_hInstance = hInstance;
|
|
m_pSupObj = pSupObj;
|
|
m_fABWDSInstalled = FALSE;
|
|
|
|
m_ulTransportStatus = 0;
|
|
|
|
m_pSupObj->AddRef();
|
|
}
|
|
|
|
|
|
CXPLogon::~CXPLogon()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor of CXPLogon. Releases memory allocated for internal
|
|
properties during the life of this transport logon object.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DBG_ENTER(TEXT("CXPLogon::~CXPLogon"));
|
|
|
|
// Release the IMAPISupport object
|
|
m_pSupObj->Release();
|
|
m_pSupObj = NULL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::QueryInterface(
|
|
REFIID riid,
|
|
LPVOID * ppvObj
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a pointer to a interface requested if the interface is
|
|
supported and implemented by this object. If it is not supported, it
|
|
returns NULL.
|
|
|
|
Arguments:
|
|
|
|
Refer to OLE Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
// OLE requires NULLing parameter
|
|
*ppvObj = NULL;
|
|
// If this is one of the two IID return an interface pointer to it
|
|
if (riid == IID_IXPLogon || riid == IID_IUnknown)
|
|
{
|
|
*ppvObj = (LPVOID)this;
|
|
// Increase usage count of this object
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
// This object does not support the interface requested
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::AddressTypes(
|
|
ULONG * pulFlags,
|
|
ULONG * pcAdrType,
|
|
LPTSTR ** pppAdrTypeArray,
|
|
ULONG * pcMAPIUID,
|
|
LPMAPIUID ** pppMAPIUIDArray
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by the MAPI Spooler when initializing this XP logon object to
|
|
allow the transport to register the address it will handle.
|
|
|
|
Arguments:
|
|
|
|
Refer to OLE Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
S_OK always
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DBG_ENTER(TEXT("CXPLogon::AddressTypes"),hr);
|
|
|
|
*pcAdrType = 1;
|
|
*pulFlags = 0;
|
|
gpszXPAddressTypes = &gszFAXAddressType;
|
|
*pppAdrTypeArray = (LPTSTR*)gpszXPAddressTypes;
|
|
*pcMAPIUID = 0;
|
|
*pppMAPIUIDArray = NULL;
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::RegisterOptions(
|
|
ULONG * pulFlags,
|
|
ULONG * pcOptions,
|
|
LPOPTIONDATA * ppOptions
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This transport does not registers any per-recipient or per-message
|
|
option processing, so we return 0 options. And NULL in the OPTIONDATA
|
|
structure pointer.
|
|
|
|
Arguments:
|
|
|
|
Refer to OLE Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hResult = S_OK;
|
|
DBG_ENTER(TEXT("CXPLogon::RegisterOptions"),hResult);
|
|
|
|
*pulFlags = 0;
|
|
*pcOptions = 0;
|
|
*ppOptions = NULL;
|
|
return hResult;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::InitializeStatusRow(
|
|
ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
To initialize or modify the status properties of a CXPLogon
|
|
object. This function allocates an array with NUM_STATUS_ROW_PROPS
|
|
properties and initializes them.
|
|
|
|
Arguments:
|
|
|
|
ulFlags - 0 if the properties are being created the first time.
|
|
MODIFY_FLAGS if a change is being made to the properties
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hResult = S_OK;
|
|
DBG_ENTER(TEXT("CXPLogon::InitializeStatusRow"),hResult);
|
|
|
|
#define NUM_STATUS_ROW_PROPS 7
|
|
SPropValue spvStatusRow[NUM_STATUS_ROW_PROPS] = { 0 };
|
|
ULONG i = 0;
|
|
|
|
//
|
|
// Set the PR_PROVIDER_DISPLAY property: The transport readable name
|
|
//
|
|
spvStatusRow[i].ulPropTag = PR_PROVIDER_DISPLAY_A;
|
|
spvStatusRow[i++].Value.lpszA = TRANSPORT_DISPLAY_NAME_STRING;
|
|
|
|
//
|
|
// Set the PR_RESOURCE_METHODS property. These are the methods implemented
|
|
// in the our IMAPIStatus implementation (CMAPIStatus class.)
|
|
//
|
|
spvStatusRow[i].ulPropTag = PR_RESOURCE_METHODS;
|
|
//
|
|
// we support ALL the methods in our implementation of IMAPIStatus interface (except the WRITABLE ones)
|
|
//
|
|
spvStatusRow[i++].Value.l = STATUS_SETTINGS_DIALOG |
|
|
STATUS_FLUSH_QUEUES |
|
|
STATUS_VALIDATE_STATE;
|
|
|
|
//
|
|
// Set the PR_STATUS_CODE property.
|
|
//
|
|
spvStatusRow[i].ulPropTag = PR_STATUS_CODE;
|
|
spvStatusRow[i++].Value.l = GetTransportStatusCode();
|
|
|
|
//
|
|
// Set the PR_STATUS_STRING property
|
|
//
|
|
TCHAR szStatus[64];
|
|
char* pcStatus = NULL;
|
|
LoadStatusString (szStatus, ARR_SIZE(szStatus));
|
|
spvStatusRow[i].ulPropTag = PR_STATUS_STRING_A;
|
|
spvStatusRow[i++].Value.lpszA = pcStatus = ConvertTStringToAString(szStatus);
|
|
if(!pcStatus)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
//
|
|
// Set the PR_DISPLAY_NAME property
|
|
//
|
|
spvStatusRow[i].ulPropTag = PR_DISPLAY_NAME_A;
|
|
spvStatusRow[i++].Value.lpszA = TRANSPORT_DISPLAY_NAME_STRING;
|
|
|
|
//
|
|
// Set the PR_REMOTE_PROGRESS property
|
|
//
|
|
spvStatusRow[i].ulPropTag = PR_REMOTE_PROGRESS;
|
|
spvStatusRow[i++].Value.l = -1; // Not initialized
|
|
|
|
//
|
|
// Set the PR_REMOTE_VALIDATE_OK property
|
|
//
|
|
spvStatusRow[i].ulPropTag = PR_REMOTE_VALIDATE_OK;
|
|
spvStatusRow[i++].Value.b = TRUE;
|
|
|
|
//
|
|
// Write the entries on the provider's session status row
|
|
//
|
|
hResult = m_pSupObj->ModifyStatusRow (i, spvStatusRow, ulFlags);
|
|
|
|
MemFree(pcStatus);
|
|
return hResult;
|
|
}
|
|
|
|
|
|
VOID WINAPI
|
|
CXPLogon::UpdateStatus(
|
|
BOOL fAddValidate,
|
|
BOOL fValidateOkState
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Updates the transport status row of this transport in the MAPI Mail
|
|
subsystem. Updates the flags according the internal state flags
|
|
maintained in status code of the transport and loads a readable status
|
|
string to reset the status row. The caller of this method should update
|
|
the status code member variable prior to calling UpdateStatus()
|
|
|
|
Arguments:
|
|
|
|
fAddValidate
|
|
fValidateOkState
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hResult = S_OK;
|
|
DBG_ENTER(TEXT("CXPLogon::UpdateStatus"),hResult);
|
|
|
|
ULONG cProps = 1;
|
|
SPropValue rgProps[1] = { 0 };
|
|
|
|
rgProps[0].ulPropTag = PR_STATUS_CODE;
|
|
rgProps[0].Value.l = GetTransportStatusCode();
|
|
|
|
hResult = m_pSupObj->ModifyStatusRow( cProps, rgProps, STATUSROW_UPDATE );
|
|
}
|
|
|
|
|
|
BOOL WINAPI
|
|
CXPLogon::LoadStatusString(
|
|
OUT LPTSTR pString,
|
|
IN UINT uStringSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Loads a string from the transport's stringtable. This method is called
|
|
by the CXPLogon::UpdateStatus method when updating a status row. This
|
|
method loads the string based on the status bits of the transport
|
|
status code
|
|
|
|
Arguments:
|
|
|
|
pString - Pointer to a string which will hold the status string
|
|
uStringSize - Maximum number of characters allowed in the string
|
|
|
|
Return Value:
|
|
|
|
TRUE - If the string was found in the string table.
|
|
FALSE - The string was not found. The String indicated by
|
|
pString is set to hold 0 characters
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL bRet = TRUE;
|
|
DBG_ENTER(TEXT("CXPLogon::LoadStatusString"),bRet);
|
|
|
|
//
|
|
//Make sure we have a terminting NULL
|
|
//
|
|
pString[uStringSize-1] = '\0';
|
|
|
|
//
|
|
//Copy the string acroding to the size of uStringSize
|
|
//
|
|
_tcsncpy( pString, _T("Status String"), uStringSize-1);
|
|
return bRet;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::TransportNotify(
|
|
ULONG * pulFlags,
|
|
LPVOID * ppvData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update the status row registered by this transport with MAPI.
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hResult = S_OK;
|
|
DBG_ENTER(TEXT("CXPLogon::TransportNotify"),hResult,TEXT("ulFlags=%d"),*pulFlags);
|
|
|
|
ULONG ulOldStatus = GetTransportStatusCode();
|
|
|
|
if (*pulFlags & NOTIFY_BEGIN_INBOUND)
|
|
{
|
|
RemoveStatusBits( STATUS_INBOUND_ENABLED );
|
|
}
|
|
if (*pulFlags & NOTIFY_END_INBOUND)
|
|
{
|
|
RemoveStatusBits( STATUS_INBOUND_ENABLED );
|
|
}
|
|
if (*pulFlags & NOTIFY_BEGIN_OUTBOUND)
|
|
{
|
|
AddStatusBits( STATUS_OUTBOUND_ENABLED );
|
|
}
|
|
if (*pulFlags & NOTIFY_END_OUTBOUND)
|
|
{
|
|
RemoveStatusBits( STATUS_OUTBOUND_ENABLED );
|
|
}
|
|
if (*pulFlags & NOTIFY_BEGIN_OUTBOUND_FLUSH)
|
|
{
|
|
m_pSupObj->SpoolerNotify( NOTIFY_SENTDEFERRED, NULL );
|
|
}
|
|
if (*pulFlags & NOTIFY_END_OUTBOUND_FLUSH)
|
|
{
|
|
RemoveStatusBits( STATUS_OUTBOUND_FLUSH );
|
|
}
|
|
if (*pulFlags & NOTIFY_END_INBOUND_FLUSH)
|
|
{
|
|
RemoveStatusBits( STATUS_INBOUND_FLUSH );
|
|
}
|
|
|
|
if (ulOldStatus != GetTransportStatusCode())
|
|
{
|
|
UpdateStatus();
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::Idle(
|
|
ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stub method. We should not get called here, because we told
|
|
the spooler not to call us here.
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
S_OK always.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// We should not get called here, because we told
|
|
// the spooler not to call us here.
|
|
//
|
|
DBG_ENTER(TEXT("CXPLogon::Idle"));
|
|
|
|
Assert(false);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::TransportLogoff(
|
|
ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is called by the spooler when the transport should do final
|
|
arragements before it gets released.
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hResult = S_OK;
|
|
DBG_ENTER(TEXT("CXPLogon::TransportLogoff"),hResult,TEXT("ulFlags=%d"),ulFlags);
|
|
|
|
//
|
|
// We should attempt to remove the transport's status row from
|
|
// the system
|
|
//
|
|
hResult = m_pSupObj->ModifyStatusRow (0, NULL, 0);
|
|
|
|
if (S_OK != hResult)
|
|
{
|
|
CALL_FAIL (GENERAL_ERR, TEXT("ModifyStatusRow"), hResult);
|
|
|
|
//
|
|
// Don't fail the call
|
|
//
|
|
hResult = S_OK;
|
|
}
|
|
return hResult;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::SubmitMessage(
|
|
ULONG ulFlags,
|
|
LPMESSAGE pMsgObj,
|
|
ULONG * pulMsgRef,
|
|
ULONG * pulReturnParm
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is called by the spooler when a client submits a
|
|
message to a recipient whose address type this transport handles.
|
|
The spooler calls this method twice for each deferred message.
|
|
The first time (before the delivery time) when the message is
|
|
submitted by the client, we simply return. The message is then queued
|
|
by the spooler for later delivery. We keep track of when it's time
|
|
to send deferred messages.
|
|
|
|
The second time we're called, the state variable will be 'READY' and
|
|
we go ahead and start the actual transmission. While we're in the
|
|
body of this function, the implied state is 'SENDING'
|
|
|
|
If the client logs out of this session, any pending messages get
|
|
queued again the next time it logs in.
|
|
|
|
In this transport we get a recipient table, we restrict the table for
|
|
unmarked recipients. After the table is ready we invoke a helper
|
|
method to do the actual transmission.
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hResult = S_OK;
|
|
LPADRLIST pOurAdrList = NULL;
|
|
ULONG ulRow, ulRecipCount = 0;
|
|
LPSPropValue pProps;
|
|
FILETIME ft;
|
|
SYSTEMTIME st;
|
|
BOOL bSentSuccessfully;
|
|
ULONG cValues;
|
|
LPSPropValue pMsgProps = NULL;
|
|
BOOL NeedDeliveryReport;
|
|
TCHAR szHeaderText[1024] = {0};
|
|
LPSTREAM lpstmT = NULL;
|
|
DWORD dwRslt;
|
|
TCHAR ErrorText[1024] = {0};
|
|
TCHAR FailedText[1024] = {0};
|
|
BOOL UseRichText = FALSE;
|
|
LPMAPITABLE AttachmentTable = NULL;
|
|
LPSRowSet pAttachmentRows = NULL;
|
|
LPMAPITABLE pTable = NULL;
|
|
LPSRowSet pRecipRows = NULL;
|
|
|
|
SPropValue propResponsibility = {0};
|
|
SPropValue propAddrType = {0};
|
|
SRestriction RestrictAnd[2] = {0};
|
|
SRestriction Restriction = {0};
|
|
DWORD dwRowCount = 0;
|
|
DWORD dwRecipientsLimit = 0;
|
|
|
|
DBG_ENTER(TEXT("CXPLogon::SubmitMessage"),hResult,TEXT("ulFlags=%d"),ulFlags);
|
|
|
|
CheckSpoolerYield( TRUE );
|
|
|
|
//
|
|
// Get the recipient table from the message
|
|
//
|
|
hResult = pMsgObj->GetRecipientTable( FALSE , &pTable );
|
|
if (FAILED(hResult))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// The spooler marks all the message recipients this transport has to
|
|
// handle with PR_RESPONSIBILITY set to FALSE
|
|
// and PR_ADDRTYPE_A is FAX
|
|
//
|
|
propResponsibility.ulPropTag = PR_RESPONSIBILITY;
|
|
propResponsibility.Value.b = FALSE;
|
|
|
|
propAddrType.ulPropTag = PR_ADDRTYPE_A;
|
|
propAddrType.Value.lpszA = FAX_ADDRESS_TYPE_A;
|
|
|
|
RestrictAnd[0].rt = RES_PROPERTY;
|
|
RestrictAnd[0].res.resProperty.relop = RELOP_EQ;
|
|
RestrictAnd[0].res.resProperty.ulPropTag = PR_RESPONSIBILITY;
|
|
RestrictAnd[0].res.resProperty.lpProp = &propResponsibility;
|
|
|
|
RestrictAnd[1].rt = RES_PROPERTY;
|
|
RestrictAnd[1].res.resProperty.relop = RELOP_EQ;
|
|
RestrictAnd[1].res.resProperty.ulPropTag = PR_ADDRTYPE_A;
|
|
RestrictAnd[1].res.resProperty.lpProp = &propAddrType;
|
|
|
|
Restriction.rt = RES_AND;
|
|
Restriction.res.resAnd.cRes = 2;
|
|
Restriction.res.resAnd.lpRes = RestrictAnd;
|
|
|
|
hResult = pTable->Restrict( &Restriction, 0 );
|
|
if (FAILED(hResult))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
hResult = pTable->GetRowCount(0, &dwRowCount);
|
|
if (FAILED(hResult))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if(0 == dwRowCount)
|
|
{
|
|
//
|
|
// There are no fax recipients
|
|
//
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Let the MAPI spooler do other things
|
|
//
|
|
CheckSpoolerYield();
|
|
|
|
hResult = HrAddColumns(
|
|
pTable,
|
|
(LPSPropTagArray) &sptRecipTable,
|
|
gpfnAllocateBuffer,
|
|
gpfnFreeBuffer
|
|
);
|
|
if (FAILED(hResult))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
hResult = HrQueryAllRows(
|
|
pTable,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
&pRecipRows
|
|
);
|
|
if (FAILED(hResult))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Let the MAPI spooler do other things
|
|
//
|
|
CheckSpoolerYield();
|
|
|
|
//
|
|
//Get the message properties
|
|
//
|
|
hResult = pMsgObj->GetProps(
|
|
(LPSPropTagArray) &sptPropsForHeader,
|
|
0,
|
|
&cValues,
|
|
&pMsgProps
|
|
);
|
|
if (!HR_SUCCEEDED(hResult))
|
|
{
|
|
CALL_FAIL(GENERAL_ERR, TEXT("GetProps"), hResult);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
hResult = pMsgObj->OpenProperty(
|
|
PR_RTF_COMPRESSED,
|
|
&IID_IStream,
|
|
0,
|
|
0,
|
|
(LPUNKNOWN*) &lpstmT
|
|
);
|
|
if (FAILED(hResult))
|
|
{
|
|
hResult = pMsgObj->OpenProperty(
|
|
PR_BODY,
|
|
&IID_IStream,
|
|
0,
|
|
0,
|
|
(LPUNKNOWN*) &lpstmT
|
|
);
|
|
if (FAILED(hResult))
|
|
{
|
|
//
|
|
// the message body is empty
|
|
//
|
|
lpstmT = NULL;
|
|
}
|
|
hResult = S_OK;
|
|
}
|
|
else
|
|
{
|
|
UseRichText = TRUE;
|
|
}
|
|
|
|
|
|
if (PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED == pMsgProps[MSG_DR_REPORT].ulPropTag && pMsgProps[MSG_DR_REPORT].Value.b)
|
|
{
|
|
NeedDeliveryReport = TRUE;
|
|
}
|
|
else
|
|
{
|
|
NeedDeliveryReport = FALSE;
|
|
}
|
|
|
|
GetSystemTime (&st);
|
|
if(!SystemTimeToFileTime(&st, &ft))
|
|
{
|
|
dwRslt = ::GetLastError ();
|
|
CALL_FAIL (RESOURCE_ERR, TEXT("SystemTimeToFileTime"), dwRslt);
|
|
}
|
|
//
|
|
// submit the fax
|
|
//
|
|
dwRslt = SendFaxDocument
|
|
(
|
|
pMsgObj,
|
|
lpstmT,
|
|
UseRichText,
|
|
pMsgProps,
|
|
pRecipRows,
|
|
&dwRecipientsLimit
|
|
);
|
|
|
|
bSentSuccessfully = (dwRslt == S_OK);
|
|
|
|
for (ulRow=0; ulRow<pRecipRows->cRows; ulRow++)
|
|
{
|
|
pProps = pRecipRows->aRow[ulRow].lpProps;
|
|
|
|
//
|
|
// Update the PR_RESPONSIBILITY to TRUE, so that MAPI will ask another transport
|
|
// provider to try and send it.
|
|
//
|
|
pProps[RECIP_RESPONSIBILITY].ulPropTag = PR_RESPONSIBILITY;
|
|
pProps[RECIP_RESPONSIBILITY].Value.b = TRUE;
|
|
|
|
if (!bSentSuccessfully)
|
|
{
|
|
//
|
|
// for each recipient: insert NDR string as a property value, update delivery time as NULL,
|
|
// and live the PR_RESPONSIBILITY == false, so that MAPI will try to send the fax via another
|
|
// transport provider.
|
|
//
|
|
pProps[RECIP_DELIVER_TIME].ulPropTag = PR_NULL;
|
|
LoadString( g_hResource, IDS_FAILED_MESSAGE, FailedText, sizeof(FailedText) / sizeof(FailedText[0]));
|
|
if (IDS_RECIPIENTS_LIMIT == dwRslt)
|
|
{
|
|
//
|
|
// Recipient limit NDR
|
|
//
|
|
TCHAR TmpErrorText[1024] = {0};
|
|
|
|
LoadString( g_hResource, dwRslt, TmpErrorText, ARR_SIZE(TmpErrorText));
|
|
_sntprintf( ErrorText, ARR_SIZE(ErrorText)-1, TmpErrorText, pRecipRows->cRows, dwRecipientsLimit);
|
|
}
|
|
else
|
|
{
|
|
LoadString( g_hResource, dwRslt, ErrorText, sizeof(ErrorText) / sizeof(ErrorText[0]));
|
|
}
|
|
|
|
_sntprintf( szHeaderText, ARR_SIZE(szHeaderText)-1, _T("\t%s\r\n\t%s\r\n"),FailedText, ErrorText);
|
|
LPSTR pTmpStr = ConvertTStringToAString(szHeaderText);
|
|
if (!pTmpStr)
|
|
{
|
|
hResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
LPSTR pStrA;
|
|
hResult = gpfnAllocateMore( CbtszsizeA(pTmpStr), pProps, (LPVOID *)&pStrA );
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
//
|
|
// Copy the formatted string and hook it into the pre-allocated (by MAPI) column
|
|
//
|
|
_mbscpy ((PUCHAR)pStrA, (PUCHAR)pTmpStr);//pStrA is preAllocated. no need to check for NULL
|
|
pProps[RECIP_REPORT_TEXT].ulPropTag = PR_REPORT_TEXT_A;
|
|
pProps[RECIP_REPORT_TEXT].Value.lpszA = pStrA;
|
|
}
|
|
else
|
|
{
|
|
pProps[RECIP_REPORT_TEXT].ulPropTag = PROP_TAG (PT_ERROR, PROP_ID (PR_REPORT_TEXT));
|
|
pProps[RECIP_REPORT_TEXT].Value.err = hResult;
|
|
}
|
|
MemFree(pTmpStr);
|
|
pTmpStr = NULL;
|
|
gpfnFreeBuffer(pStrA); //allocated with gpfnAllocateMore which is MAPIAllocateMore
|
|
pStrA = NULL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// for each recipient: insert DR string as a property value, update delivery time,
|
|
// For delivery report, each recipient must have this property set.
|
|
// Otherwise the spooler will default to generate an NDR instead.
|
|
//
|
|
pProps[RECIP_DELIVER_TIME].ulPropTag = PR_DELIVER_TIME;
|
|
pProps[RECIP_DELIVER_TIME].Value.ft = ft;
|
|
|
|
pProps[RECIP_REPORT_TEXT].ulPropTag = PROP_TAG (PT_ERROR, PROP_ID (PR_REPORT_TEXT));
|
|
pProps[RECIP_REPORT_TEXT].Value.err = S_OK;
|
|
}
|
|
|
|
//
|
|
// add this recipient to a recipients list, that includes only the recipients we've tried
|
|
// to send to. (we either succeeded to queue all of them or we failed to queue all of them).
|
|
//
|
|
|
|
//
|
|
// Does the list where this recipient goes have enough room for one more entry?
|
|
// If not, resize the address list to hold QUERY_SIZE more entries.
|
|
//
|
|
if (!(pOurAdrList ) || ((pOurAdrList)->cEntries + 1 > ulRecipCount))
|
|
{
|
|
hResult= GrowAddressList( &pOurAdrList , 10, &ulRecipCount );
|
|
if (FAILED(hResult))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have room now so store the new ADRENTRY. As part of the
|
|
// storage, we're going to copy the SRow pointer from the SRowSet
|
|
// into the ADRENTRY. Once we've done this, we won't need the
|
|
// SRowSet any more ... and the SRow will be released when
|
|
// we unwind OurAdrList
|
|
//
|
|
(pOurAdrList)->aEntries[(pOurAdrList)->cEntries].cValues = pRecipRows->aRow[ulRow].cValues;
|
|
(pOurAdrList)->aEntries[(pOurAdrList)->cEntries].rgPropVals = pRecipRows->aRow[ulRow].lpProps;
|
|
|
|
//
|
|
// Increase the number of entries in the address list
|
|
//
|
|
(pOurAdrList)->cEntries++;
|
|
}
|
|
|
|
|
|
//
|
|
// Now we need to save changes on the message and close it.
|
|
// After this, the message object can't be used.
|
|
//
|
|
hResult = pMsgObj->SaveChanges(0);
|
|
switch (hResult)
|
|
{
|
|
case S_OK:
|
|
case MAPI_E_NO_ACCESS:
|
|
break;
|
|
case MAPI_E_OBJECT_DELETED:
|
|
|
|
case MAPI_E_OBJECT_CHANGED:
|
|
goto ErrorExit;
|
|
default: break;
|
|
}
|
|
|
|
|
|
//
|
|
// Let the MAPI spooler do other things
|
|
//
|
|
CheckSpoolerYield();
|
|
|
|
//
|
|
// change our MsgObj's recipients list, so that it'll include only those that got the message.
|
|
//
|
|
if(pOurAdrList)
|
|
{
|
|
hResult = pMsgObj->ModifyRecipients( MODRECIP_MODIFY, pOurAdrList);
|
|
hResult = S_OK;
|
|
//
|
|
// We'll drop the error code from the modify recipients call
|
|
//
|
|
}
|
|
|
|
if (bSentSuccessfully)
|
|
{
|
|
if ((NeedDeliveryReport) && (pOurAdrList))
|
|
{
|
|
VERBOSE (DBG_MSG, TEXT("xport\\xplogon.cpp\\SubmitMessage: Sending delivery Report"));
|
|
//
|
|
//let spooler know he has to send delivery rec. to those addresses.
|
|
//
|
|
hResult = m_pSupObj->StatusRecips( pMsgObj, pOurAdrList);
|
|
if (!HR_FAILED(hResult))
|
|
{
|
|
//
|
|
// If we were successful, we should null out the pointer becase MAPI released
|
|
// the memory for this structure. And we should not try to release it
|
|
// again in the cleanup code.
|
|
//
|
|
pOurAdrList = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! bSentSuccessfully)
|
|
{
|
|
if(pOurAdrList)
|
|
{
|
|
hResult = pMsgObj->ModifyRecipients( MODRECIP_MODIFY, pOurAdrList);
|
|
|
|
//
|
|
// We'll drop the error code from the modify recipients call
|
|
//
|
|
VERBOSE (DBG_MSG, TEXT("xport\\xplogon.cpp\\SubmitMessage: Sending UnDelivery Report"));
|
|
//
|
|
//let spooler know he has to send undelivery rec. to those addresses.
|
|
//
|
|
hResult = m_pSupObj->StatusRecips( pMsgObj, pOurAdrList);
|
|
if (!HR_FAILED(hResult))
|
|
{
|
|
//
|
|
// If we were successful, we should null out the pointer becase MAPI released
|
|
// the memory for this structure. And we should not try to release it
|
|
// again in the cleanup code.
|
|
//
|
|
pOurAdrList = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
ErrorExit:
|
|
//
|
|
// Release the table, we're finished with it
|
|
//
|
|
if (pTable)
|
|
{
|
|
pTable->Release();
|
|
}
|
|
|
|
if (pRecipRows)
|
|
{
|
|
FreeProws( pRecipRows );
|
|
}
|
|
|
|
if (pMsgProps)
|
|
{
|
|
MAPIFreeBuffer( pMsgProps );
|
|
pMsgProps = NULL;
|
|
}
|
|
|
|
if (lpstmT)
|
|
{
|
|
lpstmT->Release();
|
|
}
|
|
if(pOurAdrList)
|
|
{
|
|
MAPIFreeBuffer(pOurAdrList);
|
|
}
|
|
|
|
//
|
|
// In case there is a warning or error floating around, don't let it escape to the spooler.
|
|
//
|
|
if (FAILED(hResult))
|
|
{
|
|
//
|
|
// We default to MAPI_E_NOT_ME so that the spooler would attempt handle
|
|
// the message to other transport (currently running in this profile)
|
|
// that handle the same address type as ours.
|
|
//
|
|
hResult = MAPI_E_NOT_ME;
|
|
}
|
|
else
|
|
{
|
|
hResult = S_OK;
|
|
}
|
|
return hResult;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::GrowAddressList(
|
|
LPADRLIST *ppAdrList,
|
|
ULONG ulResizeBy,
|
|
ULONG *pulOldAndNewCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
In this function, given an address list with pulOldAndNewCount of
|
|
entries, we resize the address list to hold the old number of
|
|
entries plus the ulResizeBy entries. The old address list contents
|
|
are copied to the new list and the count reset. The memory for the
|
|
old address list is released here.
|
|
|
|
Arguments:
|
|
|
|
ppAdrList - Pointer to an address where the old address list
|
|
is and where the new resized address list will
|
|
be returned
|
|
ulResizeBy - Number of new address entries to add to the list
|
|
pulOldAndNewCount - Number of entries in the old address list. In
|
|
this parameter, upon sucessful return, will have
|
|
the number of in the new address list
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hResult = S_OK;
|
|
DBG_ENTER(TEXT("CXPLogon::GrowAddressList"),hResult);
|
|
|
|
LPADRLIST pNewAdrList;
|
|
// Calculate how big the new buffer for the expanded address list should be
|
|
ULONG cbSize = CbNewADRLIST ((*pulOldAndNewCount) + ulResizeBy);
|
|
// Allocate the memory for it
|
|
hResult = gpfnAllocateBuffer (cbSize, (LPVOID *)&pNewAdrList);
|
|
if (FAILED(hResult))
|
|
{
|
|
// We can't continue
|
|
return hResult;
|
|
}
|
|
|
|
// Zero-out all memory for neatness
|
|
ZeroMemory (pNewAdrList, cbSize);
|
|
|
|
// If we had entries in the old address list, copy the memory from
|
|
// the old addres list into the new expanded list
|
|
if ((*pulOldAndNewCount))
|
|
{
|
|
CopyMemory( pNewAdrList, *ppAdrList, CbNewADRLIST ((*pulOldAndNewCount)) );
|
|
}
|
|
|
|
// Set the number of entries in the new address list to the OLD size
|
|
pNewAdrList->cEntries = (*pulOldAndNewCount);
|
|
|
|
// We must return the number of available entries in the new expanded address list
|
|
(*pulOldAndNewCount) += ulResizeBy;
|
|
|
|
// Free the old memory and put the new pointer in place
|
|
gpfnFreeBuffer (*ppAdrList);
|
|
*ppAdrList = pNewAdrList;
|
|
return hResult;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::EndMessage(
|
|
ULONG ulMsgRef,
|
|
ULONG *pulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is called by the spooler for each message we're to
|
|
deliver. It's the mate to SubmitMessage. We're called here twice
|
|
for each deferred message and once for non-deferred (realtime)
|
|
messages.
|
|
|
|
We first check the transport state, and if we're
|
|
WAITING for the scheduled delivery time to arrive, we return
|
|
END_DONT_RESEND in *pulFlags, which tells the spooler to queue this
|
|
message for deferred delivery.
|
|
|
|
If the state is SENDING, we're getting called here after
|
|
a message has been dequeued and delivered. Return 0 in *pulFlags
|
|
to tell the spooler the message has been delivered.
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hResult = S_OK;
|
|
DBG_ENTER(TEXT("CXPLogon::EndMessage"),hResult);
|
|
|
|
*pulFlags = 0;
|
|
return hResult;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::Poll(
|
|
ULONG *pulIncoming
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stub method. We should not get called here, because we told
|
|
the spooler not to call us here.
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::StartMessage(
|
|
ULONG ulFlags,
|
|
LPMESSAGE pMsgObj,
|
|
ULONG * pulMsgRef
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method gets called when an incoming message is pending to be
|
|
processed.
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
DBG_ENTER(TEXT("CXPLogon::StartMessage"));
|
|
|
|
//
|
|
//We should not get called here since we don't deal with incoming messages
|
|
//
|
|
Assert(false);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::OpenStatusEntry(
|
|
LPCIID pInterface,
|
|
ULONG ulFlags,
|
|
ULONG * pulObjType,
|
|
LPMAPISTATUS * ppEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is called to get an IMAPIStatus object for this XPLOGON
|
|
session.
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hResult = S_OK;
|
|
DBG_ENTER(TEXT("CXPLogon::OpenStatusEntry"),hResult);
|
|
|
|
if (MAPI_MODIFY & ulFlags)
|
|
{
|
|
hResult = E_ACCESSDENIED;
|
|
goto exit;
|
|
}
|
|
|
|
*pulObjType = MAPI_STATUS;
|
|
|
|
exit:
|
|
return hResult;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::ValidateState(
|
|
ULONG ulUIParam,
|
|
ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function gets caller by a client in order to validate the
|
|
transport logon properties. This function open the profile with the
|
|
most up-to-date properties and then compares them to what the transport
|
|
has stored internally.
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CXPLogon::FlushQueues(
|
|
ULONG ulUIParam,
|
|
ULONG cbTargetTransport,
|
|
LPENTRYID pTargetTransport,
|
|
ULONG ulFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by the MAPI spooler when, upon request of the client or
|
|
ourselves, we need to flush the inbound or outbound queue.
|
|
Here we make connections to the server to download messages, refresh
|
|
the remote message headers, and request the spooler to send us any
|
|
deferred messages.
|
|
Transport connecting only in FlushQueues() allow the MAPI spooler to
|
|
better manage contention of multiple transport accessing common
|
|
communication resources (such as COM ports) and let the spooler give us
|
|
messages to process when is best for the overall subsystem.
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
void WINAPI
|
|
CXPLogon::CheckSpoolerYield(
|
|
BOOL fReset
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enforce the 0.2 second rule for transport that need to yield to the
|
|
MAPI spooler. Called periodically while processing a message to
|
|
determine if we have used more than 0.2 seconds. If so, then call
|
|
SpoolerYield(), else just continue.
|
|
This is called with fReset set to TRUE when we first enter one
|
|
of the Transport Logon methods (usually one that is known to
|
|
take a long time like StartMessage() or SubmitMessage(). )
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwStop;
|
|
static DWORD dwStart;
|
|
if (fReset)
|
|
{
|
|
dwStart = GetTickCount();
|
|
}
|
|
else
|
|
{
|
|
dwStop = GetTickCount();
|
|
if ((dwStop - dwStart) > 200) // 200 milliseconds
|
|
{
|
|
m_pSupObj->SpoolerYield (0);
|
|
dwStart = GetTickCount();
|
|
}
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
CXPLogon::AddRef()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
++m_cRef;
|
|
return m_cRef;
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
CXPLogon::Release()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Refer to MAPI Documentation on this method.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG ulCount = --m_cRef;
|
|
if (!ulCount)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
return ulCount;
|
|
}
|