/*++ Copyright (c) 1996 Microsoft Corporation Module Name: faxdoc.cpp Abstract: This module contains all code necessary to print an exchange message as a fax document. Author: Wesley Witt (wesw) 13-Aug-1996 Revision History: 20/10/99 -danl- Connect to appropriate server, get basenote from windir dd/mm/yy -author- description --*/ #include "faxxp.h" #include "emsabtag.h" #include "mapiutil.h" #include "debugex.h" #include using namespace std; #pragma hdrstop struct CRecipCmp { /* Comparison operator 'less' Compare two FAX_PERSONAL_PROFILEs by recipient's name and fax number */ bool operator()(LPCFAX_PERSONAL_PROFILE lpcRecipient1, LPCFAX_PERSONAL_PROFILE lpcRecipient2) const { bool bRes = false; int nFaxNumberCpm = 0; if(!lpcRecipient1 || !lpcRecipient2 || !lpcRecipient1->lptstrFaxNumber || !lpcRecipient2->lptstrFaxNumber) { Assert(false); return bRes; } nFaxNumberCpm = _tcscmp(lpcRecipient1->lptstrFaxNumber, lpcRecipient2->lptstrFaxNumber); if(nFaxNumberCpm < 0) { bRes = true; } else if(nFaxNumberCpm == 0) { // // The fax numbers are same // lets compare the names // if(lpcRecipient1->lptstrName && lpcRecipient2->lptstrName) { bRes = (_tcsicmp(lpcRecipient1->lptstrName, lpcRecipient2->lptstrName) < 0); } else { bRes = (lpcRecipient1->lptstrName < lpcRecipient2->lptstrName); } } return bRes; } }; typedef set RECIPIENTS_SET; // prototypes LPTSTR ConvertAStringToTString(LPCSTR lpcstrSource); extern "C" BOOL MergeTiffFiles( LPTSTR BaseTiffFile, LPTSTR NewTiffFile ); extern "C" BOOL PrintRandomDocument( LPCTSTR FaxPrinterName, LPCTSTR DocName, LPTSTR OutputFile ); PVOID CXPLogon::MyGetPrinter( LPTSTR PrinterName, DWORD Level ) /*++ Routine Description: Gets the printer data for a specific printer Arguments: PrinterName - Name of the desired printer Return Value: Pointer to a printer info structure or NULL for failure. --*/ { DBG_ENTER(TEXT("CXPLogon::MyGetPrinter")); PVOID PrinterInfo = NULL; HANDLE hPrinter = NULL; DWORD Bytes; PRINTER_DEFAULTS PrinterDefaults; PrinterDefaults.pDatatype = NULL; PrinterDefaults.pDevMode = NULL; PrinterDefaults.DesiredAccess = PRINTER_ACCESS_USE; if (!OpenPrinter( PrinterName, &hPrinter, &PrinterDefaults )) { CALL_FAIL (GENERAL_ERR, TEXT("OpenPrinter"),::GetLastError()); goto exit; } if ((!GetPrinter( hPrinter, Level, NULL, 0, &Bytes )) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)) { // we just want to know how much memory we need, so we pass NULL and 0, // this way, the function will fail, but will return us the number of // bytes required in Bytes CALL_FAIL (GENERAL_ERR, TEXT("GetPrinter"), ::GetLastError()); goto exit; } PrinterInfo = (LPPRINTER_INFO_2) MemAlloc( Bytes ); if (!PrinterInfo) { goto exit; } if (!GetPrinter( hPrinter, Level, (LPBYTE) PrinterInfo, Bytes, &Bytes )) { MemFree(PrinterInfo); PrinterInfo = NULL; goto exit; } exit: if(hPrinter) { ClosePrinter( hPrinter ); } return PrinterInfo; } static BOOL GetFaxTempFileName( OUT LPTSTR lpstrTempName, IN DWORD dwOutStrSize ) /*++ Routine Description: Generates a temporal file with prefix 'fax' in directory designated for temporal files. Arguments: [OUT] lpstrTempName - Output paramter. Pointer to the temporal file name. The buffer should be MAX_PATH characters. [IN] dwOutStrSize - Size of buffer lpstrTempName in TCHARs Return Value: TRUE if success, FALSE otherwise --*/ { BOOL bRes = TRUE; DBG_ENTER(TEXT("GetFaxTempFileName"),bRes); TCHAR strTempPath[MAX_PATH] = {0}; TCHAR strTempFile[MAX_PATH] = {0}; DWORD ec = ERROR_SUCCESS; // LastError for this function. Assert(lpstrTempName); if (!GetTempPath( sizeof(strTempPath)/sizeof(TCHAR), strTempPath )) { ec=::GetLastError(); goto Exit; } if (GetTempFileName( strTempPath, _T("fax"), 0, strTempFile ) == 0) { ec=::GetLastError(); goto Exit; } // //Copy the source string and leave space for the NULL char // _tcsncpy(lpstrTempName, strTempFile, dwOutStrSize-1); Exit: if (ERROR_SUCCESS != ec) { SetLastError(ec); bRes = FALSE; } return bRes; } BOOL CXPLogon::PrintRichText( HWND hWndRichEdit, HDC hDC ) /*++ Routine Description: Prints the rich text contained in a rich text window into a DC. Arguments: hWndRichEdit - Window handle for the rich text window hDC - Printer device context Return Value: None. --*/ { BOOL bRet = FALSE; DBG_ENTER(TEXT("CXPLogon::PrintRichText"), bRet); FORMATRANGE fr; LONG lTextOut; LONG lTextCurr; RECT rcTmp; fr.hdc = hDC; fr.hdcTarget = hDC; fr.chrg.cpMin = 0; fr.chrg.cpMax = -1; // // Set page rect to phys page size in twips // fr.rcPage.top = 0; fr.rcPage.left = 0; fr.rcPage.right = MulDiv(GetDeviceCaps(hDC, PHYSICALWIDTH), 1440, GetDeviceCaps(hDC, LOGPIXELSX)); fr.rcPage.bottom = MulDiv(GetDeviceCaps(hDC, PHYSICALHEIGHT), 1440, GetDeviceCaps(hDC, LOGPIXELSY)); // // Set up 3/4" horizontal and 1" vertical margins, but leave a minimum of 1" // printable space in each direction. Otherwise, use full page. // fr.rc = fr.rcPage; // start with full page if (fr.rcPage.right > 2*3*1440/4 + 1440) { fr.rc.right -= (fr.rc.left = 3*1440/4); } if (fr.rcPage.bottom > 3*1440) { fr.rc.bottom -= (fr.rc.top = 1440); } // // save the formatting rectangle // rcTmp = fr.rc; if (!SetMapMode( hDC, MM_TEXT )) { CALL_FAIL (GENERAL_ERR, TEXT("SetMapMode"), ::GetLastError()); goto error; } lTextOut = 0; lTextCurr = 0; while (TRUE) { // // Just measure the text // lTextOut = (LONG)SendMessage( hWndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM) &fr ); if(lTextOut <= lTextCurr) { // // The end of the text // break; } lTextCurr = lTextOut; if (StartPage( hDC ) <= 0) { CALL_FAIL (GENERAL_ERR, TEXT("StartPage"), ::GetLastError()); goto error; } // // Render the page // lTextOut = (LONG)SendMessage( hWndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr ); if (EndPage( hDC ) <= 0) { CALL_FAIL (GENERAL_ERR, TEXT("EndPage"), ::GetLastError()); goto error; } fr.chrg.cpMin = lTextOut; fr.chrg.cpMax = -1; // // EM_FORMATRANGE tends to modify fr.rc.bottom, reset here // fr.rc = rcTmp; } bRet = TRUE; error: // // flush the cache // SendMessage( hWndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL ); return bRet; } DWORD CXPLogon::PrintPlainText( HDC hDC, LPSTREAM lpstmT, LPTSTR tszSubject, PFAXXP_CONFIG FaxConfig ) /*++ Routine Description: Prints a stream of plain text into the printer DC provided. Note: this code was stolen from notepad. Arguments: hDC - Printer DC lpstmT - Stream pointer for rich text. tszSubject - Subject FaxConfig - Fax configuration data Return Value: ERROR_SUCCESS - if success Error IDS_... code if failed. --*/ { DWORD rVal = ERROR_SUCCESS; LPTSTR BodyText = NULL; LPTSTR lpLine; LPTSTR pLineEOL; LPTSTR pNextLine; HRESULT hResult; HFONT hFont = NULL; HFONT hPrevFont = NULL; TEXTMETRIC tm; INT nLinesPerPage; INT dyTop; // width of top border (pixels) INT dyBottom; // width of bottom border INT dxLeft; // width of left border INT dxRight; // width of right border INT yPrintChar; // height of a character INT tabSize; // Size of a tab for print device in device units INT yCurpos = 0; INT xCurpos = 0; INT nPixelsLeft = 0; INT guess = 0; SIZE Size; // to see if text will fit in space left INT nPrintedLines = 0; BOOL fPageStarted = FALSE; INT iPageNum = 0; INT xPrintRes; // printer resolution in x direction INT yPrintRes; // printer resolution in y direction INT yPixInch; // pixels/inch INT xPixInch; // pixels/inch INT xPixUnit; // pixels/local measurement unit INT yPixUnit; // pixels/local measurement unit BOOL fEnglish; DWORD Chars=0; DWORD dwBodyLen=0; DWORD dwSubjectLen=0; STATSTG Stats; INT PrevBkMode = 0; DBG_ENTER(TEXT("CXPLogon::PrintPlainText"),rVal); Assert(hDC); Assert(FaxConfig); if(lpstmT) { hResult = lpstmT->Stat( &Stats, 0 ); if (FAILED(hResult)) { rVal = IDS_CANT_ACCESS_MSG_DATA; goto exit; } dwBodyLen = (INT) Stats.cbSize.QuadPart; } if(tszSubject) { dwSubjectLen = _tcslen(tszSubject); } BodyText = (LPTSTR) MemAlloc(dwSubjectLen * sizeof(TCHAR) + dwBodyLen + 4 ); if (!BodyText) { rVal = IDS_OUT_OF_MEM; goto exit; } if(tszSubject) { _tcscpy(BodyText, tszSubject); lpLine = _tcsninc(BodyText, dwSubjectLen); } else { lpLine = BodyText; } if(lpstmT) { hResult = lpstmT->Read( (LPVOID)lpLine, dwBodyLen, (LPDWORD) &dwBodyLen ); if (FAILED(hResult)) { rVal = IDS_CANT_ACCESS_MSG_DATA; goto exit; } } lpLine = BodyText; Chars = _tcslen(lpLine); // // check if the body is not empty // if the message length is shorter then 32(arbitrary number) // and all the carachters are control or space. // if(Chars < 32) { BOOL bEmpty = TRUE; TCHAR* pTchar = lpLine; for(DWORD dw = 0; dw < Chars; ++dw) { if(!_istspace(*pTchar) && !_istcntrl(*pTchar)) { bEmpty = FALSE; break; } pTchar = _tcsinc(pTchar); } if(bEmpty) { rVal = IDS_NO_MSG_BODY; goto exit; } } fEnglish = GetProfileInt( _T("intl"), _T("iMeasure"), 1 ); xPrintRes = GetDeviceCaps( hDC, HORZRES ); yPrintRes = GetDeviceCaps( hDC, VERTRES ); xPixInch = GetDeviceCaps( hDC, LOGPIXELSX ); yPixInch = GetDeviceCaps( hDC, LOGPIXELSY ); // // compute x and y pixels per local measurement unit // if (fEnglish) { xPixUnit= xPixInch; yPixUnit= yPixInch; } else { xPixUnit= CMToInches( xPixInch ); yPixUnit= CMToInches( yPixInch ); } SetMapMode( hDC, MM_TEXT ); // // match font size to the device point size // FaxConfig->FontStruct.lfHeight = -MulDiv(FaxConfig->FontStruct.lfHeight, yPixInch, 72); hFont = CreateFontIndirect( &FaxConfig->FontStruct ); hPrevFont = (HFONT) SelectObject( hDC, hFont ); SetBkMode( hDC, TRANSPARENT ); if (!GetTextMetrics( hDC, &tm )) { rVal = IDS_CANT_PRINT_BODY; goto exit; } yPrintChar = tm.tmHeight + tm.tmExternalLeading; tabSize = tm.tmAveCharWidth * 8; // // compute margins in pixels // dxLeft = LEFT_MARGIN * xPixUnit; dxRight = RIGHT_MARGIN * xPixUnit; dyTop = TOP_MARGIN * yPixUnit; dyBottom = BOTTOM_MARGIN * yPixUnit; // // Number of lines on a page with margins // nLinesPerPage = ((yPrintRes - dyTop - dyBottom) / yPrintChar); while (*lpLine) { if ( _tcsncmp(lpLine,TEXT("\r"),1) == 0 ) { lpLine = _tcsninc(lpLine,2); yCurpos += yPrintChar; nPrintedLines++; xCurpos= 0; continue; } pLineEOL = lpLine; pLineEOL = _tcschr(pLineEOL,TEXT('\r')); do { if ((nPrintedLines == 0) && (!fPageStarted)) { StartPage( hDC ); fPageStarted = TRUE; yCurpos = 0; xCurpos = 0; } if ( _tcsncmp(lpLine,TEXT("\t"),1) == 0 ) { // // round up to the next tab stop // if the current position is on the tabstop, goto next one // xCurpos = ((xCurpos + tabSize) / tabSize ) * tabSize; lpLine = _tcsinc(lpLine); } else { // // find end of line or tab // pNextLine = lpLine; while (*pNextLine && (pNextLine != pLineEOL) && ( _tcsncmp(pNextLine,TEXT("\t"),1) ) ) { pNextLine = _tcsinc(pNextLine); } // // find out how many characters will fit on line // Chars = (INT)(pNextLine - lpLine); nPixelsLeft = xPrintRes - dxRight - dxLeft - xCurpos; GetTextExtentExPoint( hDC, lpLine, Chars, nPixelsLeft, &guess, NULL, &Size ); if (guess) { // // at least one character fits - print // TextOut( hDC, dxLeft+xCurpos, yCurpos+dyTop, lpLine, guess ); xCurpos += Size.cx; // account for printing lpLine = _tcsninc(lpLine,guess);// printed characters } else { // // no characters fit what's left // no characters will fit in space left // if none ever will, just print one // character to keep progressing through // input file. // if (xCurpos == 0) { if( lpLine != pNextLine ) { // // print something if not null line // could use exttextout here to clip // TextOut( hDC, dxLeft+xCurpos, yCurpos+dyTop, lpLine, 1 ); lpLine = _tcsinc(lpLine); } } else { // // perhaps the next line will get it // xCurpos = xPrintRes; // force to next line } } // // move printhead in y-direction // if ((xCurpos >= (xPrintRes - dxRight - dxLeft) ) || (lpLine == pLineEOL)) { yCurpos += yPrintChar; nPrintedLines++; xCurpos = 0; } if (nPrintedLines >= nLinesPerPage) { EndPage( hDC ); fPageStarted = FALSE; nPrintedLines = 0; xCurpos = 0; yCurpos = 0; iPageNum++; } } } while (*lpLine && (lpLine != pLineEOL)); if ( _tcsncmp(lpLine,TEXT("\r"),1) == 0 ) { lpLine = _tcsinc(lpLine); } if ( _tcsncmp(lpLine,TEXT("\n"),1) == 0 ) { lpLine = _tcsinc(lpLine); } } if (fPageStarted) { EndPage( hDC ); } exit: MemFree( BodyText ); if (hPrevFont) { SelectObject( hDC, hPrevFont ); DeleteObject( hFont ); } if (PrevBkMode) { SetBkMode( hDC, PrevBkMode ); } return rVal; } extern "C" DWORD CALLBACK EditStreamRead( DWORD_PTR dwCookie, OUT LPBYTE pbBuff, LONG cb, LONG *pcb ) /*++ Routine Description: Wrapper function for the IStream read method. This function is used to read rich text from an exchange stream. Arguments: dwCookie - This pointer for the IStream object pbBuff - Pointer to the data buffer cb - Size of the data buffer pcb - Returned byte count Return Value: Return code from IStream::Read --*/ { return ((LPSTREAM)dwCookie)->Read( pbBuff, cb, (ULONG*) pcb ); } DWORD CXPLogon::PrintAttachmentToFile( IN LPMESSAGE pMsgObj, IN PFAXXP_CONFIG pFaxConfig, OUT LPTSTR * lpptstrOutAttachments ) /*++ Routine Description: Prints all attachments to the output file, by itearating over the attachment table Arguments: pMsgObj - Pointer to message object. Used to get an attachmnet table pFaxConfig - Pointer to fax configuration lpptstrOutAttachments - Name of the output tiff file. The string should be empty Return Value: 0 - if success Last error code from if failed. Comments: If this function succeeded it allocates a memory for *lpptstrOutAttachments and creates a temporal file *lpptstrOutAttachments. It's up to user to free both these allocations, by DeleteFile(*lpptstrOutAttachments); MemFree(*lpptstrOutAttachments); --*/ { DWORD rVal = 0; DBG_ENTER(TEXT("CXPLogon::PrintAttachmentToFile"),rVal); LPSPropValue pPropsAttachTable = NULL; LPSPropValue pPropsAttach = NULL; LPMAPITABLE AttachmentTable = NULL; LPSRowSet pAttachmentRows = NULL; LPATTACH lpAttach = NULL; LPSTREAM lpstmA = NULL; LPTSTR AttachFileName = NULL; TCHAR TempPath[MAX_PATH]; TCHAR TempFile[MAX_PATH]; TCHAR DocFile[MAX_PATH]; HANDLE hFile = INVALID_HANDLE_VALUE; LPTSTR DocType = NULL; LPSTR p = NULL; BOOL DeleteAttachFile = FALSE; LPTSTR FileName = NULL; BOOL AllAttachmentsGood = TRUE; TCHAR strTempTiffFile[MAX_PATH] = {0}; TCHAR strMergedTiffFile[MAX_PATH] = {0}; HRESULT hResult = S_OK; DWORD i = 0; ULONG PropCount = 0; DWORD Bytes; LPTSTR lptstrTempStr = NULL; Assert(lpptstrOutAttachments); Assert(*lpptstrOutAttachments == NULL); // // get the attachment table, if it is available // hResult = pMsgObj->GetAttachmentTable( 0, &AttachmentTable ); if (HR_SUCCEEDED(hResult)) { hResult = HrAddColumns( AttachmentTable, (LPSPropTagArray) &sptAttachTableProps, gpfnAllocateBuffer, gpfnFreeBuffer ); if (HR_SUCCEEDED(hResult)) { hResult = HrQueryAllRows( AttachmentTable, NULL, NULL, NULL, 0, &pAttachmentRows ); if (FAILED(hResult)) { pAttachmentRows = NULL; } else { if (pAttachmentRows->cRows == 0) { FreeProws( pAttachmentRows ); pAttachmentRows = NULL; } } } } if (pAttachmentRows) { // // this loop verifies that each document's attachment registration // supports the printto verb. // AllAttachmentsGood = TRUE; for (i = 0; i < pAttachmentRows->cRows; ++i) { pPropsAttachTable = pAttachmentRows->aRow[i].lpProps; lpAttach = NULL; pPropsAttach = NULL; if (pPropsAttachTable[MSG_ATTACH_METHOD].Value.ul == NO_ATTACHMENT) { goto next_attachment1; } // // open the attachment // hResult = pMsgObj->OpenAttach( pPropsAttachTable[MSG_ATTACH_NUM].Value.ul, NULL, MAPI_BEST_ACCESS, &lpAttach ); if (FAILED(hResult)) { AllAttachmentsGood = FALSE; goto next_attachment1; } // // get the attachment properties // hResult = lpAttach->GetProps( (LPSPropTagArray) &sptAttachProps, 0, &PropCount, &pPropsAttach ); if (FAILED(hResult)) { AllAttachmentsGood = FALSE; goto next_attachment1; } // // try to get the extension if the file. // this indicates what type of dicument it is. // if we cannot get the document type then it is // impossible to print the document. // if (DocType) { MemFree( DocType ); DocType = NULL; } if (PROP_TYPE(pPropsAttach[MSG_ATTACH_EXTENSION].ulPropTag) == PT_ERROR) { if (PROP_TYPE(pPropsAttach[MSG_ATTACH_LFILENAME].ulPropTag) != PT_ERROR) { p = strrchr( pPropsAttach[MSG_ATTACH_LFILENAME].Value.lpszA, '.' ); if (p) { DocType = ConvertAStringToTString( p ); if(!DocType) { rVal = IDS_OUT_OF_MEM; goto exit; } } } else if (PROP_TYPE(pPropsAttach[MSG_ATTACH_FILENAME].ulPropTag) != PT_ERROR) { p = strrchr( pPropsAttach[MSG_ATTACH_FILENAME].Value.lpszA, '.' ); if (p) { DocType = ConvertAStringToTString( p ); if(!DocType) { rVal = IDS_OUT_OF_MEM; goto exit; } } } } else { DocType = ConvertAStringToTString( pPropsAttach[MSG_ATTACH_EXTENSION].Value.lpszA ); if(!DocType) { rVal = IDS_OUT_OF_MEM; goto exit; } } if (!DocType) { AllAttachmentsGood = FALSE; goto next_attachment1; } Bytes = sizeof(TempFile); rVal = RegQueryValue( HKEY_CLASSES_ROOT, DocType, TempFile, (PLONG) &Bytes ); if ((rVal != ERROR_SUCCESS) && (rVal != ERROR_INVALID_DATA)) { VERBOSE (DBG_MSG, TEXT("File Type: %s: isn't associated to any application"), DocType); AllAttachmentsGood = FALSE; goto next_attachment1; } wsprintf( TempPath, _T("%s\\shell\\printto\\command"), TempFile ); Bytes = sizeof(TempFile); rVal = RegQueryValue( HKEY_CLASSES_ROOT, TempPath, TempFile, (PLONG) &Bytes ); if ((rVal != ERROR_SUCCESS) && (rVal != ERROR_INVALID_DATA)) { VERBOSE (DBG_MSG, TEXT("File extension \"*%s\" doesn't have the PrintTo verb"), DocType); AllAttachmentsGood = FALSE; goto next_attachment1; } next_attachment1: if (lpAttach) { lpAttach->Release(); } if (pPropsAttach) { MAPIFreeBuffer( pPropsAttach ); pPropsAttach = NULL; } } if (!AllAttachmentsGood) { rVal = IDS_BAD_ATTACHMENTS; goto exit; } for (i = 0; i < pAttachmentRows->cRows; ++i) { pPropsAttachTable = pAttachmentRows->aRow[i].lpProps; lpAttach = NULL; pPropsAttach = NULL; if (pPropsAttachTable[MSG_ATTACH_METHOD].Value.ul == NO_ATTACHMENT) { goto next_attachment2; } // // open the attachment // hResult = pMsgObj->OpenAttach( pPropsAttachTable[MSG_ATTACH_NUM].Value.ul, NULL, MAPI_BEST_ACCESS, &lpAttach ); if (FAILED(hResult)) { goto next_attachment2; } // // get the attachment properties // hResult = lpAttach->GetProps( (LPSPropTagArray) &sptAttachProps, 0, &PropCount, &pPropsAttach ); if (FAILED(hResult)) { goto next_attachment2; } // // try to get the extension if the file. // this indicates what type of dicument it is. // if we cannot get the document type then it is // impossible to print the document. // if (DocType) { MemFree( DocType ); DocType = NULL; } if (PROP_TYPE(pPropsAttach[MSG_ATTACH_EXTENSION].ulPropTag) == PT_ERROR) { if (PROP_TYPE(pPropsAttach[MSG_ATTACH_LFILENAME].ulPropTag) != PT_ERROR) { p = strrchr( pPropsAttach[MSG_ATTACH_LFILENAME].Value.lpszA, '.' ); if (p) { DocType = ConvertAStringToTString( p ); if(!DocType) { rVal = IDS_OUT_OF_MEM; goto exit; } } } else if (PROP_TYPE(pPropsAttach[MSG_ATTACH_FILENAME].ulPropTag) != PT_ERROR) { p = strrchr( pPropsAttach[MSG_ATTACH_FILENAME].Value.lpszA, '.' ); if (p) { DocType = ConvertAStringToTString( p ); if(!DocType) { rVal = IDS_OUT_OF_MEM; goto exit; } } } } else { DocType = ConvertAStringToTString( pPropsAttach[MSG_ATTACH_EXTENSION].Value.lpszA ); if(!DocType) { rVal = IDS_OUT_OF_MEM; goto exit; } } if (!DocType) { goto next_attachment2; } lpstmA = NULL; AttachFileName = NULL; DeleteAttachFile = FALSE; // // get the attached file name // if (FileName) MemFree(FileName); if (PROP_TYPE(pPropsAttach[MSG_ATTACH_PATHNAME].ulPropTag) != PT_ERROR) { FileName = ConvertAStringToTString(pPropsAttach[MSG_ATTACH_PATHNAME].Value.lpszA); if(!FileName) { rVal = IDS_OUT_OF_MEM; goto exit; } } else { FileName = NULL; } if (FileName) { AttachFileName = StringDup( FileName ); if(!AttachFileName) { rVal = IDS_OUT_OF_MEM; goto exit; } } // // get the stream object // switch( pPropsAttach[MSG_ATTACH_METHOD].Value.ul ) { case ATTACH_BY_VALUE: hResult = lpAttach->OpenProperty( PR_ATTACH_DATA_BIN, &IID_IStream, 0, 0, (LPUNKNOWN*) &lpstmA ); if (FAILED(hResult)) { goto next_attachment2; } break; case ATTACH_EMBEDDED_MSG: case ATTACH_OLE: hResult = lpAttach->OpenProperty( PR_ATTACH_DATA_OBJ, &IID_IStreamDocfile, 0, 0, (LPUNKNOWN*) &lpstmA ); if (FAILED(hResult)) { hResult = lpAttach->OpenProperty( PR_ATTACH_DATA_BIN, &IID_IStreamDocfile, 0, 0, (LPUNKNOWN*) &lpstmA ); if (FAILED(hResult)) { hResult = lpAttach->OpenProperty( PR_ATTACH_DATA_OBJ, &IID_IStorage, 0, 0, (LPUNKNOWN*) &lpstmA ); if (FAILED(hResult)) { goto next_attachment2; } } } break; } if (lpstmA) { DWORD dwSize = GetTempPath( sizeof(TempPath)/sizeof(TCHAR) , TempPath ); Assert( dwSize != 0); GetTempFileName( TempPath, _T("Fax"), 0, TempFile ); hFile = CreateFile( TempFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL ); if (hFile != INVALID_HANDLE_VALUE) { #define BLOCK_SIZE (64*1024) LPBYTE StrmData; DWORD BytesWrite; StrmData = (LPBYTE) MemAlloc( BLOCK_SIZE ); if(!StrmData) { rVal = IDS_OUT_OF_MEM; goto exit; } do { hResult = lpstmA->Read( StrmData, BLOCK_SIZE, &Bytes ); if (FAILED(hResult)) { break; } WriteFile( hFile, StrmData, Bytes, &BytesWrite, NULL ); } while (Bytes == BLOCK_SIZE); CloseHandle( hFile ); if(StrmData) { MemFree( StrmData ); StrmData = NULL; } if (AttachFileName) { MemFree( AttachFileName ); AttachFileName = NULL; } _tcscpy( DocFile, TempFile ); lptstrTempStr = _tcsrchr( DocFile, '.' ); if (lptstrTempStr) { _tcscpy( lptstrTempStr, DocType ); MoveFile( TempFile, DocFile ); AttachFileName = StringDup( DocFile ); if(!AttachFileName) { rVal = IDS_OUT_OF_MEM; goto exit; } } else { AttachFileName = StringDup( TempFile ); if(!AttachFileName) { rVal = IDS_OUT_OF_MEM; goto exit; } } DeleteAttachFile = TRUE; } lpstmA->Release(); } if (AttachFileName) { if (!GetFaxTempFileName(strTempTiffFile, ARR_SIZE(strTempTiffFile))) { rVal = IDS_BAD_ATTACHMENTS;//GetLastError(); goto exit; } // // print the attachment // if (!PrintRandomDocument( pFaxConfig->PrinterName, AttachFileName, strTempTiffFile)) { CALL_FAIL (GENERAL_ERR, TEXT("PrintRandomDocument"), ::GetLastError()); rVal = IDS_BAD_ATTACHMENTS;//GetLastError(); if (!DeleteFile( strTempTiffFile )) { CALL_FAIL (GENERAL_ERR, TEXT("DeleteFile"), ::GetLastError()); } goto exit; } if (strMergedTiffFile[0] != 0) { // // merge the attachments // if (!MergeTiffFiles( strMergedTiffFile, strTempTiffFile)) { CALL_FAIL (GENERAL_ERR, TEXT("MergeTiffFiles"), ::GetLastError()); rVal = IDS_BAD_ATTACHMENTS;//GetLastError(); if (!DeleteFile( strTempTiffFile )) { CALL_FAIL (GENERAL_ERR, TEXT("DeleteFile"), ::GetLastError()); } goto exit; } if (!DeleteFile( strTempTiffFile )) { CALL_FAIL (GENERAL_ERR, TEXT("DeleteFile"), ::GetLastError()); } } else { // copies a first attachment _tcscpy(strMergedTiffFile,strTempTiffFile); } if (DeleteAttachFile) { if (!DeleteFile( AttachFileName )) { CALL_FAIL (GENERAL_ERR, TEXT("DeleteFile"), ::GetLastError()); } } if(AttachFileName) { MemFree( AttachFileName ); AttachFileName = NULL; } } next_attachment2: if (lpAttach) { lpAttach->Release(); } if (pPropsAttach) { MAPIFreeBuffer( pPropsAttach ); pPropsAttach = NULL; } } } else { // // no attachments // rVal = IDS_NO_MSG_ATTACHMENTS; } if (strMergedTiffFile[0] != 0) { if (!(*lpptstrOutAttachments = StringDup(strMergedTiffFile))) { rVal = IDS_OUT_OF_MEM; } } exit: if (FileName) { MemFree( FileName ); } if (DocType) { MemFree( DocType ); } if (pAttachmentRows) { FreeProws( pAttachmentRows ); } if (AttachmentTable) { AttachmentTable->Release(); } if (AttachFileName) { MemFree( AttachFileName ); } return rVal; } DWORD CXPLogon::PrintMessageToFile( IN LPSTREAM lpstmT, IN BOOL UseRichText, IN PFAXXP_CONFIG pFaxConfig, IN LPTSTR tszSubject, OUT LPTSTR* lpptstrOutDocument ) /*++ Routine Description: Prints the message body to the output file. Arguments: lpstmT - Pointer to the message body stream UseRichText - boolean value. TRUE if the message is in Rich format, FALSE - if this is a plain text pFaxConfig - Pointer to fax configuration (used by plain text printing) tszSubject - Subject lpptstrOutDocument - Name of the output tiff file. The string should be empty Return Value: ERROR_SUCCESS - if success Error IDS_... code if failed. Comments: If this function succeeded it allocates a memory for *lpptstrOutDocument and creates a temporal file *lpptstrOutDocument. It's up to user to free both these allocations, by DeleteFile(*lpptstrOutDocument); MemFree(*lpptstrOutDocument); --*/ { DWORD rVal = ERROR_SUCCESS; LARGE_INTEGER BigZero = {0}; LPSTREAM lpstm = NULL; HRESULT hResult; HWND hWndRichEdit = NULL; HDC hDC = NULL; EDITSTREAM es = {0}; TCHAR strOutputTiffFile[MAX_PATH] = {0}; TCHAR DocName[64]; TCHAR tszSubjectFormat[64]; TCHAR* ptszSubjectText = NULL; DWORD dwSubjectSize = 0; DOCINFO docInfo = { sizeof(DOCINFO), NULL, NULL, NULL, 0, }; DBG_ENTER(TEXT("CXPLogon::PrintMessageToFile"),rVal); Assert(pFaxConfig); Assert(lpptstrOutDocument); Assert(*lpptstrOutDocument==NULL); if (!(hDC = CreateDC( NULL, pFaxConfig->PrinterName, NULL, NULL))) { CALL_FAIL (GENERAL_ERR, TEXT("CreateDC"), ::GetLastError()); rVal = IDS_CANT_PRINT_BODY; goto exit; } LoadString(g_hResource, IDS_MESSAGE_DOC_NAME, DocName, sizeof(DocName) / sizeof(DocName[0])); docInfo.lpszDocName = DocName; if (!GetFaxTempFileName(strOutputTiffFile, ARR_SIZE(strOutputTiffFile))) { rVal = IDS_CANT_PRINT_BODY; goto exit; } docInfo.lpszOutput = strOutputTiffFile ; docInfo.lpszDatatype = _T("RAW"); if (StartDoc(hDC, &docInfo) <= 0) { CALL_FAIL (GENERAL_ERR, TEXT("StartDoc"), ::GetLastError()); rVal = IDS_CANT_PRINT_BODY; goto exit; } // // position the stream to the beginning // if(lpstmT) { hResult = lpstmT->Seek( BigZero, STREAM_SEEK_SET, NULL ); if (HR_FAILED (hResult)) { rVal = IDS_CANT_ACCESS_MSG_DATA; goto exit; } } if(!pFaxConfig->UseCoverPage && tszSubject && _tcslen(tszSubject)) { // // get subject string // dwSubjectSize = _tcslen(tszSubject) * sizeof(TCHAR) + sizeof(tszSubjectFormat); ptszSubjectText = (TCHAR*)MemAlloc(dwSubjectSize); if(!ptszSubjectText) { rVal = IDS_OUT_OF_MEM; goto exit; } if(!LoadString(g_hResource, IDS_SUBJECT_FORMAT, tszSubjectFormat, sizeof(tszSubjectFormat) / sizeof(tszSubjectFormat[0]))) { Assert(FALSE); CALL_FAIL (GENERAL_ERR, TEXT("LoadString"), ::GetLastError()); _tcscpy(tszSubjectFormat, TEXT("%s")); } _stprintf(ptszSubjectText, tszSubjectFormat, tszSubject); dwSubjectSize = _tcslen(ptszSubjectText); } if (UseRichText) { if(lpstmT) { hResult = WrapCompressedRTFStream( lpstmT, 0, &lpstm ); if (HR_FAILED (hResult)) { rVal = IDS_CANT_ACCESS_MSG_DATA; goto exit; } } hWndRichEdit = CreateWindowEx( 0, // extended window style TEXT("RICHEDIT"), // registered class name TEXT(""), // window name ES_MULTILINE, // window style 0, // horizontal position of window 0, // vertical position of window 0, // window width 0, // window height NULL, // handle to parent or owner window NULL, // menu handle or child identifier g_hModule, // handle to application instance NULL); // window-creation data if (!hWndRichEdit) { CALL_FAIL (GENERAL_ERR, TEXT("CreateWindowEx"), ::GetLastError()); rVal = IDS_CANT_PRINT_BODY; goto exit; } if(ptszSubjectText && _tcslen(ptszSubjectText)) { // // add subject to body // SendMessage(hWndRichEdit, WM_SETTEXT, 0, (LPARAM)ptszSubjectText); // // Set the subject's font // CHARFORMAT CharFormat = {0}; CharFormat.cbSize = sizeof (CHARFORMAT); CharFormat.dwMask = CFM_BOLD | CFM_CHARSET | CFM_FACE | CFM_ITALIC | CFM_SIZE | CFM_STRIKEOUT | CFM_UNDERLINE; CharFormat.dwEffects = ((FW_BOLD <= pFaxConfig->FontStruct.lfWeight) ? CFE_BOLD : 0) | ((pFaxConfig->FontStruct.lfItalic) ? CFE_ITALIC : 0) | ((pFaxConfig->FontStruct.lfStrikeOut) ? CFE_STRIKEOUT : 0) | ((pFaxConfig->FontStruct.lfUnderline) ? CFE_UNDERLINE : 0); // // Height is already in point size. // CharFormat.yHeight = abs ( pFaxConfig->FontStruct.lfHeight ); // // Convert point to twip // CharFormat.yHeight *= 20; CharFormat.bCharSet = pFaxConfig->FontStruct.lfCharSet; CharFormat.bPitchAndFamily = pFaxConfig->FontStruct.lfPitchAndFamily; lstrcpyn (CharFormat.szFaceName, pFaxConfig->FontStruct.lfFaceName, LF_FACESIZE); SendMessage(hWndRichEdit, EM_SETCHARFORMAT, SCF_ALL, // Apply font formatting to all the control's text (LPARAM)&CharFormat); // New font settings // // Place insertion point at the end of the subject text // See MSDN under "HOWTO: Place a Caret After Edit-Control Text" // SendMessage(hWndRichEdit, EM_SETSEL, MAKELONG(0xffff,0xffff), MAKELONG(0xffff,0xffff)); } if(lpstm) { es.pfnCallback = EditStreamRead; es.dwCookie = (DWORD_PTR) lpstm; SendMessage(hWndRichEdit, EM_STREAMIN, SF_RTF | SFF_SELECTION | SFF_PLAINRTF, (LPARAM) &es); } // // Check if the body is not empty. // If the message length is shorter then 32 (arbitrary number) // and all the characters are control or space. // TCHAR tszText[32] = {0}; DWORD dwTextSize; if (!GetWindowText(hWndRichEdit, tszText, sizeof(tszText)/sizeof(tszText[0])-1)) { if (ERROR_INSUFFICIENT_BUFFER == ::GetLastError ()) { // // Subject + Body are longer than 31 characters. // We're assuming they have valid printable text and // that this is not an empty message. // goto DoPrintRichText; } // // This is another type of error // rVal = ::GetLastError (); CALL_FAIL (GENERAL_ERR, TEXT("GetWindowText"), rVal); goto exit; } dwTextSize = _tcslen(tszText); if(dwTextSize < sizeof(tszText)/sizeof(tszText[0])-2) { BOOL bEmpty = TRUE; TCHAR* pTchar = tszText; for(DWORD dw = 0; dw < dwTextSize; ++dw) { if(!_istspace(*pTchar) && !_istcntrl(*pTchar)) { bEmpty = FALSE; break; } pTchar = _tcsinc(pTchar); } if(bEmpty) { rVal = IDS_NO_MSG_BODY; goto exit; } } DoPrintRichText: if (!PrintRichText(hWndRichEdit, hDC)) { rVal = IDS_CANT_PRINT_BODY; goto exit; } } else { rVal = PrintPlainText(hDC, lpstmT, ptszSubjectText, pFaxConfig); if (rVal) { goto exit; } } // closes DC if (EndDoc(hDC) <=0) { Assert(FALSE); // better not to be here goto exit; } if (!DeleteDC(hDC)) { Assert(FALSE); // better not to be here goto exit; } hDC = NULL; if (strOutputTiffFile[0] != 0) { if (!(*lpptstrOutDocument = StringDup(strOutputTiffFile))) { rVal = IDS_OUT_OF_MEM; //ERROR_NOT_ENOUGH_MEMORY; goto exit; } VERBOSE (DBG_MSG, TEXT("Attachment File is %s:"), *lpptstrOutDocument); } rVal = ERROR_SUCCESS; exit: if (lpstm) { lpstm->Release(); } if (hDC) { DeleteDC(hDC); } MemFree(ptszSubjectText); if(ERROR_SUCCESS != rVal && _tcslen(strOutputTiffFile)) { if (!DeleteFile( strOutputTiffFile )) { CALL_FAIL (GENERAL_ERR, TEXT("DeleteFile"), ::GetLastError()); } } return rVal; } DWORD CXPLogon::PrintFaxDocumentToFile( IN LPMESSAGE pMsgObj, IN LPSTREAM lpstmT, IN BOOL UseRichText, IN PFAXXP_CONFIG pFaxConfig, IN LPTSTR tszSubject, OUT LPTSTR* lpptstrMessageFileName ) /*++ Routine Description: Runs printing of the message body and attachments to the output file. Arguments: pMsgObj - Pointer to the message object lpstmT - Pointer to the message body stream UseRichText - boolean value. TRUE if the message is in Rich format, FALSE - if this is a plain text pFaxConfig - Pointer to fax configuration (used by plain text printing) tszSubject - Subject lpptstrMessageFileName - Name of the output tiff file. The string should be empty Return Value: 0 - if success Error code if failed. Comments: If this function succeeded it returns an allocated memory for *lpptstrMessageFileName and a temporal file *lpptstrMessageFileName. It's up to user to free both these allocations, by DeleteFile(*lpptstrMessageFileName); MemFree(*lpptstrMessageFileName); --*/ { DWORD rVal = 0; LPTSTR lptstrAttachmentsTiff = NULL; BOOL bAttachment = TRUE; BOOL bBody = TRUE; DBG_ENTER(TEXT("CXPLogon::PrintFaxDocumentToFile"),rVal); Assert(lpptstrMessageFileName); Assert(*lpptstrMessageFileName == NULL); // // prints attachments // rVal = PrintAttachmentToFile(pMsgObj, pFaxConfig, &lptstrAttachmentsTiff); if(rVal) { if(IDS_NO_MSG_ATTACHMENTS == rVal) { rVal = 0; bAttachment = FALSE; } else { CALL_FAIL (GENERAL_ERR, TEXT("PrintAttachmentToFile"), 0); goto error; } } // // prints the body // rVal = PrintMessageToFile(lpstmT, UseRichText, pFaxConfig, tszSubject, lpptstrMessageFileName); if(rVal) { if(IDS_NO_MSG_BODY == rVal) { rVal = 0; bBody = FALSE; } else { CALL_FAIL (GENERAL_ERR, TEXT("PrintMessageToFile"), 0); goto error; } } if(!bBody && !bAttachment) { rVal = IDS_EMPTY_MESSAGE; goto error; } if (!*lpptstrMessageFileName) // empty body { if (lptstrAttachmentsTiff) // the message contains attachments { if (!(*lpptstrMessageFileName = StringDup(lptstrAttachmentsTiff))) { rVal = IDS_OUT_OF_MEM; goto error; } } } else // the message contains body { if (lptstrAttachmentsTiff) // the message contains attachments { // merges message and attachements if (!MergeTiffFiles( *lpptstrMessageFileName, lptstrAttachmentsTiff)) { rVal = IDS_CANT_PRINT_BODY; goto error; } // deletes attachements if(!DeleteFile(lptstrAttachmentsTiff)) { VERBOSE (DBG_MSG, TEXT("DeleteFile Failed in xport\\faxdoc.cpp")); } MemFree(lptstrAttachmentsTiff); lptstrAttachmentsTiff = NULL; } } return rVal; error: if (lptstrAttachmentsTiff) { if(!DeleteFile(lptstrAttachmentsTiff)) { VERBOSE (DBG_MSG, TEXT("DeleteFile Failed in xport\\faxdoc.cpp")); } MemFree(lptstrAttachmentsTiff); lptstrAttachmentsTiff = NULL; } if (*lpptstrMessageFileName) { if(!DeleteFile(*lpptstrMessageFileName)) { VERBOSE (DBG_MSG, TEXT("DeleteFile Failed in xport\\faxdoc.cpp")); } MemFree(*lpptstrMessageFileName); *lpptstrMessageFileName = NULL; } return rVal; } DWORD CXPLogon::SendFaxDocument( LPMESSAGE pMsgObj, LPSTREAM lpstmT, BOOL UseRichText, LPSPropValue pMsgProps, LPSRowSet pRecipRows, LPDWORD lpdwRecipientsLimit ) /*++ Routine Description: Prints an exchange message and attachments to the fax printer. Arguments: pMsgObj - Pointer to message object lpstmT - Stream pointer for rich text. UseRichText - boolean value. TRUE if the message is in Rich format, FALSE - if this is a plain text pMsgProps - Message properties (those that are defined in sptPropsForHeader) pRecipRows - Properties of recipients lpdwRecipientsLimit - recieves the recipietns limit in case of failure. '0' means no limit Return Value: Zero for success, otherwise error code. --*/ { DWORD dwRetVal = 0; PPRINTER_INFO_2 PrinterInfo = NULL; PRINTER_DEFAULTS PrinterDefaults; HANDLE hPrinter = NULL; DWORD ec = 0; HRESULT hResult = S_OK; EDITSTREAM es = {0}; LPPROFSECT pProfileObj = NULL; ULONG PropCount = 0; ULONG PropMsgCount = 0; LPSPropValue pProps = NULL; LPSPropValue pPropsMsg = NULL; FAXXP_CONFIG FaxConfig = {0}; MAPINAMEID NameIds[NUM_FAX_MSG_PROPS]; MAPINAMEID *pNameIds[NUM_FAX_MSG_PROPS] = { &NameIds[0], &NameIds[1], &NameIds[2], &NameIds[3], &NameIds[4], &NameIds[5]}; LPSPropTagArray MsgPropTags = NULL; HKEY hKey = 0; DWORD RegSize = 0; DWORD RegType = 0; DWORD CountPrinters = 0; LPTSTR lptstrRecipientName = NULL ; LPTSTR lptstrRecipientNumber = NULL ; LPTSTR lptstrRecName = NULL ; LPTSTR lptstrRecFaxNumber = NULL ; LPTSTR lptstrSubject = NULL ; LPTSTR lptszServerName = NULL; LPTSTR lptstrDocumentFileName = NULL; HANDLE FaxServer = NULL; FAX_COVERPAGE_INFO_EX CovInfo = {0}; FAX_PERSONAL_PROFILE SenderProfile = {0}; FAX_JOB_PARAM_EX JobParamsEx = {0}; PFAX_PERSONAL_PROFILE pRecipients = NULL; DWORDLONG dwlParentJobId = 0; DWORDLONG* lpdwlRecipientJobIds = NULL; BOOL bRslt = FALSE; LPSPropValue pRecipProps = NULL; DWORD dwRecipient = 0; TCHAR strCoverpageName[MAX_PATH] = {0}; BOOL bServerBased = TRUE; DWORD dwRecipientNumber = 0; DWORD dwRights = 0; //access rights of fax sender LPADRBOOK lpAdrBook = NULL; LPTSTR lpstrSenderSMTPAdr = NULL;//sender's SMTP adr, including "SMTP:" prefix LPTSTR lpstrSMTPPrefix = NULL; LPTSTR lpstrSenderAdr = NULL;//sender's SMTP adr. without prefix ULONG cValues = 0; ULONG ulObjType = NULL; LPMAILUSER pMailUser = NULL; LPSPropValue lpPropValue = NULL; ULONG i, j; BOOL bGotSenderAdr = FALSE; LPTSTR lptstrCPFullPath = NULL; LPTSTR lptstrCPName = NULL; DWORD dwError = 0; BOOL bResult = FALSE; DWORD dwReceiptsOptions = DRT_NONE; RECIPIENTS_SET setRecip; // Recipients set used to remove the duplications SizedSPropTagArray(1, sptPropxyAddrProp) = {1, PR_EMS_AB_PROXY_ADDRESSES_A}; DWORD dwRecipientsLimit = 0; DBG_ENTER(TEXT("CXPLogon::SendFaxDocument"), dwRetVal); // // ***************************** // get the fax config properties // ***************************** // hResult = m_pSupObj->OpenProfileSection( &g_FaxGuid, MAPI_MODIFY, &pProfileObj ); if (HR_FAILED (hResult)) { CALL_FAIL (GENERAL_ERR, TEXT("OpenProfileSection"), hResult); dwRetVal = IDS_CANT_ACCESS_PROFILE; goto exit; } hResult = pProfileObj->GetProps( (LPSPropTagArray) &sptFaxProps, 0, &PropCount, &pProps ); if ((FAILED(hResult))||(hResult == ResultFromScode(MAPI_W_ERRORS_RETURNED)) ) { CALL_FAIL (GENERAL_ERR, TEXT("GetProps"), hResult); dwRetVal = IDS_INTERNAL_ERROR; goto exit; } FaxConfig.PrinterName = StringDup( (LPTSTR)pProps[PROP_FAX_PRINTER_NAME].Value.bin.lpb ); if(! FaxConfig.PrinterName) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } FaxConfig.CoverPageName = StringDup( (LPTSTR)pProps[PROP_COVERPAGE_NAME].Value.bin.lpb ); if(! FaxConfig.CoverPageName) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } FaxConfig.UseCoverPage = pProps[PROP_USE_COVERPAGE].Value.ul; FaxConfig.ServerCoverPage = pProps[PROP_SERVER_COVERPAGE].Value.ul; CopyMemory( &FaxConfig.FontStruct, pProps[PROP_FONT].Value.bin.lpb, pProps[PROP_FONT].Value.bin.cb ); FaxConfig.SendSingleReceipt= pProps[PROP_SEND_SINGLE_RECEIPT].Value.ul; FaxConfig.bAttachFax = pProps[PROP_ATTACH_FAX].Value.ul; // // ************************************* // now get the message config properties // ************************************* // NameIds[MSGPI_FAX_PRINTER_NAME].lpguid = (LPGUID)&PS_PUBLIC_STRINGS; NameIds[MSGPI_FAX_PRINTER_NAME].ulKind = MNID_STRING; NameIds[MSGPI_FAX_PRINTER_NAME].Kind.lpwstrName = MSGPS_FAX_PRINTER_NAME; NameIds[MSGPI_FAX_COVERPAGE_NAME].lpguid = (LPGUID)&PS_PUBLIC_STRINGS; NameIds[MSGPI_FAX_COVERPAGE_NAME].ulKind = MNID_STRING; NameIds[MSGPI_FAX_COVERPAGE_NAME].Kind.lpwstrName = MSGPS_FAX_COVERPAGE_NAME; NameIds[MSGPI_FAX_USE_COVERPAGE].lpguid = (LPGUID)&PS_PUBLIC_STRINGS; NameIds[MSGPI_FAX_USE_COVERPAGE].ulKind = MNID_STRING; NameIds[MSGPI_FAX_USE_COVERPAGE].Kind.lpwstrName = MSGPS_FAX_USE_COVERPAGE; NameIds[MSGPI_FAX_SERVER_COVERPAGE].lpguid = (LPGUID)&PS_PUBLIC_STRINGS; NameIds[MSGPI_FAX_SERVER_COVERPAGE].ulKind = MNID_STRING; NameIds[MSGPI_FAX_SERVER_COVERPAGE].Kind.lpwstrName = MSGPS_FAX_SERVER_COVERPAGE; NameIds[MSGPI_FAX_SEND_SINGLE_RECEIPT].lpguid = (LPGUID)&PS_PUBLIC_STRINGS; NameIds[MSGPI_FAX_SEND_SINGLE_RECEIPT].ulKind = MNID_STRING; NameIds[MSGPI_FAX_SEND_SINGLE_RECEIPT].Kind.lpwstrName = MSGPS_FAX_SEND_SINGLE_RECEIPT; NameIds[MSGPI_FAX_ATTACH_FAX].lpguid = (LPGUID)&PS_PUBLIC_STRINGS; NameIds[MSGPI_FAX_ATTACH_FAX].ulKind = MNID_STRING; NameIds[MSGPI_FAX_ATTACH_FAX].Kind.lpwstrName = MSGPS_FAX_ATTACH_FAX; hResult = pMsgObj->GetIDsFromNames( (ULONG) NUM_FAX_MSG_PROPS, pNameIds, MAPI_CREATE, &MsgPropTags ); if (HR_FAILED(hResult)) { if(hResult == MAPI_E_NOT_ENOUGH_MEMORY) { dwRetVal = IDS_OUT_OF_MEM; } else { dwRetVal = IDS_INTERNAL_ERROR; } CALL_FAIL (GENERAL_ERR, TEXT("GetIDsFromNames"), hResult); goto exit; } MsgPropTags->aulPropTag[MSGPI_FAX_PRINTER_NAME] = PROP_TAG( PT_BINARY, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_PRINTER_NAME])); MsgPropTags->aulPropTag[MSGPI_FAX_COVERPAGE_NAME] = PROP_TAG( PT_BINARY, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_COVERPAGE_NAME])); MsgPropTags->aulPropTag[MSGPI_FAX_USE_COVERPAGE] = PROP_TAG( PT_LONG, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_USE_COVERPAGE])); MsgPropTags->aulPropTag[MSGPI_FAX_SERVER_COVERPAGE] = PROP_TAG( PT_LONG, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_SERVER_COVERPAGE])); MsgPropTags->aulPropTag[MSGPI_FAX_SEND_SINGLE_RECEIPT] = PROP_TAG( PT_LONG, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_SEND_SINGLE_RECEIPT])); MsgPropTags->aulPropTag[MSGPI_FAX_ATTACH_FAX] = PROP_TAG( PT_LONG, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_ATTACH_FAX])); hResult = pMsgObj->GetProps( MsgPropTags, 0, &PropMsgCount, &pPropsMsg ); if(hResult == ResultFromScode(MAPI_W_ERRORS_RETURNED)) { VERBOSE (DBG_MSG, TEXT("GetProps in SendFaxDocument returned MAPI_W_ERRORS_RETURNED")); } if (FAILED(hResult)) // // happens if user did not press ok on the "fax attributes" DlgBox - it's not an error! // { CALL_FAIL (GENERAL_ERR, TEXT("GetProps"), hResult); hResult = S_OK; } // //prefer the config props defined for the message (if they exist) on those defined for the fax. // if (PROP_TYPE(pPropsMsg[MSGPI_FAX_PRINTER_NAME].ulPropTag) != PT_ERROR) { MemFree( FaxConfig.PrinterName ); FaxConfig.PrinterName = StringDup((LPTSTR)pPropsMsg[MSGPI_FAX_PRINTER_NAME].Value.bin.lpb); if(! FaxConfig.PrinterName) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } } if (PROP_TYPE(pPropsMsg[MSGPI_FAX_COVERPAGE_NAME].ulPropTag) != PT_ERROR) { MemFree( FaxConfig.CoverPageName); FaxConfig.CoverPageName = StringDup((LPTSTR)pPropsMsg[MSGPI_FAX_COVERPAGE_NAME].Value.bin.lpb); if(! FaxConfig.CoverPageName) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } } if (PROP_TYPE(pPropsMsg[MSGPI_FAX_USE_COVERPAGE].ulPropTag) != PT_ERROR) { FaxConfig.UseCoverPage = pPropsMsg[MSGPI_FAX_USE_COVERPAGE].Value.ul; } if (PROP_TYPE(pPropsMsg[MSGPI_FAX_SERVER_COVERPAGE].ulPropTag) != PT_ERROR) { FaxConfig.ServerCoverPage = pPropsMsg[MSGPI_FAX_SERVER_COVERPAGE].Value.ul; } if (PROP_TYPE(pPropsMsg[MSGPI_FAX_SEND_SINGLE_RECEIPT].ulPropTag) != PT_ERROR) { FaxConfig.SendSingleReceipt = pPropsMsg[MSGPI_FAX_SEND_SINGLE_RECEIPT].Value.ul; } if (PROP_TYPE(pPropsMsg[MSGPI_FAX_ATTACH_FAX].ulPropTag) != PT_ERROR) { FaxConfig.bAttachFax = pPropsMsg[MSGPI_FAX_ATTACH_FAX].Value.ul; } if (PROP_TYPE(pMsgProps[MSG_SUBJECT].ulPropTag) != PT_ERROR) { lptstrSubject = ConvertAStringToTString(pMsgProps[MSG_SUBJECT].Value.lpszA); if(! lptstrSubject) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } } // // ****************************************** // open the printer, and create the tiff file // ****************************************** // // // open the printer - first try to get info on the printer in FaxConfig, // if you fail, search all the printers until the first fax printer is found. // PrinterInfo = (PPRINTER_INFO_2) MyGetPrinter( FaxConfig.PrinterName, 2 ); if (NULL == PrinterInfo) { // if the chosen printer is not accessable, try to locate another SharedFax printer PrinterInfo = (PPRINTER_INFO_2) MyEnumPrinters( NULL, 2, &CountPrinters ); if (NULL != PrinterInfo) { for (i=0; i<(int)CountPrinters; i++) { if (_tcscmp( PrinterInfo[i].pDriverName, FAX_DRIVER_NAME ) == 0) { break; } } } else { CountPrinters = i = 0; //no printers were found } if (i == (int)CountPrinters) //if there are no printers, or none of them is a fax printer { dwRetVal = IDS_NO_FAX_PRINTER; goto exit; } // // if a SharedFax printer was found, update it as the printer that we'll send the fax threw // MemFree( FaxConfig.PrinterName ); FaxConfig.PrinterName = StringDup( PrinterInfo[i].pPrinterName ); if(! FaxConfig.PrinterName) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } MemFree( PrinterInfo ); PrinterInfo = (PPRINTER_INFO_2) MyGetPrinter( FaxConfig.PrinterName, 2 ); if (NULL == PrinterInfo) { dwRetVal = IDS_CANT_ACCESS_PRINTER; goto exit; } } PrinterDefaults.pDatatype = NULL; PrinterDefaults.pDevMode = NULL; PrinterDefaults.DesiredAccess = PRINTER_ACCESS_USE; if (!OpenPrinter( FaxConfig.PrinterName, &hPrinter, &PrinterDefaults )) { dwRetVal = IDS_CANT_ACCESS_PRINTER; goto exit; } dwRetVal = PrintFaxDocumentToFile( pMsgObj, lpstmT, UseRichText, &FaxConfig , lptstrSubject, &lptstrDocumentFileName); if (IDS_EMPTY_MESSAGE == dwRetVal) { // // The message is empty. This is not really an error. // dwRetVal = 0; if(!FaxConfig.UseCoverPage) { // // If the message is empty and no cover page is specified there is // nothing more to do. // goto exit; } } if(dwRetVal) { goto exit; } VERBOSE (DBG_MSG, TEXT("Final Tiff is %s:"), lptstrDocumentFileName); // // ************************************** // initializes sender and recipients info // ************************************** // // // sender's info // SenderProfile.dwSizeOfStruct = sizeof(SenderProfile); hResult = FaxGetSenderInformation(&SenderProfile); if(S_OK != hResult) { CALL_FAIL (GENERAL_ERR, TEXT("FaxGetSenderInformation"), hResult); if (HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) == hResult) { dwRetVal = IDS_INTERNAL_ERROR; } else if (HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY) == hResult ) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } } // // recipients' info // pRecipRows includes rows coresponding only to recipients that their PR_RESPONSIBILITY == FALSE // dwRecipientNumber = pRecipRows->cRows; pRecipients = (PFAX_PERSONAL_PROFILE)MemAlloc(sizeof(FAX_PERSONAL_PROFILE) * dwRecipientNumber); if(! pRecipients) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } ZeroMemory(pRecipients, sizeof(FAX_PERSONAL_PROFILE) * dwRecipientNumber); dwRecipient = 0; for (DWORD dwRecipRow=0; dwRecipRow < pRecipRows->cRows ; ++dwRecipRow) { pRecipProps = pRecipRows->aRow[dwRecipRow].lpProps; lptstrRecipientName = ConvertAStringToTString(pRecipProps[RECIP_NAME].Value.lpszA); if(! lptstrRecipientName) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } lptstrRecipientNumber = ConvertAStringToTString(pRecipProps[RECIP_EMAIL_ADR].Value.lpszA); if(! lptstrRecipientNumber) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } if(_tcsstr(lptstrRecipientName, lptstrRecipientNumber)) { // // PR_EMAIL_ADDRESS_A is substring of PR_DISPLAY_NAME_A // so we suppose that PR_DISPLAY_NAME_A was not specified. // Try to get the recipient name from PR_EMAIL_ADDRESS_A // MemFree( lptstrRecipientName ); lptstrRecipientName = NULL; } // // finds a fax number from the name string, // e.g. "Fax Number@+14 (2) 324324" --> +14 (2) 324324) // LPTSTR pRecipientNumber = _tcschr(lptstrRecipientNumber, '@'); if (pRecipientNumber) { // //if there was a @, increment the pointer to point to the next char after it. // *pRecipientNumber = '\0'; pRecipientNumber = _tcsinc(pRecipientNumber); if(!lptstrRecipientName) { lptstrRecipientName = StringDup(lptstrRecipientNumber); if(! lptstrRecipientName) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } } } else { // //if there's no @ in the string, it's OK as it was. // pRecipientNumber = lptstrRecipientNumber; } // // initializes recipient info // pRecipients[dwRecipient].dwSizeOfStruct = sizeof(FAX_PERSONAL_PROFILE); pRecipients[dwRecipient].lptstrFaxNumber = StringDup(pRecipientNumber); if(! pRecipients[dwRecipient].lptstrFaxNumber) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } if(lptstrRecipientName) { pRecipients[dwRecipient].lptstrName = StringDup(lptstrRecipientName); if(! pRecipients[dwRecipient].lptstrName) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } } __try { // // Insert all the recipients into a set. // If there are any duplications insert() failes // if(setRecip.insert(&pRecipients[dwRecipient]).second == true) { ++dwRecipient; } else { // // Such recipients already exists // MemFree(pRecipients[dwRecipient].lptstrName); pRecipients[dwRecipient].lptstrName = NULL; MemFree(pRecipients[dwRecipient].lptstrFaxNumber); pRecipients[dwRecipient].lptstrFaxNumber = NULL; } } __except (EXCEPTION_EXECUTE_HANDLER) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } if(lptstrRecipientName) { MemFree( lptstrRecipientName ); lptstrRecipientName = NULL; } if(lptstrRecipientNumber) { MemFree( lptstrRecipientNumber ); lptstrRecipientNumber = NULL; } } // for // // Update the recipient number to the actual size without duplications // dwRecipientNumber = dwRecipient; // // ******************* // get cover page info // ******************* // if (FaxConfig.UseCoverPage) { bServerBased = FaxConfig.ServerCoverPage; if(bServerBased) { _tcscpy(strCoverpageName,FaxConfig.CoverPageName); } else { // // this is a personal CP, we have to add to it's name the full UNC path // TCHAR CpDir[MAX_PATH] = {0}; TCHAR* pCpName = NULL; bResult = GetClientCpDir( CpDir, sizeof(CpDir) / sizeof(CpDir[0])); if(! bResult) { CALL_FAIL(GENERAL_ERR, TEXT("GetClientCpDir"), ::GetLastError()); dwRetVal = IDS_INTERNAL_ERROR; goto exit; } _tcscat(CpDir,FaxConfig.CoverPageName); if((_tcslen(CpDir)/sizeof(TCHAR) + _tcslen(FAX_COVER_PAGE_FILENAME_EXT)/sizeof(TCHAR) + 1) > MAX_PATH) { dwRetVal = IDS_INTERNAL_ERROR; goto exit; } _tcscat(CpDir, FAX_COVER_PAGE_FILENAME_EXT); _tcscpy(strCoverpageName, CpDir); } VERBOSE (DBG_MSG, TEXT("Sending Fax with Coverpage: %s"), strCoverpageName); // // initializes a cover page info // CovInfo.dwSizeOfStruct = sizeof( FAX_COVERPAGE_INFO_EX); CovInfo.dwCoverPageFormat = FAX_COVERPAGE_FMT_COV; CovInfo.lptstrCoverPageFileName = strCoverpageName; //if it's not a server's CP, should include exact path to the CP file CovInfo.bServerBased = bServerBased ; CovInfo.lptstrNote = NULL; CovInfo.lptstrSubject = lptstrSubject; } else { // // no cover page // CovInfo.dwSizeOfStruct = sizeof( FAX_COVERPAGE_INFO_EX); CovInfo.dwCoverPageFormat = FAX_COVERPAGE_FMT_COV_SUBJECT_ONLY; CovInfo.lptstrSubject = lptstrSubject; } // // ************************* // connect to the fax server // ************************* // if (!GetServerNameFromPrinterInfo(PrinterInfo ,&lptszServerName ) || !FaxConnectFaxServer(lptszServerName,&FaxServer)) { CALL_FAIL (GENERAL_ERR, TEXT("FaxConnectFaxServer"), ::GetLastError()); dwRetVal = IDS_CANT_ACCESS_SERVER; goto exit; } VERBOSE (DBG_MSG, TEXT("Connected to Fax Server: %s"), lptszServerName); // // ***************************** // initialize the job parameters // ***************************** // JobParamsEx.dwSizeOfStruct = sizeof( FAX_JOB_PARAM_EX); VERBOSE (DBG_MSG, TEXT("******************JobParamsEx:***********************")); // // get the sender's SMTP address // pMsgProps hold PropsForHeader properties, including PR_SENDER_ENTRYID // hResult = m_pSupObj->OpenAddressBook(NULL, 0, &lpAdrBook); if (FAILED(hResult)) { CALL_FAIL (GENERAL_ERR, TEXT("OpenAddressBook"), ::GetLastError()); } else { hResult = lpAdrBook->OpenEntry( pMsgProps[MSG_SENDER_ENTRYID].Value.bin.cb, (LPENTRYID)pMsgProps[MSG_SENDER_ENTRYID].Value.bin.lpb, NULL, 0, &ulObjType, (LPUNKNOWN*)&pMailUser ); if (FAILED(hResult)) { CALL_FAIL (GENERAL_ERR, TEXT("OpenEntry"), ::GetLastError()); } else { hResult = pMailUser->GetProps( (LPSPropTagArray)&sptPropxyAddrProp, 0, &cValues, &lpPropValue ); if (!HR_SUCCEEDED(hResult) || PT_ERROR == PROP_TYPE(lpPropValue->ulPropTag)) { // // We either failed to get the property or the property retrieved has some error. // If we're unable to locate sender's address, we won't be sending a Delivry Receipt, // but we won't fail the sending. // CALL_FAIL (GENERAL_ERR, TEXT("GetProps from MailUser failed, no receipt will be sent!"), hResult); } else { // //loop through the proxy multivalue property // for(j=0;jValue.MVszA.cValues; j++) { lpstrSenderSMTPAdr = ConvertAStringToTString(lpPropValue->Value.MVszA.lppszA[j]); if(! lpstrSenderSMTPAdr) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } // // check if address begins with "SMTP:": // function returns pointer to begining of second param.'s appearance in first param. // if it does not appear, returns NULL // lpstrSMTPPrefix = _tcsstr(lpstrSenderSMTPAdr, TEXT("SMTP:")); if( lpstrSenderSMTPAdr == lpstrSMTPPrefix) { // // Remove this prefix from it, and store it in JobParamsEx. // lpstrSenderAdr = lpstrSenderSMTPAdr + _tcslen(TEXT("SMTP:")); JobParamsEx.lptstrReceiptDeliveryAddress = _tcsdup(lpstrSenderAdr); if(! JobParamsEx.lptstrReceiptDeliveryAddress) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } bGotSenderAdr = TRUE; VERBOSE(DBG_MSG, TEXT("Receipt delivery address is %s"), JobParamsEx.lptstrReceiptDeliveryAddress); break; } } } } } // // when to send, sort of delivery receipt // JobParamsEx.dwScheduleAction = JSA_NOW; if(!FaxGetReceiptsOptions(FaxServer, &dwReceiptsOptions)) { CALL_FAIL(GENERAL_ERR, TEXT("FaxGetReceiptsOptions"), ::GetLastError()); } JobParamsEx.dwReceiptDeliveryType = DRT_NONE; if (bGotSenderAdr && (dwReceiptsOptions & DRT_EMAIL)) { if (TRUE == FaxConfig.SendSingleReceipt) { JobParamsEx.dwReceiptDeliveryType = DRT_EMAIL | DRT_GRP_PARENT; } else { JobParamsEx.dwReceiptDeliveryType = DRT_EMAIL; } if (FaxConfig.bAttachFax) { JobParamsEx.dwReceiptDeliveryType |= DRT_ATTACH_FAX; } } VERBOSE(DBG_MSG, TEXT("Receipt Delivery Type = %ld"), JobParamsEx.dwReceiptDeliveryType); // // priority // if (pMsgProps[MSG_IMPORTANCE].ulPropTag == PR_IMPORTANCE) { if(FALSE == (FaxAccessCheckEx(FaxServer, MAXIMUM_ALLOWED, &dwRights))) { if((hResult = ::GetLastError()) != ERROR_SUCCESS) { CALL_FAIL(GENERAL_ERR, TEXT("FaxAccessCheckEx"), hResult); dwRetVal = IDS_CANT_ACCESS_PROFILE; goto exit; } } // //try to give the sender the prio he asked for. if it's not allowed, try a lower prio. // switch(pMsgProps[MSG_IMPORTANCE].Value.l) { case (IMPORTANCE_HIGH): if ((FAX_ACCESS_SUBMIT_HIGH & dwRights) == FAX_ACCESS_SUBMIT_HIGH) { JobParamsEx.Priority = FAX_PRIORITY_TYPE_HIGH; break; } //fall through case (IMPORTANCE_NORMAL): if ((FAX_ACCESS_SUBMIT_NORMAL & dwRights) == FAX_ACCESS_SUBMIT_NORMAL) { JobParamsEx.Priority = FAX_PRIORITY_TYPE_NORMAL; break; } //fall through case (IMPORTANCE_LOW): if ((FAX_ACCESS_SUBMIT & dwRights) == FAX_ACCESS_SUBMIT) { JobParamsEx.Priority = FAX_PRIORITY_TYPE_LOW; } else { VERBOSE(ASSERTION_FAILED, TEXT("xport\\faxdoc.cpp\\SendFaxDocument: user has no access rights!")); //the user has no right to submit faxes, at any priority! dwRetVal = IDS_NO_SUBMIT_RITHTS; goto exit; } break; default: VERBOSE(ASSERTION_FAILED, TEXT("xport\\faxdoc.cpp\\SendFaxDocument: message importance has undefined value")); ASSERTION_FAILURE } } else { VERBOSE(ASSERTION_FAILED, TEXT("xport\\faxdoc.cpp\\SendFaxDocument: Message had no importance property value!")); dwRetVal = IDS_INTERNAL_ERROR; ASSERTION_FAILURE; goto exit; } VERBOSE(DBG_MSG, TEXT("Message Priority is %ld (0=low, 1=normal, 2=high)"), JobParamsEx.Priority ); // // doc name, number of pages, // TCHAR DocName[64]; LoadString(g_hResource, IDS_MESSAGE_DOC_NAME, DocName, sizeof(DocName) / sizeof (DocName[0])); JobParamsEx.lptstrDocumentName = DocName; JobParamsEx.dwPageCount = 0; //means the server will count the number of pages in the job lpdwlRecipientJobIds = (DWORDLONG*)MemAlloc(sizeof(DWORDLONG)*dwRecipientNumber); if(! lpdwlRecipientJobIds) { dwRetVal = IDS_OUT_OF_MEM; goto exit; } // // ************ // Send the fax // ************ // bRslt= FaxSendDocumentEx( FaxServer, (LPCTSTR) lptstrDocumentFileName, &CovInfo, &SenderProfile, dwRecipientNumber, pRecipients, &JobParamsEx, &dwlParentJobId, lpdwlRecipientJobIds ); if (!bRslt) { hResult = ::GetLastError(); CALL_FAIL (GENERAL_ERR, TEXT("FaxSendDocumentEx"), hResult); // maybe we should swich possible retruned values from SendFaxDocEx, // and choose a more informative IDS switch(hResult) { case ERROR_NOT_ENOUGH_MEMORY: dwRetVal = IDS_OUT_OF_MEM; break; case ERROR_NO_SYSTEM_RESOURCES: dwRetVal = IDS_INTERNAL_ERROR; break; case ERROR_CANT_ACCESS_FILE: dwRetVal = IDS_PERSONAL_CP_FORBIDDEN; break; case ERROR_BAD_FORMAT: dwRetVal = IDS_BAD_CANNONICAL_ADDRESS; break; case FAX_ERR_RECIPIENTS_LIMIT: dwRetVal = IDS_RECIPIENTS_LIMIT; if (!FaxGetRecipientsLimit(FaxServer, &dwRecipientsLimit)) { CALL_FAIL (GENERAL_ERR, TEXT("FaxGetRecipientsLimit"), ::GetLastError()); } break; default: dwRetVal = IDS_CANT_PRINT; break; } goto exit; } FaxClose(FaxServer); FaxServer = NULL; dwRetVal = 0; exit: if(lpAdrBook) { lpAdrBook->Release(); } if(pMailUser) { pMailUser->Release(); } if (FaxServer) { FaxClose(FaxServer); } if (pRecipients) { for (dwRecipient=0; dwRecipientRelease(); } if (pProps) { MAPIFreeBuffer( pProps ); } if (MsgPropTags) { MAPIFreeBuffer( MsgPropTags ); } if (pPropsMsg) { MAPIFreeBuffer( pPropsMsg ); } if (hPrinter) { ClosePrinter( hPrinter ); } if (PrinterInfo) { MemFree( PrinterInfo ); } if (FaxConfig.PrinterName) { MemFree( FaxConfig.PrinterName ); } if (FaxConfig.CoverPageName) { MemFree( FaxConfig.CoverPageName ); } if (lptstrRecipientName) { MemFree(lptstrRecipientName); } if (lptstrRecipientNumber) { MemFree(lptstrRecipientNumber); } if (lptstrRecName) { MemFree(lptstrRecName); } if (lptstrRecFaxNumber) { MemFree(lptstrRecFaxNumber); } if (lptstrSubject) { MemFree(lptstrSubject); } if (lptstrDocumentFileName) { DeleteFile(lptstrDocumentFileName); MemFree(lptstrDocumentFileName); } if (lpdwlRecipientJobIds) { MemFree(lpdwlRecipientJobIds); } if (lptszServerName) { MemFree(lptszServerName); } FaxFreeSenderInformation(&SenderProfile); *lpdwRecipientsLimit = dwRecipientsLimit; return dwRetVal; }