Source code of Windows XP (NT5)
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.

1824 lines
48 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. faxdoc.cpp
  5. Abstract:
  6. This module contains all code necessary to print an
  7. exchange message as a fax document.
  8. Author:
  9. Wesley Witt (wesw) 13-Aug-1996
  10. --*/
  11. #include "faxxp.h"
  12. #pragma hdrstop
  13. #ifdef WIN95
  14. #include "mfx.h"
  15. #include "fwprov.h"
  16. #endif
  17. PVOID
  18. CXPLogon::MyGetPrinter(
  19. LPSTR PrinterName,
  20. DWORD Level
  21. )
  22. /*++
  23. Routine Description:
  24. Gets the printer data for a specifi printer
  25. Arguments:
  26. PrinterName - Name of the desired printer
  27. Return Value:
  28. Pointer to a printer info structure or NULL for failure.
  29. --*/
  30. {
  31. PVOID PrinterInfo = NULL;
  32. HANDLE hPrinter;
  33. DWORD Bytes;
  34. PRINTER_DEFAULTS PrinterDefaults;
  35. PrinterDefaults.pDatatype = NULL;
  36. PrinterDefaults.pDevMode = NULL;
  37. PrinterDefaults.DesiredAccess = PRINTER_ACCESS_USE;
  38. if (!OpenPrinter( PrinterName, &hPrinter, &PrinterDefaults )) {
  39. goto exit;
  40. }
  41. if ((!GetPrinter( hPrinter, Level, NULL, 0, &Bytes )) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
  42. goto exit;
  43. }
  44. PrinterInfo = (LPPRINTER_INFO_2) MemAlloc( Bytes );
  45. if (!PrinterInfo) {
  46. goto exit;
  47. }
  48. if (!GetPrinter( hPrinter, Level, (LPBYTE) PrinterInfo, Bytes, &Bytes )) {
  49. goto exit;
  50. }
  51. exit:
  52. ClosePrinter( hPrinter );
  53. return PrinterInfo;
  54. }
  55. VOID
  56. CXPLogon::PrintRichText(
  57. HWND hWndRichEdit,
  58. HDC hDC,
  59. PFAXXP_CONFIG FaxConfig
  60. )
  61. /*++
  62. Routine Description:
  63. Prints the rich text contained in a rich text
  64. window into a DC.
  65. Arguments:
  66. hWndRichEdit - Window handle for the rich text window
  67. hDC - Printer device context
  68. Return Value:
  69. None.
  70. --*/
  71. {
  72. FORMATRANGE fr;
  73. LONG lTextOut;
  74. LONG lTextAmt;
  75. RECT rcTmp;
  76. LPTSTR szText;
  77. LPTSTR szChar;
  78. fr.hdc = hDC;
  79. fr.hdcTarget = hDC;
  80. fr.chrg.cpMin = 0;
  81. fr.chrg.cpMax = -1;
  82. //
  83. // Set page rect to phys page size in twips
  84. //
  85. fr.rcPage.top = 0;
  86. fr.rcPage.left = 0;
  87. fr.rcPage.right = MulDiv(GetDeviceCaps(hDC, PHYSICALWIDTH),
  88. 1440,
  89. GetDeviceCaps(hDC, LOGPIXELSX));
  90. fr.rcPage.bottom = MulDiv(GetDeviceCaps(hDC, PHYSICALHEIGHT),
  91. 1440,
  92. GetDeviceCaps(hDC, LOGPIXELSY));
  93. //
  94. // Set up 3/4" horizontal and 1" vertical margins, but leave a minimum of 1"
  95. // printable space in each direction. Otherwise, use full page.
  96. //
  97. fr.rc = fr.rcPage; // start with full page
  98. if (fr.rcPage.right > 2*3*1440/4 + 1440) {
  99. fr.rc.right -= (fr.rc.left = 3*1440/4);
  100. }
  101. if (fr.rcPage.bottom > 3*1440) {
  102. fr.rc.bottom -= (fr.rc.top = 1440);
  103. }
  104. //
  105. // save the formatting rectangle
  106. //
  107. rcTmp = fr.rc;
  108. SetMapMode( hDC, MM_TEXT );
  109. lTextOut = 0;
  110. lTextAmt = (LONG)SendMessage( hWndRichEdit, WM_GETTEXTLENGTH, 0, 0 );
  111. szText = (LPTSTR) MemAlloc((lTextAmt + 1) * sizeof(TCHAR));
  112. if (szText) {
  113. SendMessage(hWndRichEdit, WM_GETTEXT, (WPARAM) lTextAmt, (LPARAM) szText);
  114. szChar = szText;
  115. while (lTextOut < lTextAmt) {
  116. if ((*szChar != ' ') && (*szChar != '\t') && (*szChar != '\r') && (*szChar != '\n')) {
  117. break;
  118. }
  119. lTextOut++;
  120. szChar = CharNext(szChar);
  121. }
  122. MemFree(szText);
  123. if (!strlen(szChar)) {
  124. lTextAmt = 0;
  125. }
  126. }
  127. while (lTextOut < lTextAmt) {
  128. // Reset margins
  129. fr.rc = rcTmp;
  130. StartPage(hDC);
  131. lTextOut = (LONG) SendMessage(hWndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
  132. EndPage(hDC);
  133. fr.chrg.cpMin = lTextOut;
  134. fr.chrg.cpMax = -1;
  135. }
  136. //
  137. // flush the cache
  138. //
  139. SendMessage(hWndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
  140. }
  141. extern "C"
  142. DWORD CALLBACK
  143. EditStreamRead(
  144. DWORD_PTR dwCookie,
  145. LPBYTE pbBuff,
  146. LONG cb,
  147. LONG *pcb
  148. )
  149. /*++
  150. Routine Description:
  151. Wrapper function for the IStream read method.
  152. This function is used to read rich text from
  153. an exchange stream.
  154. Arguments:
  155. dwCookie - This pointer for the IStream object
  156. pbBuff - Pointer to the data buffer
  157. cb - Size of the data buffer
  158. pcb - Returned byte count
  159. Return Value:
  160. Return code from IStream::Read
  161. --*/
  162. {
  163. return ((LPSTREAM)dwCookie)->Read( pbBuff, cb, (ULONG*) pcb );
  164. }
  165. BOOL
  166. CXPLogon::PrintText(
  167. HDC hDC,
  168. LPSTREAM lpstmT,
  169. PFAXXP_CONFIG FaxConfig
  170. )
  171. /*++
  172. Routine Description:
  173. Prints a stream of plain text into the printer DC provided.
  174. Note: this code was stolen from notepad.
  175. Arguments:
  176. hDC - Printer DC
  177. lpstmT - Stream pointer for rich text.
  178. FaxConfig - Fax configuration data
  179. Return Value:
  180. TRUE for success.
  181. FALSE for failure.
  182. --*/
  183. {
  184. LPSTR BodyText = NULL;
  185. LPSTR lpLine;
  186. LPSTR pLineEOL;
  187. LPSTR pNextLine;
  188. HRESULT hResult;
  189. HFONT hFont = NULL;
  190. HFONT hPrevFont = NULL;
  191. TEXTMETRIC tm;
  192. BOOL rVal = TRUE;
  193. INT nLinesPerPage;
  194. INT dyTop; // width of top border (pixels)
  195. INT dyBottom; // width of bottom border
  196. INT dxLeft; // width of left border
  197. INT dxRight; // width of right border
  198. INT yPrintChar; // height of a character
  199. INT tabSize; // Size of a tab for print device in device units
  200. INT yCurpos = 0;
  201. INT xCurpos = 0;
  202. INT nPixelsLeft = 0;
  203. INT guess = 0;
  204. SIZE Size; // to see if text will fit in space left
  205. INT nPrintedLines = 0;
  206. BOOL fPageStarted = FALSE;
  207. INT iPageNum = 0;
  208. INT xPrintRes; // printer resolution in x direction
  209. INT yPrintRes; // printer resolution in y direction
  210. INT yPixInch; // pixels/inch
  211. INT xPixInch; // pixels/inch
  212. INT xPixUnit; // pixels/local measurement unit
  213. INT yPixUnit; // pixels/local measurement unit
  214. BOOL fEnglish;
  215. INT Chars;
  216. STATSTG Stats;
  217. INT PrevBkMode = 0;
  218. hResult = lpstmT->Stat( &Stats, 0 );
  219. if (hResult) {
  220. rVal = FALSE;
  221. goto exit;
  222. }
  223. Chars = (INT) Stats.cbSize.QuadPart;
  224. BodyText = (LPSTR) MemAlloc( Chars + 4 );
  225. if (!BodyText) {
  226. rVal = FALSE;
  227. goto exit;
  228. }
  229. hResult = lpstmT->Read( (LPVOID) BodyText, Chars, (LPDWORD) &Chars );
  230. if (hResult) {
  231. rVal = FALSE;
  232. goto exit;
  233. }
  234. lpLine = BodyText;
  235. fEnglish = GetProfileInt( "intl", "iMeasure", 1 );
  236. xPrintRes = GetDeviceCaps( hDC, HORZRES );
  237. yPrintRes = GetDeviceCaps( hDC, VERTRES );
  238. xPixInch = GetDeviceCaps( hDC, LOGPIXELSX );
  239. yPixInch = GetDeviceCaps( hDC, LOGPIXELSY );
  240. //
  241. // compute x and y pixels per local measurement unit
  242. //
  243. if (fEnglish) {
  244. xPixUnit= xPixInch;
  245. yPixUnit= yPixInch;
  246. } else {
  247. xPixUnit= CMToInches( xPixInch );
  248. yPixUnit= CMToInches( yPixInch );
  249. }
  250. SetMapMode( hDC, MM_TEXT );
  251. hFont = CreateFontIndirect( &FaxConfig->FontStruct );
  252. hPrevFont = (HFONT) SelectObject( hDC, hFont );
  253. SetBkMode( hDC, TRANSPARENT );
  254. if (!GetTextMetrics( hDC, &tm )) {
  255. rVal = FALSE;
  256. goto exit;
  257. }
  258. yPrintChar = tm.tmHeight + tm.tmExternalLeading;
  259. tabSize = tm.tmAveCharWidth * 8;
  260. //
  261. // compute margins in pixels
  262. //
  263. dxLeft = LEFT_MARGIN * xPixUnit;
  264. dxRight = RIGHT_MARGIN * xPixUnit;
  265. dyTop = TOP_MARGIN * yPixUnit;
  266. dyBottom = BOTTOM_MARGIN * yPixUnit;
  267. //
  268. // Number of lines on a page with margins
  269. //
  270. nLinesPerPage = ((yPrintRes - dyTop - dyBottom) / yPrintChar);
  271. while (*lpLine) {
  272. if (*lpLine == '\r') {
  273. lpLine += 2;
  274. yCurpos += yPrintChar;
  275. nPrintedLines++;
  276. xCurpos= 0;
  277. continue;
  278. }
  279. pLineEOL = lpLine;
  280. while (*pLineEOL && *pLineEOL != '\r') pLineEOL++;
  281. do {
  282. if ((nPrintedLines == 0) && (!fPageStarted)) {
  283. StartPage( hDC );
  284. fPageStarted = TRUE;
  285. yCurpos = 0;
  286. xCurpos = 0;
  287. }
  288. if (*lpLine == '\t') {
  289. //
  290. // round up to the next tab stop
  291. // if the current position is on the tabstop, goto next one
  292. //
  293. xCurpos = ((xCurpos + tabSize) / tabSize ) * tabSize;
  294. lpLine++;
  295. } else {
  296. //
  297. // find end of line or tab
  298. //
  299. pNextLine = lpLine;
  300. while ((pNextLine != pLineEOL) && *pNextLine != '\t') pNextLine++;
  301. //
  302. // find out how many characters will fit on line
  303. //
  304. Chars = (INT)(pNextLine - lpLine);
  305. nPixelsLeft = xPrintRes - dxRight - dxLeft - xCurpos;
  306. GetTextExtentExPoint( hDC, lpLine, Chars, nPixelsLeft, &guess, NULL, &Size );
  307. if (guess) {
  308. //
  309. // at least one character fits - print
  310. //
  311. TextOut( hDC, dxLeft+xCurpos, yCurpos+dyTop, lpLine, guess );
  312. xCurpos += Size.cx; // account for printing
  313. lpLine += guess; // printed characters
  314. } else {
  315. //
  316. // no characters fit what's left
  317. // no characters will fit in space left
  318. // if none ever will, just print one
  319. // character to keep progressing through
  320. // input file.
  321. //
  322. if (xCurpos == 0) {
  323. if( lpLine != pNextLine ) {
  324. //
  325. // print something if not null line
  326. // could use exttextout here to clip
  327. //
  328. TextOut( hDC, dxLeft+xCurpos, yCurpos+dyTop, lpLine, 1 );
  329. lpLine++;
  330. }
  331. } else {
  332. //
  333. // perhaps the next line will get it
  334. //
  335. xCurpos = xPrintRes; // force to next line
  336. }
  337. }
  338. //
  339. // move printhead in y-direction
  340. //
  341. if ((xCurpos >= (xPrintRes - dxRight - dxLeft) ) || (lpLine == pLineEOL)) {
  342. yCurpos += yPrintChar;
  343. nPrintedLines++;
  344. xCurpos = 0;
  345. }
  346. if (nPrintedLines >= nLinesPerPage) {
  347. EndPage( hDC );
  348. fPageStarted = FALSE;
  349. nPrintedLines = 0;
  350. xCurpos = 0;
  351. yCurpos = 0;
  352. iPageNum++;
  353. }
  354. }
  355. } while( lpLine != pLineEOL );
  356. if (*lpLine == '\r') {
  357. lpLine += 1;
  358. }
  359. if (*lpLine == '\n') {
  360. lpLine += 1;
  361. }
  362. }
  363. if (fPageStarted) {
  364. EndPage( hDC );
  365. }
  366. exit:
  367. MemFree( BodyText );
  368. if (hPrevFont) {
  369. SelectObject( hDC, hPrevFont );
  370. DeleteObject( hFont );
  371. }
  372. if (PrevBkMode) {
  373. SetBkMode( hDC, PrevBkMode );
  374. }
  375. return rVal;
  376. }
  377. DWORD
  378. CXPLogon::PrintAttachment(
  379. LPSTR FaxPrinterName,
  380. LPSTR DocName
  381. )
  382. /*++
  383. Routine Description:
  384. Prints a document that is attached to a message
  385. Arguments:
  386. FaxPrinterName - name of the printer to print the attachment on
  387. DocName - name of the attachment document
  388. Return Value:
  389. Print job id or zero for failure.
  390. --*/
  391. {
  392. SHELLEXECUTEINFO sei;
  393. CHAR Args[MAX_PATH];
  394. CHAR TempDir[MAX_PATH];
  395. HANDLE hMap = NULL;
  396. HANDLE hEvent = NULL;
  397. HANDLE hMutex = NULL;
  398. HANDLE hMutexAttach = NULL;
  399. LPDWORD pJobId = NULL;
  400. DWORD JobId = 0;
  401. #ifndef WIN95
  402. SECURITY_ATTRIBUTES memsa,mutsa,synsa,eventsa;
  403. SECURITY_DESCRIPTOR memsd,mutsd,synsd,eventsd;
  404. #endif
  405. //
  406. // serialize access to this function.
  407. // this is necessary because we can't have more than one
  408. // app accessing our shared memory region and mutex
  409. //
  410. hMutexAttach = OpenMutex(MUTEX_ALL_ACCESS,FALSE,FAXRENDER_MUTEX);
  411. if (!hMutexAttach) {
  412. //
  413. // we need to open this mutex with a NULL dacl so that everyone can access this
  414. //
  415. #ifndef WIN95
  416. synsa.nLength = sizeof(SECURITY_ATTRIBUTES);
  417. synsa.bInheritHandle = TRUE;
  418. synsa.lpSecurityDescriptor = &synsd;
  419. if(!InitializeSecurityDescriptor(&synsd, SECURITY_DESCRIPTOR_REVISION)) {
  420. goto exit;
  421. }
  422. if(!SetSecurityDescriptorDacl(&synsd, TRUE, (PACL)NULL, FALSE)) {
  423. goto exit;
  424. }
  425. #endif
  426. hMutexAttach = CreateMutex(
  427. #ifndef WIN95
  428. &synsa,
  429. #else
  430. NULL,
  431. #endif
  432. TRUE,
  433. FAXRENDER_MUTEX
  434. );
  435. if (!hMutexAttach) {
  436. goto exit;
  437. }
  438. } else {
  439. if (WaitForSingleObject( hMutexAttach, 1000* 60 * 5) != WAIT_OBJECT_0) {
  440. //
  441. // something went wrong
  442. //
  443. CloseHandle( hMutexAttach );
  444. goto exit;
  445. }
  446. }
  447. //
  448. // since mapispooler might be running under a different security context,
  449. // we must setup a NULL security descriptor
  450. //
  451. #ifndef WIN95
  452. memsa.nLength = sizeof(SECURITY_ATTRIBUTES);
  453. memsa.bInheritHandle = TRUE;
  454. memsa.lpSecurityDescriptor = &memsd;
  455. if(!InitializeSecurityDescriptor(&memsd, SECURITY_DESCRIPTOR_REVISION)) {
  456. goto exit;
  457. }
  458. if(!SetSecurityDescriptorDacl(&memsd, TRUE, (PACL)NULL, FALSE)) {
  459. goto exit;
  460. }
  461. mutsa.nLength = sizeof(SECURITY_ATTRIBUTES);
  462. mutsa.bInheritHandle = TRUE;
  463. mutsa.lpSecurityDescriptor = &mutsd;
  464. if(!InitializeSecurityDescriptor(&mutsd, SECURITY_DESCRIPTOR_REVISION)) {
  465. goto exit;
  466. }
  467. if(!SetSecurityDescriptorDacl(&mutsd, TRUE, (PACL)NULL, FALSE)) {
  468. goto exit;
  469. }
  470. eventsa.nLength = sizeof(SECURITY_ATTRIBUTES);
  471. eventsa.bInheritHandle = TRUE;
  472. eventsa.lpSecurityDescriptor = &eventsd;
  473. if(!InitializeSecurityDescriptor(&eventsd, SECURITY_DESCRIPTOR_REVISION)) {
  474. goto exit;
  475. }
  476. if(!SetSecurityDescriptorDacl(&eventsd, TRUE, (PACL)NULL, FALSE)) {
  477. goto exit;
  478. }
  479. #endif
  480. //
  481. // create the shared memory region for the print jobid
  482. // the jobid is filled in by the fax printer driver
  483. //
  484. hMap = CreateFileMapping(
  485. INVALID_HANDLE_VALUE,
  486. #ifndef WIN95
  487. &memsa,
  488. #else
  489. NULL,
  490. #endif
  491. PAGE_READWRITE | SEC_COMMIT,
  492. 0,
  493. 4096,
  494. FAXXP_MEM_NAME
  495. );
  496. if (!hMap) {
  497. goto exit;
  498. }
  499. pJobId = (LPDWORD) MapViewOfFile(
  500. hMap,
  501. FILE_MAP_WRITE,
  502. 0,
  503. 0,
  504. 0
  505. );
  506. if (!pJobId) {
  507. goto exit;
  508. }
  509. *pJobId = (DWORD) 0;
  510. //
  511. // get the temp path name and use it for the
  512. // working dir of the launched app
  513. //
  514. GetTempPath( sizeof(TempDir), TempDir );
  515. //
  516. // set the arguments to the app.
  517. // these arguments are either passed on
  518. // the command line with the /pt switch or
  519. // use as variables for substitution in the
  520. // ddeexec value in the registry.
  521. //
  522. // the values are as follows:
  523. // %1 = file name
  524. // %2 = printer name
  525. // %3 = driver name
  526. // %4 = port name
  527. //
  528. // the first argument does not need to be
  529. // supplied in the args array because it is implied,
  530. // shellexecuteex gets it from the lpFile field.
  531. // arguments 3 & 4 are left blank because they
  532. // are win31 artifacts that are not necessary
  533. // any more. each argument must be enclosed
  534. // in double quotes.
  535. //
  536. wsprintf( Args, "\"%s\" \"\" \"\"", FaxPrinterName );
  537. //
  538. // fill in the SHELLEXECUTEINFO structure
  539. //
  540. sei.cbSize = sizeof(sei);
  541. sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT;
  542. sei.hwnd = NULL;
  543. sei.lpVerb = "printto";
  544. sei.lpFile = DocName;
  545. sei.lpParameters = Args;
  546. sei.lpDirectory = TempDir;
  547. sei.nShow = SW_SHOWMINNOACTIVE;
  548. sei.hInstApp = NULL;
  549. sei.lpIDList = NULL;
  550. sei.lpClass = NULL;
  551. sei.hkeyClass = NULL;
  552. sei.dwHotKey = 0;
  553. sei.hIcon = NULL;
  554. sei.hProcess = NULL;
  555. //
  556. // create the named mutex for the print driver.
  557. // this is initially unclaimed, and is claimed by the first instance
  558. // of the print driver invoked after this. We do this last in order to
  559. // avoid a situation where we catch the incorrect instance of the print driver
  560. // printing
  561. //
  562. hMutex = CreateMutex(
  563. #ifndef WIN95
  564. &mutsa,
  565. #else
  566. NULL,
  567. #endif
  568. FALSE,
  569. FAXXP_MUTEX_NAME
  570. );
  571. if (!hMutex) {
  572. goto exit;
  573. }
  574. //
  575. // create the named event for the print driver.
  576. // this event is signaled when the print driver is finished rendering the document
  577. //
  578. hEvent = CreateEvent(
  579. #ifndef WIN95
  580. &eventsa,
  581. #else
  582. NULL,
  583. #endif
  584. FALSE,
  585. FALSE,
  586. FAXXP_EVENT_NAME
  587. );
  588. if (!hEvent) {
  589. goto exit;
  590. }
  591. //
  592. // launch the app
  593. //
  594. if (!ShellExecuteEx( &sei )) {
  595. goto exit;
  596. }
  597. //
  598. // wait for the print driver to finish rendering the document
  599. //
  600. if (WaitForSingleObject( hEvent, 1000 * 60 * 5 ) != WAIT_OBJECT_0) {
  601. //
  602. // something went wrong...
  603. //
  604. goto exit;
  605. }
  606. //
  607. // wait for the print driver to exit so we can get the document
  608. //
  609. if (WaitForSingleObject( hMutex, 1000 * 60 * 5) != WAIT_OBJECT_0) {
  610. //
  611. // something went wrong
  612. //
  613. goto exit;
  614. }
  615. ReleaseMutex(hMutex);
  616. //
  617. // save the print jobid
  618. //
  619. JobId = *pJobId;
  620. exit:
  621. //
  622. // clean up and leave...
  623. //
  624. if (sei.hProcess) CloseHandle( sei.hProcess );
  625. if (hEvent) CloseHandle( hEvent );
  626. if (hMutex) CloseHandle( hMutex );
  627. if (pJobId) UnmapViewOfFile( pJobId );
  628. if (hMap) CloseHandle( hMap );
  629. if (hMutexAttach) {
  630. ReleaseMutex( hMutexAttach );
  631. CloseHandle( hMutexAttach );
  632. }
  633. return JobId;
  634. }
  635. DWORD
  636. CXPLogon::SendFaxDocument(
  637. LPMESSAGE pMsgObj,
  638. LPSTREAM lpstmT,
  639. BOOL UseRichText,
  640. LPSPropValue pMsgProps,
  641. LPSPropValue pRecipProps
  642. )
  643. /*++
  644. Routine Description:
  645. Prints an exchange message to the fax printer.
  646. Arguments:
  647. lpstmT - Stream pointer for rich text.
  648. pMsgProps - Message properties.
  649. pRecipProps - Recipient properties.
  650. Return Value:
  651. Zero for success, otherwise error code.
  652. --*/
  653. {
  654. PPRINTER_INFO_2 PrinterInfo = NULL;
  655. PRINTER_DEFAULTS PrinterDefaults;
  656. HANDLE hPrinter = NULL;
  657. HDC hDC = NULL;
  658. INT JobId;
  659. DWORD Bytes;
  660. DWORD ec;
  661. DWORD rVal = 0;
  662. HRESULT hResult;
  663. LPSTREAM lpstm = NULL;
  664. EDITSTREAM es = {0};
  665. HWND hWndRichEdit = NULL;
  666. LPPROFSECT pProfileObj = NULL;
  667. ULONG PropCount = 0;
  668. ULONG PropMsgCount = 0;
  669. LPSPropValue pPropsAttachTable = NULL;
  670. LPSPropValue pPropsAttach = NULL;
  671. LPSPropValue pProps = NULL;
  672. LPSPropValue pPropsMsg = NULL;
  673. FAXXP_CONFIG FaxConfig = {0};
  674. DWORD JobIdAttachment = 0;
  675. INT i = 0;
  676. LPMAPITABLE AttachmentTable = NULL;
  677. LPSRowSet pAttachmentRows = NULL;
  678. LPATTACH lpAttach = NULL;
  679. LPSTREAM lpstmA = NULL;
  680. LPSTR AttachFileName = NULL;
  681. CHAR TempPath[MAX_PATH];
  682. CHAR TempFile[MAX_PATH];
  683. CHAR DocFile[MAX_PATH];
  684. HANDLE hFile = INVALID_HANDLE_VALUE;
  685. LPSTR DocType = NULL;
  686. DWORD LastJobId = 0;
  687. PJOB_INFO_1 JobInfo1 = NULL;
  688. JOB_INFO_3 JobInfo3;
  689. LPSTR p;
  690. DWORD Pages = 0;
  691. BOOL DeleteAttachFile;
  692. LPSTR FileName = NULL;
  693. BOOL AllAttachmentsGood = TRUE;
  694. MAPINAMEID NameIds[NUM_FAX_MSG_PROPS];
  695. MAPINAMEID *pNameIds[NUM_FAX_MSG_PROPS] = {&NameIds[0], &NameIds[1], &NameIds[2], &NameIds[3]};
  696. LPSPropTagArray MsgPropTags = NULL;
  697. LARGE_INTEGER BigZero = {0};
  698. CHAR FullName[128];
  699. HKEY hKey;
  700. DWORD RegSize;
  701. DWORD RegType;
  702. DWORD CountPrinters;
  703. #ifdef WIN95
  704. LCID LocaleId;
  705. LPSTR lpszBuffer;
  706. DWORD BytesWrite;
  707. HINSTANCE hinstFwProv;
  708. PFWSPJ pfnStartPrintJob;
  709. #else
  710. USER_INFO UserInfo = {0};
  711. FAX_PRINT_INFOA FaxPrintInfo = {0};
  712. FAX_CONTEXT_INFO ContextInfo = {0};
  713. FAX_COVERPAGE_INFOA FaxCpInfo = {0};
  714. // char szCoverPageName[MAX_PATH];
  715. #endif // WIN95
  716. //
  717. // get the fax config properties
  718. //
  719. hResult = m_pSupObj->OpenProfileSection(
  720. &FaxGuid,
  721. MAPI_MODIFY,
  722. &pProfileObj
  723. );
  724. if (HR_FAILED (hResult)) {
  725. rVal = IDS_CANT_ACCESS_PROFILE;
  726. goto exit;
  727. }
  728. hResult = pProfileObj->GetProps(
  729. (LPSPropTagArray) &sptFaxProps,
  730. 0,
  731. &PropCount,
  732. &pProps
  733. );
  734. if (FAILED(hResult)) {
  735. rVal = IDS_CANT_ACCESS_PROFILE;
  736. goto exit;
  737. }
  738. FaxConfig.PrinterName = StringDup( pProps[PROP_FAX_PRINTER_NAME].Value.LPSZ );
  739. FaxConfig.CoverPageName = StringDup( pProps[PROP_COVERPAGE_NAME].Value.LPSZ );
  740. FaxConfig.UseCoverPage = pProps[PROP_USE_COVERPAGE].Value.ul;
  741. FaxConfig.ServerCoverPage = pProps[PROP_SERVER_COVERPAGE].Value.ul;
  742. CopyMemory( &FaxConfig.FontStruct, pProps[PROP_FONT].Value.bin.lpb, pProps[PROP_FONT].Value.bin.cb );
  743. //
  744. // now get the message properties
  745. //
  746. NameIds[MSGPI_FAX_PRINTER_NAME].lpguid = (LPGUID)&PS_PUBLIC_STRINGS;
  747. NameIds[MSGPI_FAX_PRINTER_NAME].ulKind = MNID_STRING;
  748. NameIds[MSGPI_FAX_PRINTER_NAME].Kind.lpwstrName = MSGPS_FAX_PRINTER_NAME;
  749. NameIds[MSGPI_FAX_COVERPAGE_NAME].lpguid = (LPGUID)&PS_PUBLIC_STRINGS;
  750. NameIds[MSGPI_FAX_COVERPAGE_NAME].ulKind = MNID_STRING;
  751. NameIds[MSGPI_FAX_COVERPAGE_NAME].Kind.lpwstrName = MSGPS_FAX_COVERPAGE_NAME;
  752. NameIds[MSGPI_FAX_USE_COVERPAGE].lpguid = (LPGUID)&PS_PUBLIC_STRINGS;
  753. NameIds[MSGPI_FAX_USE_COVERPAGE].ulKind = MNID_STRING;
  754. NameIds[MSGPI_FAX_USE_COVERPAGE].Kind.lpwstrName = MSGPS_FAX_USE_COVERPAGE;
  755. NameIds[MSGPI_FAX_SERVER_COVERPAGE].lpguid = (LPGUID)&PS_PUBLIC_STRINGS;
  756. NameIds[MSGPI_FAX_SERVER_COVERPAGE].ulKind = MNID_STRING;
  757. NameIds[MSGPI_FAX_SERVER_COVERPAGE].Kind.lpwstrName = MSGPS_FAX_SERVER_COVERPAGE;
  758. hResult = pMsgObj->GetIDsFromNames( NUM_FAX_MSG_PROPS, pNameIds, MAPI_CREATE, &MsgPropTags );
  759. if (FAILED(hResult)) {
  760. rVal = IDS_CANT_ACCESS_PROFILE;
  761. goto exit;
  762. }
  763. MsgPropTags->aulPropTag[MSGPI_FAX_PRINTER_NAME] = PROP_TAG( PT_TSTRING, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_PRINTER_NAME]) );
  764. MsgPropTags->aulPropTag[MSGPI_FAX_COVERPAGE_NAME] = PROP_TAG( PT_TSTRING, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_COVERPAGE_NAME]) );
  765. MsgPropTags->aulPropTag[MSGPI_FAX_USE_COVERPAGE] = PROP_TAG( PT_LONG, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_USE_COVERPAGE]) );
  766. MsgPropTags->aulPropTag[MSGPI_FAX_SERVER_COVERPAGE] = PROP_TAG( PT_LONG, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_SERVER_COVERPAGE]) );
  767. hResult = pMsgObj->GetProps( MsgPropTags, 0, &PropMsgCount, &pPropsMsg );
  768. if (FAILED(hResult)) {
  769. rVal = IDS_CANT_ACCESS_PROFILE;
  770. goto exit;
  771. }
  772. if (PROP_TYPE(pPropsMsg[MSGPI_FAX_PRINTER_NAME].ulPropTag) != PT_ERROR) {
  773. MemFree( FaxConfig.PrinterName );
  774. FaxConfig.PrinterName = StringDup( pPropsMsg[MSGPI_FAX_PRINTER_NAME].Value.LPSZ );
  775. }
  776. if (PROP_TYPE(pPropsMsg[MSGPI_FAX_COVERPAGE_NAME].ulPropTag) != PT_ERROR) {
  777. MemFree( FaxConfig.CoverPageName );
  778. FaxConfig.CoverPageName = StringDup( pPropsMsg[MSGPI_FAX_COVERPAGE_NAME].Value.LPSZ );
  779. }
  780. if (PROP_TYPE(pPropsMsg[MSGPI_FAX_USE_COVERPAGE].ulPropTag) != PT_ERROR) {
  781. FaxConfig.UseCoverPage = pPropsMsg[MSGPI_FAX_USE_COVERPAGE].Value.ul;
  782. }
  783. if (PROP_TYPE(pPropsMsg[MSGPI_FAX_SERVER_COVERPAGE].ulPropTag) != PT_ERROR) {
  784. FaxConfig.ServerCoverPage = pPropsMsg[MSGPI_FAX_SERVER_COVERPAGE].Value.ul;
  785. }
  786. //
  787. // open the printer
  788. //
  789. PrinterInfo = (PPRINTER_INFO_2) MyGetPrinter( FaxConfig.PrinterName, 2 );
  790. if (!PrinterInfo) {
  791. PrinterInfo = (PPRINTER_INFO_2) MyEnumPrinters( NULL, 2, &CountPrinters, PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS );
  792. if (PrinterInfo) {
  793. for (i=0; i<(int)CountPrinters; i++) {
  794. if (strcmp( PrinterInfo[i].pDriverName, FAX_DRIVER_NAME ) == 0) {
  795. break;
  796. }
  797. }
  798. } else {
  799. CountPrinters = i = 0;
  800. }
  801. if (i == (int)CountPrinters) {
  802. rVal = IDS_NO_FAX_PRINTER;
  803. goto exit;
  804. }
  805. MemFree( FaxConfig.PrinterName );
  806. FaxConfig.PrinterName = StringDup( PrinterInfo[i].pPrinterName );
  807. MemFree( PrinterInfo );
  808. PrinterInfo = (PPRINTER_INFO_2) MyGetPrinter( FaxConfig.PrinterName, 2 );
  809. if (!PrinterInfo) {
  810. rVal = IDS_CANT_ACCESS_PROFILE;
  811. goto exit;
  812. }
  813. }
  814. PrinterDefaults.pDatatype = NULL;
  815. PrinterDefaults.pDevMode = NULL;
  816. PrinterDefaults.DesiredAccess = PRINTER_ACCESS_USE;
  817. if (!OpenPrinter( FaxConfig.PrinterName, &hPrinter, &PrinterDefaults )) {
  818. rVal = IDS_CANT_ACCESS_PRINTER;
  819. goto exit;
  820. }
  821. //
  822. // get the attachment table, if it is available
  823. //
  824. hResult = pMsgObj->GetAttachmentTable( 0, &AttachmentTable );
  825. if (HR_SUCCEEDED(hResult)) {
  826. hResult = HrAddColumns(
  827. AttachmentTable,
  828. (LPSPropTagArray) &sptAttachTableProps,
  829. gpfnAllocateBuffer,
  830. gpfnFreeBuffer
  831. );
  832. if (HR_SUCCEEDED(hResult)) {
  833. hResult = HrQueryAllRows(
  834. AttachmentTable,
  835. NULL,
  836. NULL,
  837. NULL,
  838. 0,
  839. &pAttachmentRows
  840. );
  841. if (FAILED(hResult)) {
  842. pAttachmentRows = NULL;
  843. } else {
  844. if (pAttachmentRows->cRows == 0) {
  845. MemFree( pAttachmentRows );
  846. pAttachmentRows = NULL;
  847. }
  848. }
  849. }
  850. }
  851. if (pAttachmentRows) {
  852. //
  853. // this loop verifies that each document's attachment registration
  854. // supports the printto verb.
  855. //
  856. AllAttachmentsGood = TRUE;
  857. for (i=pAttachmentRows->cRows-1; i>=0; i--) {
  858. pPropsAttachTable = pAttachmentRows->aRow[i].lpProps;
  859. lpAttach = NULL;
  860. pPropsAttach = NULL;
  861. if (pPropsAttachTable[MSG_ATTACH_METHOD].Value.ul == NO_ATTACHMENT) {
  862. goto next_attachment1;
  863. }
  864. //
  865. // open the attachment
  866. //
  867. hResult = pMsgObj->OpenAttach( pPropsAttachTable[MSG_ATTACH_NUM].Value.ul, NULL, MAPI_BEST_ACCESS, &lpAttach );
  868. if (FAILED(hResult)) {
  869. AllAttachmentsGood = FALSE;
  870. goto next_attachment1;
  871. }
  872. //
  873. // get the attachment properties
  874. //
  875. hResult = lpAttach->GetProps(
  876. (LPSPropTagArray) &sptAttachProps,
  877. 0,
  878. &PropCount,
  879. &pPropsAttach
  880. );
  881. if (FAILED(hResult)) {
  882. AllAttachmentsGood = FALSE;
  883. goto next_attachment1;
  884. }
  885. //
  886. // try to get the extension if the file.
  887. // this indicates what type of dicument it is.
  888. // if we cannot get the document type then it is
  889. // impossible to print the document.
  890. //
  891. if (DocType) {
  892. MemFree( DocType );
  893. DocType = NULL;
  894. }
  895. if (PROP_TYPE(pPropsAttach[MSG_ATTACH_EXTENSION].ulPropTag) == PT_ERROR) {
  896. if (PROP_TYPE(pPropsAttach[MSG_ATTACH_LFILENAME].ulPropTag) != PT_ERROR) {
  897. p = strchr( pPropsAttach[MSG_ATTACH_LFILENAME].Value.LPSZ, '.' );
  898. if (p) {
  899. DocType = StringDup( p );
  900. }
  901. } else if (PROP_TYPE(pPropsAttach[MSG_ATTACH_FILENAME].ulPropTag) != PT_ERROR) {
  902. p = strchr( pPropsAttach[MSG_ATTACH_FILENAME].Value.LPSZ, '.' );
  903. if (p) {
  904. DocType = StringDup( p );
  905. }
  906. }
  907. } else {
  908. DocType = StringDup( pPropsAttach[MSG_ATTACH_EXTENSION].Value.LPSZ );
  909. }
  910. if (!DocType) {
  911. AllAttachmentsGood = FALSE;
  912. goto next_attachment1;
  913. }
  914. Bytes = sizeof(TempFile);
  915. rVal = RegQueryValue( HKEY_CLASSES_ROOT, DocType, TempFile, (PLONG) &Bytes );
  916. if ((rVal != ERROR_SUCCESS) && (rVal != ERROR_INVALID_DATA)) {
  917. AllAttachmentsGood = FALSE;
  918. goto next_attachment1;
  919. }
  920. wsprintf( TempPath, "%s\\shell\\printto\\command", TempFile );
  921. Bytes = sizeof(TempFile);
  922. rVal = RegQueryValue( HKEY_CLASSES_ROOT, TempPath, TempFile, (PLONG) &Bytes );
  923. if ((rVal != ERROR_SUCCESS) && (rVal != ERROR_INVALID_DATA)) {
  924. AllAttachmentsGood = FALSE;
  925. goto next_attachment1;
  926. }
  927. next_attachment1:
  928. if (lpAttach) {
  929. lpAttach->Release();
  930. }
  931. if (pPropsAttach) {
  932. MemFree( pPropsAttach );
  933. }
  934. }
  935. if (!AllAttachmentsGood) {
  936. rVal = IDS_BAD_ATTACHMENTS;
  937. goto exit;
  938. }
  939. for (i=pAttachmentRows->cRows-1; i>=0; i--) {
  940. pPropsAttachTable = pAttachmentRows->aRow[i].lpProps;
  941. lpAttach = NULL;
  942. pPropsAttach = NULL;
  943. if (pPropsAttachTable[MSG_ATTACH_METHOD].Value.ul == NO_ATTACHMENT) {
  944. goto next_attachment2;
  945. }
  946. //
  947. // open the attachment
  948. //
  949. hResult = pMsgObj->OpenAttach( pPropsAttachTable[MSG_ATTACH_NUM].Value.ul, NULL, MAPI_BEST_ACCESS, &lpAttach );
  950. if (FAILED(hResult)) {
  951. goto next_attachment2;
  952. }
  953. //
  954. // get the attachment properties
  955. //
  956. hResult = lpAttach->GetProps(
  957. (LPSPropTagArray) &sptAttachProps,
  958. 0,
  959. &PropCount,
  960. &pPropsAttach
  961. );
  962. if (FAILED(hResult)) {
  963. goto next_attachment2;
  964. }
  965. //
  966. // try to get the extension if the file.
  967. // this indicates what type of dicument it is.
  968. // if we cannot get the document type then it is
  969. // impossible to print the document.
  970. //
  971. if (DocType) {
  972. MemFree( DocType );
  973. DocType = NULL;
  974. }
  975. if (PROP_TYPE(pPropsAttach[MSG_ATTACH_EXTENSION].ulPropTag) == PT_ERROR) {
  976. if (PROP_TYPE(pPropsAttach[MSG_ATTACH_LFILENAME].ulPropTag) != PT_ERROR) {
  977. p = strchr( pPropsAttach[MSG_ATTACH_LFILENAME].Value.LPSZ, '.' );
  978. if (p) {
  979. DocType = StringDup( p );
  980. }
  981. } else if (PROP_TYPE(pPropsAttach[MSG_ATTACH_FILENAME].ulPropTag) != PT_ERROR) {
  982. p = strchr( pPropsAttach[MSG_ATTACH_FILENAME].Value.LPSZ, '.' );
  983. if (p) {
  984. DocType = StringDup( p );
  985. }
  986. }
  987. } else {
  988. DocType = StringDup( pPropsAttach[MSG_ATTACH_EXTENSION].Value.LPSZ );
  989. }
  990. if (!DocType) {
  991. goto next_attachment2;
  992. }
  993. lpstmA = NULL;
  994. AttachFileName = NULL;
  995. DeleteAttachFile = FALSE;
  996. //
  997. // get the attached file name so that we can resolve any links
  998. //
  999. if (PROP_TYPE(pPropsAttach[MSG_ATTACH_PATHNAME].ulPropTag) != PT_ERROR) {
  1000. FileName = pPropsAttach[MSG_ATTACH_PATHNAME].Value.LPSZ;
  1001. } else {
  1002. FileName = NULL;
  1003. }
  1004. if (_stricmp( DocType, LNK_FILENAME_EXT ) == 0) {
  1005. if (!FileName) {
  1006. goto next_attachment2;
  1007. }
  1008. if (ResolveShortcut( FileName, DocFile )) {
  1009. p = strchr( DocFile, '.' );
  1010. if (p) {
  1011. MemFree( DocType );
  1012. DocType = StringDup( p );
  1013. AttachFileName = StringDup( DocFile );
  1014. }
  1015. }
  1016. } else if (FileName) {
  1017. AttachFileName = StringDup( FileName );
  1018. }
  1019. //
  1020. // get the stream object
  1021. //
  1022. switch( pPropsAttach[MSG_ATTACH_METHOD].Value.ul ) {
  1023. case ATTACH_BY_VALUE:
  1024. hResult = lpAttach->OpenProperty(
  1025. PR_ATTACH_DATA_BIN,
  1026. &IID_IStream,
  1027. 0,
  1028. 0,
  1029. (LPUNKNOWN*) &lpstmA
  1030. );
  1031. if (FAILED(hResult)) {
  1032. goto next_attachment2;
  1033. }
  1034. break;
  1035. case ATTACH_EMBEDDED_MSG:
  1036. case ATTACH_OLE:
  1037. hResult = lpAttach->OpenProperty(
  1038. PR_ATTACH_DATA_OBJ,
  1039. &IID_IStreamDocfile,
  1040. 0,
  1041. 0,
  1042. (LPUNKNOWN*) &lpstmA
  1043. );
  1044. if (FAILED(hResult)) {
  1045. hResult = lpAttach->OpenProperty(
  1046. PR_ATTACH_DATA_BIN,
  1047. &IID_IStreamDocfile,
  1048. 0,
  1049. 0,
  1050. (LPUNKNOWN*) &lpstmA
  1051. );
  1052. if (FAILED(hResult)) {
  1053. hResult = lpAttach->OpenProperty(
  1054. PR_ATTACH_DATA_OBJ,
  1055. &IID_IStorage,
  1056. 0,
  1057. 0,
  1058. (LPUNKNOWN*) &lpstmA
  1059. );
  1060. if (FAILED(hResult)) {
  1061. goto next_attachment2;
  1062. }
  1063. }
  1064. }
  1065. break;
  1066. }
  1067. if (lpstmA) {
  1068. GetTempPath( sizeof(TempPath), TempPath );
  1069. GetTempFileName( TempPath, "Fax", 0, TempFile );
  1070. hFile = CreateFile(
  1071. TempFile,
  1072. GENERIC_READ | GENERIC_WRITE,
  1073. 0,
  1074. NULL,
  1075. CREATE_ALWAYS,
  1076. 0,
  1077. NULL
  1078. );
  1079. if (hFile != INVALID_HANDLE_VALUE) {
  1080. #define BLOCK_SIZE (64*1024)
  1081. LPBYTE StrmData;
  1082. DWORD Bytes;
  1083. DWORD BytesWrite;
  1084. StrmData = (LPBYTE) MemAlloc( BLOCK_SIZE );
  1085. do {
  1086. hResult = lpstmA->Read( StrmData, BLOCK_SIZE, &Bytes );
  1087. if (FAILED(hResult)) {
  1088. break;
  1089. }
  1090. WriteFile( hFile, StrmData, Bytes, &BytesWrite, NULL );
  1091. } while (Bytes == BLOCK_SIZE);
  1092. CloseHandle( hFile );
  1093. MemFree( StrmData );
  1094. if (AttachFileName) {
  1095. MemFree( AttachFileName );
  1096. }
  1097. strcpy( DocFile, TempFile );
  1098. p = strchr( DocFile, '.' );
  1099. if (p) {
  1100. strcpy( p, DocType );
  1101. MoveFile( TempFile, DocFile );
  1102. AttachFileName = StringDup( DocFile );
  1103. } else {
  1104. AttachFileName = StringDup( TempFile );
  1105. }
  1106. DeleteAttachFile = TRUE;
  1107. }
  1108. lpstmA->Release();
  1109. }
  1110. if (AttachFileName) {
  1111. //
  1112. // print the attachment
  1113. //
  1114. JobIdAttachment = PrintAttachment( FaxConfig.PrinterName, AttachFileName );
  1115. if (JobIdAttachment) {
  1116. GetJob( hPrinter, JobIdAttachment, 1, NULL, 0, &Bytes );
  1117. JobInfo1 = (PJOB_INFO_1) MemAlloc( Bytes );
  1118. if (JobInfo1) {
  1119. if (GetJob( hPrinter, JobIdAttachment, 1, (LPBYTE) JobInfo1, Bytes, &Bytes )) {
  1120. Pages += JobInfo1->TotalPages;
  1121. }
  1122. MemFree( JobInfo1 );
  1123. }
  1124. if (LastJobId) {
  1125. JobInfo3.JobId = JobIdAttachment;
  1126. JobInfo3.NextJobId = LastJobId;
  1127. JobInfo3.Reserved = 0;
  1128. SetJob( hPrinter, JobIdAttachment, 3, (PBYTE) &JobInfo3, 0 );
  1129. SetJob( hPrinter, LastJobId, 0, NULL, JOB_CONTROL_RESUME );
  1130. }
  1131. LastJobId = JobIdAttachment;
  1132. }
  1133. if (DeleteAttachFile) {
  1134. DeleteFile( AttachFileName );
  1135. }
  1136. MemFree( AttachFileName );
  1137. }
  1138. next_attachment2:
  1139. if (lpAttach) {
  1140. lpAttach->Release();
  1141. }
  1142. if (pPropsAttach) {
  1143. MemFree( pPropsAttach );
  1144. }
  1145. }
  1146. }
  1147. FullName[0] = 0;
  1148. ec = RegOpenKey(
  1149. HKEY_CURRENT_USER,
  1150. REGKEY_FAX_USERINFO,
  1151. &hKey
  1152. );
  1153. if (ec == ERROR_SUCCESS) {
  1154. RegSize = sizeof(FullName);
  1155. ec = RegQueryValueEx(
  1156. hKey,
  1157. REGVAL_FULLNAME,
  1158. 0,
  1159. &RegType,
  1160. (LPBYTE) FullName,
  1161. &RegSize
  1162. );
  1163. RegCloseKey( hKey );
  1164. }
  1165. #ifdef WIN95
  1166. JobId = 0xffffffff;
  1167. //
  1168. // allocate FaxInfo structure
  1169. //
  1170. FaxInfo Fax_Info;
  1171. memset( &Fax_Info, 0, sizeof(FaxInfo) );
  1172. //
  1173. // provide recipient-specific information
  1174. //
  1175. strncpy(
  1176. Fax_Info.Recipients[0].szName,
  1177. pRecipProps[RECIP_NAME].Value.lpszA,
  1178. NAME_LEN + 1
  1179. );
  1180. strncpy(
  1181. Fax_Info.Recipients[0].szFaxNumber,
  1182. pRecipProps[RECIP_EMAIL_ADR].Value.lpszA,
  1183. PHONE_NUMBER_LEN
  1184. );
  1185. Fax_Info.nRecipientCount = 1;
  1186. //
  1187. // provide cover page information
  1188. //
  1189. Fax_Info.bSendCoverPage = FaxConfig.UseCoverPage;
  1190. if (Fax_Info.bSendCoverPage) {
  1191. Fax_Info.bLocalCoverPage = ! FaxConfig.ServerCoverPage;
  1192. strncpy( Fax_Info.szCoverPage, FaxConfig.CoverPageName, MAX_PATH );
  1193. strcat( Fax_Info.szCoverPage, ".cov" );
  1194. }
  1195. strncpy( Fax_Info.szSubject, pMsgProps[MSG_SUBJECT].Value.lpszA, SUBJECT_LEN+1 );
  1196. //
  1197. // formatted date/time
  1198. //
  1199. LocaleId = LOCALE_USER_DEFAULT;
  1200. lpszBuffer = Fax_Info.szTimeSent;
  1201. Bytes = sizeof( Fax_Info.szTimeSent );
  1202. BytesWrite = GetDateFormat( LocaleId, 0x0000, NULL, NULL, lpszBuffer, Bytes );
  1203. if (BytesWrite == 0) {
  1204. rVal = GetLastError();
  1205. goto exit;
  1206. } else if (BytesWrite >= Bytes) {
  1207. rVal = ERROR_INSUFFICIENT_BUFFER;
  1208. goto exit;
  1209. } else {
  1210. Bytes -= BytesWrite;
  1211. }
  1212. strcat( lpszBuffer, " " );
  1213. Bytes -= 1;
  1214. lpszBuffer = &lpszBuffer[strlen(lpszBuffer)];
  1215. if (Bytes == 0) {
  1216. rVal = ERROR_INSUFFICIENT_BUFFER;
  1217. goto exit;
  1218. }
  1219. BytesWrite = GetTimeFormat( LocaleId, 0x0000, NULL, NULL, lpszBuffer, Bytes );
  1220. if (BytesWrite == 0) {
  1221. rVal = GetLastError();
  1222. goto exit;
  1223. } else if ( BytesWrite >= Bytes ) {
  1224. rVal = ERROR_INSUFFICIENT_BUFFER;
  1225. goto exit;
  1226. }
  1227. //
  1228. // Get system code page and other sender info not grabbed from the INI file
  1229. //
  1230. Fax_Info.CodePage = GetACP();
  1231. //
  1232. // provide sender-specific information to fax driver
  1233. //
  1234. hinstFwProv = LoadLibrary( "fwprov.dll" );
  1235. if (!hinstFwProv) {
  1236. rVal = GetLastError();
  1237. goto exit;
  1238. }
  1239. //
  1240. // initiate fax print job
  1241. //
  1242. pfnStartPrintJob = (PFWSPJ)GetProcAddress( hinstFwProv, "fwStartPrintJob" );
  1243. if (!pfnStartPrintJob) {
  1244. rVal = GetLastError();
  1245. goto exit;
  1246. }
  1247. if (!pfnStartPrintJob( FaxConfig.PrinterName, &Fax_Info, (PDWORD)&JobId, &hDC )) {
  1248. rVal = GetLastError();
  1249. goto exit;
  1250. }
  1251. //
  1252. // done with fwprov.dll
  1253. //
  1254. if (hinstFwProv) {
  1255. FreeLibrary( hinstFwProv );
  1256. }
  1257. #else
  1258. GetUserInfo( FaxConfig.PrinterName, &UserInfo );
  1259. TCHAR DocName[64];
  1260. LoadString(FaxXphInstance,IDS_FAX_MESSAGE,DocName,sizeof(DocName)/sizeof(TCHAR));
  1261. FaxPrintInfo.SizeOfStruct = sizeof(FAX_PRINT_INFOA);
  1262. FaxPrintInfo.DocName = DocName;
  1263. FaxPrintInfo.RecipientName = pRecipProps[RECIP_NAME].Value.lpszA;
  1264. FaxPrintInfo.RecipientNumber = pRecipProps[RECIP_EMAIL_ADR].Value.lpszA;
  1265. FaxPrintInfo.SenderName = FullName;
  1266. FaxPrintInfo.SenderCompany = UserInfo.Company;
  1267. FaxPrintInfo.SenderDept = UserInfo.Dept;
  1268. FaxPrintInfo.SenderBillingCode = UserInfo.BillingCode;
  1269. JobId = 0xffffffff;
  1270. if (PrinterInfo->Attributes & PRINTER_ATTRIBUTE_LOCAL) {
  1271. FaxPrintInfo.DrProfileName = m_ProfileName;
  1272. FaxPrintInfo.DrEmailAddress = NULL;
  1273. } else {
  1274. FaxPrintInfo.DrEmailAddress = m_ProfileName;
  1275. FaxPrintInfo.DrProfileName = NULL;
  1276. }
  1277. ContextInfo.SizeOfStruct = sizeof(FAX_CONTEXT_INFOW);
  1278. ec = FaxStartPrintJob(
  1279. FaxConfig.PrinterName,
  1280. &FaxPrintInfo,
  1281. (LPDWORD) &JobId,
  1282. &ContextInfo
  1283. );
  1284. if (!ec) {
  1285. rVal= GetLastError();
  1286. goto exit;
  1287. }
  1288. if (FaxConfig.UseCoverPage) {
  1289. FaxCpInfo.SizeOfStruct = sizeof(FAX_COVERPAGE_INFOA);
  1290. FaxCpInfo.CoverPageName = FaxConfig.CoverPageName;
  1291. FaxCpInfo.UseServerCoverPage = FaxConfig.ServerCoverPage;
  1292. FaxCpInfo.RecName = pRecipProps[RECIP_NAME].Value.lpszA;
  1293. FaxCpInfo.RecFaxNumber = pRecipProps[RECIP_EMAIL_ADR].Value.lpszA;
  1294. FaxCpInfo.Subject = pMsgProps[MSG_SUBJECT].Value.lpszA;
  1295. FaxCpInfo.Note = NULL;
  1296. FaxCpInfo.PageCount = Pages + 2;
  1297. GetLocalTime( &FaxCpInfo.TimeSent );
  1298. ec = FaxPrintCoverPage(
  1299. &ContextInfo,
  1300. &FaxCpInfo
  1301. );
  1302. if (!ec) {
  1303. rVal= GetLastError();
  1304. goto exit;
  1305. }
  1306. } else if (strlen(pMsgProps[MSG_SUBJECT].Value.lpszA) && !lpstmT) {
  1307. //
  1308. // HACK: try to use the "basenote.cov" coverpage so that we at least print something.
  1309. // if they just entered in a subject but no note, then nothing will be printed
  1310. //
  1311. TCHAR CoverpageName[MAX_PATH];
  1312. ExpandEnvironmentStrings(TEXT("%systemroot%\\system32\\basenote.cov"),CoverpageName,sizeof(CoverpageName));
  1313. FaxCpInfo.SizeOfStruct = sizeof(FAX_COVERPAGE_INFOA);
  1314. FaxCpInfo.CoverPageName = CoverpageName;
  1315. FaxCpInfo.UseServerCoverPage = FALSE;
  1316. FaxCpInfo.RecName = pRecipProps[RECIP_NAME].Value.lpszA;
  1317. FaxCpInfo.RecFaxNumber = pRecipProps[RECIP_EMAIL_ADR].Value.lpszA;
  1318. FaxCpInfo.Subject = pMsgProps[MSG_SUBJECT].Value.lpszA;
  1319. FaxCpInfo.Note = NULL;
  1320. FaxCpInfo.PageCount = Pages + 2;
  1321. GetLocalTime( &FaxCpInfo.TimeSent );
  1322. ec = FaxPrintCoverPage(
  1323. &ContextInfo,
  1324. &FaxCpInfo
  1325. );
  1326. //
  1327. // don't bail if this fails, it's a hack anyway
  1328. //
  1329. /*if (!ec) {
  1330. rVal= GetLastError();
  1331. goto exit;
  1332. } */
  1333. }
  1334. #endif
  1335. if (lpstmT) {
  1336. //
  1337. // position the stream to the beginning
  1338. //
  1339. hResult = lpstmT->Seek( BigZero, STREAM_SEEK_SET, NULL );
  1340. if (HR_FAILED (hResult)) {
  1341. rVal = IDS_CANT_ACCESS_MSG_DATA;
  1342. goto exit;
  1343. }
  1344. if (UseRichText) {
  1345. hResult = WrapCompressedRTFStream( lpstmT, 0, &lpstm );
  1346. if (HR_FAILED (hResult)) {
  1347. rVal = IDS_CANT_ACCESS_MSG_DATA;
  1348. goto exit;
  1349. }
  1350. //
  1351. // print the document
  1352. //
  1353. hWndRichEdit = CreateWindowEx(
  1354. WS_OVERLAPPED,
  1355. "RICHEDIT",
  1356. "",
  1357. ES_MULTILINE,
  1358. 0, 0,
  1359. 0, 0,
  1360. NULL,
  1361. NULL,
  1362. FaxXphInstance,
  1363. NULL
  1364. );
  1365. if (!hWndRichEdit) {
  1366. ec = GetLastError();
  1367. rVal = IDS_CANT_ACCESS_MSG_DATA;
  1368. goto exit;
  1369. }
  1370. es.pfnCallback = EditStreamRead;
  1371. es.dwCookie = (DWORD_PTR) lpstm;
  1372. SendMessage(
  1373. hWndRichEdit,
  1374. EM_STREAMIN,
  1375. SF_RTF | SFF_SELECTION | SFF_PLAINRTF,
  1376. (LPARAM) &es
  1377. );
  1378. if (es.dwError) {
  1379. ec = es.dwError;
  1380. rVal = IDS_CANT_ACCESS_MSG_DATA;
  1381. goto exit;
  1382. }
  1383. #ifdef WIN95
  1384. PrintRichText( hWndRichEdit, hDC, &FaxConfig );
  1385. #else
  1386. PrintRichText( hWndRichEdit, ContextInfo.hDC, &FaxConfig );
  1387. #endif
  1388. } else {
  1389. #ifdef WIN95
  1390. PrintText( hDC, lpstmT, &FaxConfig );
  1391. #else
  1392. PrintText( ContextInfo.hDC, lpstmT, &FaxConfig );
  1393. #endif
  1394. }
  1395. }
  1396. if (LastJobId) {
  1397. //
  1398. // chain the main body job to the first
  1399. // attachment job and resume the attachment job
  1400. //
  1401. JobInfo3.JobId = JobId;
  1402. JobInfo3.NextJobId = LastJobId;
  1403. JobInfo3.Reserved = 0;
  1404. if (!SetJob( hPrinter, JobId, 3, (PBYTE) &JobInfo3, 0 )) {
  1405. DebugPrint(( "SetJob() failed, ec=%d", GetLastError() ));
  1406. }
  1407. if (!SetJob( hPrinter, LastJobId, 0, NULL, JOB_CONTROL_RESUME )) {
  1408. DebugPrint(( "SetJob() failed, ec=%d", GetLastError() ));
  1409. }
  1410. //
  1411. // update the page count
  1412. //
  1413. GetJob( hPrinter, JobId, 1, NULL, 0, &Bytes );
  1414. JobInfo1 = (PJOB_INFO_1) MemAlloc( Bytes );
  1415. if (JobInfo1) {
  1416. JobInfo1->TotalPages += Pages;
  1417. if (!SetJob( hPrinter, JobId, 1, (PBYTE) JobInfo1, 0 )) {
  1418. DebugPrint(( "SetJob() failed, ec=%d", GetLastError() ));
  1419. }
  1420. MemFree( JobInfo1 );
  1421. }
  1422. }
  1423. rVal = 0;
  1424. exit:
  1425. if (pProfileObj) {
  1426. pProfileObj->Release();
  1427. }
  1428. if (pProps) {
  1429. MemFree( pProps );
  1430. }
  1431. if (MsgPropTags) {
  1432. MemFree( MsgPropTags );
  1433. }
  1434. if (pPropsMsg) {
  1435. MemFree( pPropsMsg );
  1436. }
  1437. if (pAttachmentRows) {
  1438. MemFree( pAttachmentRows );
  1439. }
  1440. if (AttachmentTable) {
  1441. AttachmentTable->Release();
  1442. }
  1443. if (lpstm) {
  1444. lpstm->Release();
  1445. }
  1446. #ifdef WIN95
  1447. if (hDC) {
  1448. EndDoc( hDC );
  1449. SetJob( hPrinter, JobId, 0, NULL, JOB_CONTROL_RESUME );
  1450. DeleteDC( hDC );
  1451. }
  1452. #else
  1453. if (ContextInfo.hDC) {
  1454. EndDoc( ContextInfo.hDC );
  1455. SetJob( hPrinter, JobId, 0, NULL, JOB_CONTROL_RESUME );
  1456. DeleteDC( ContextInfo.hDC );
  1457. }
  1458. #endif
  1459. if (hPrinter) {
  1460. ClosePrinter( hPrinter );
  1461. }
  1462. if (hWndRichEdit) {
  1463. DestroyWindow( hWndRichEdit );
  1464. }
  1465. if (PrinterInfo) {
  1466. MemFree( PrinterInfo );
  1467. }
  1468. if (FaxConfig.PrinterName) {
  1469. MemFree( FaxConfig.PrinterName );
  1470. }
  1471. if (FaxConfig.CoverPageName) {
  1472. MemFree( FaxConfig.CoverPageName );
  1473. }
  1474. if (DocType) {
  1475. MemFree( DocType );
  1476. }
  1477. return rVal;
  1478. }