Leaked source code of windows server 2003
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.

6077 lines
183 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. job.c
  5. Abstract:
  6. This module implements the job creation and deletion.
  7. Also included in the file are the queue management
  8. functions and thread management.
  9. Author:
  10. Wesley Witt (wesw) 24-Jan-1996
  11. Revision History:
  12. --*/
  13. #include "faxsvc.h"
  14. #include "faxreg.h"
  15. #pragma hdrstop
  16. #include <strsafe.h>
  17. #include <efsputil.h>
  18. using namespace std;
  19. // Globals
  20. LIST_ENTRY g_JobListHead; //List of currently running jobs (for which FaxDevStartJob was called).
  21. CFaxCriticalSection g_CsJob;
  22. HANDLE g_StatusCompletionPortHandle;
  23. HINSTANCE g_hResource;
  24. DWORD g_dwFaxSendRetries;
  25. DWORD g_dwFaxSendRetryDelay;
  26. DWORD g_dwFaxDirtyDays;
  27. BOOL g_fFaxUseDeviceTsid;
  28. BOOL g_fFaxUseBranding;
  29. BOOL g_fServerCp;
  30. FAX_TIME g_StartCheapTime;
  31. FAX_TIME g_StopCheapTime;
  32. DWORD g_dwNextJobId;
  33. #define JOB_GROUP_FILE_EXTENSION TEXT("FSP")
  34. static BOOL SendJobReceipt (BOOL bPositive, JOB_QUEUE * lpJobQueue, LPCTSTR lpctstrAttachment);
  35. static BOOL CheckForJobRetry (PJOB_QUEUE lpJobQueue);
  36. static
  37. DWORD
  38. TranslateCanonicalNumber(
  39. LPTSTR lptstrCanonicalFaxNumber,
  40. DWORD dwDeviceID,
  41. LPTSTR lptstrDialableAddress,
  42. DWORD dwDialableAddressCount,
  43. LPTSTR lptstrDisplayableAddress,
  44. DWORD dwDisplayableAddressCount
  45. );
  46. static PJOB_ENTRY
  47. StartLegacySendJob(
  48. PJOB_QUEUE lpJobQueue,
  49. PLINE_INFO lpLineInfo
  50. );
  51. static PJOB_ENTRY CreateJobEntry(PJOB_QUEUE lpJobQueue, LINE_INFO * lpLineInfo, BOOL bTranslateNumber);
  52. BOOL FreeJobEntry(PJOB_ENTRY lpJobEntry , BOOL bDestroy);
  53. static BOOL UpdatePerfCounters(const JOB_QUEUE * lpcJobQueue);
  54. static BOOL
  55. CreateCoverpageTiffFile(
  56. IN short Resolution,
  57. IN const FAX_COVERPAGE_INFOW2 *CoverpageInfo,
  58. IN LPCWSTR lpcwstrExtension,
  59. OUT LPWSTR lpwstrCovTiffFile,
  60. IN DWORD dwCovTiffFileCount
  61. );
  62. static LPWSTR
  63. GetFaxPrinterName(
  64. VOID
  65. );
  66. DWORD BrandFax(LPCTSTR lpctstrFileName, LPCFSPI_BRAND_INFO pcBrandInfo)
  67. {
  68. #define MAX_BRANDING_LEN 115
  69. #define BRANDING_HEIGHT 22 // in scan lines.
  70. //
  71. // We allocate fixed size arrays on the stack to avoid many small allocs on the heap.
  72. //
  73. LPTSTR lptstrBranding = NULL;
  74. DWORD lenBranding =0;
  75. TCHAR szBrandingEnd[MAX_BRANDING_LEN+1];
  76. DWORD lenBrandingEnd = 0;
  77. LPTSTR lptstrCallerNumberPlusCompanyName = NULL;
  78. DWORD lenCallerNumberPlusCompanyName = 0;
  79. DWORD delta =0 ;
  80. DWORD ec = ERROR_SUCCESS;
  81. LPTSTR lptstrDate = NULL;
  82. LPTSTR lptstrTime = NULL;
  83. LPTSTR lptstrDateTime = NULL;
  84. int lenDate =0 ;
  85. int lenTime =0;
  86. LPDWORD MsgPtr[6];
  87. HRESULT hr;
  88. DWORD dwDateTimeLength = 0;
  89. LPTSTR lptstrSenderTsid;
  90. LPTSTR lptstrRecipientPhoneNumber;
  91. LPTSTR lptstrSenderCompany;
  92. DWORD dwSenderTsidLen;
  93. DWORD dwSenderCompanyLen;
  94. DEBUG_FUNCTION_NAME(TEXT("BrandFax"));
  95. Assert(lpctstrFileName);
  96. Assert(pcBrandInfo);
  97. lptstrSenderTsid = pcBrandInfo->lptstrSenderTsid ? pcBrandInfo->lptstrSenderTsid : TEXT("");
  98. lptstrRecipientPhoneNumber = pcBrandInfo->lptstrRecipientPhoneNumber ? pcBrandInfo->lptstrRecipientPhoneNumber : TEXT("");
  99. lptstrSenderCompany = pcBrandInfo->lptstrSenderCompany ? pcBrandInfo->lptstrSenderCompany : TEXT("");
  100. dwSenderTsidLen = lptstrSenderTsid ? _tcslen(lptstrSenderTsid) : 0;
  101. dwSenderCompanyLen = lptstrSenderCompany ? _tcslen(lptstrSenderCompany) : 0;
  102. lenDate = GetY2KCompliantDate(
  103. LOCALE_SYSTEM_DEFAULT,
  104. 0,
  105. &pcBrandInfo->tmDateTime,
  106. NULL,
  107. NULL);
  108. if ( ! lenDate )
  109. {
  110. ec = GetLastError();
  111. DebugPrintEx(
  112. DEBUG_ERR,
  113. TEXT("GetY2KCompliantDate() failed (ec: %ld)"),
  114. ec
  115. );
  116. goto Error;
  117. }
  118. lptstrDate = (LPTSTR) MemAlloc(lenDate * sizeof(TCHAR)); // lenDate includes terminating NULL
  119. if (!lptstrDate)
  120. {
  121. ec = GetLastError();
  122. DebugPrintEx(
  123. DEBUG_ERR,
  124. TEXT("Failed to allocate date buffer of size %ld (ec: %ld)"),
  125. lenDate * sizeof(TCHAR),
  126. ec);
  127. goto Error;
  128. }
  129. if (!GetY2KCompliantDate(
  130. LOCALE_SYSTEM_DEFAULT,
  131. 0,
  132. &pcBrandInfo->tmDateTime,
  133. lptstrDate,
  134. lenDate))
  135. {
  136. ec = GetLastError();
  137. DebugPrintEx(
  138. DEBUG_ERR,
  139. TEXT("GetY2KCompliantDate() failed (ec: %ld)"),
  140. ec
  141. );
  142. goto Error;
  143. }
  144. lenTime = FaxTimeFormat( LOCALE_SYSTEM_DEFAULT,
  145. TIME_NOSECONDS,
  146. &pcBrandInfo->tmDateTime,
  147. NULL,
  148. NULL,
  149. 0 );
  150. if ( !lenTime )
  151. {
  152. ec = GetLastError();
  153. DebugPrintEx(
  154. DEBUG_ERR,
  155. TEXT("FaxTimeFormat() failed (ec: %ld)"),
  156. ec
  157. );
  158. goto Error;
  159. }
  160. lptstrTime = (LPTSTR) MemAlloc(lenTime * sizeof(TCHAR)); // lenTime includes terminating NULL
  161. if (!lptstrTime)
  162. {
  163. ec = GetLastError();
  164. DebugPrintEx(
  165. DEBUG_ERR,
  166. TEXT("Failed to allocate time buffer of size %ld (ec: %ld)"),
  167. lenTime * sizeof(TCHAR),
  168. ec);
  169. goto Error;
  170. }
  171. if ( ! FaxTimeFormat(
  172. LOCALE_SYSTEM_DEFAULT,
  173. TIME_NOSECONDS,
  174. &pcBrandInfo->tmDateTime,
  175. NULL, // use locale format
  176. lptstrTime,
  177. lenTime) )
  178. {
  179. ec = GetLastError();
  180. DebugPrintEx(
  181. DEBUG_ERR,
  182. TEXT("FaxTimeFormat() failed (ec: %ld)"),
  183. ec
  184. );
  185. goto Error;
  186. }
  187. //
  188. // Concatenate date and time
  189. //
  190. dwDateTimeLength = lenDate + lenTime; // should be enough, lenDate and lentime both include '\0', and we add only one ' ' between the date and time.
  191. lptstrDateTime = (LPTSTR) MemAlloc (dwDateTimeLength * sizeof(TCHAR));
  192. if (!lptstrDateTime)
  193. {
  194. ec = GetLastError();
  195. DebugPrintEx(
  196. DEBUG_ERR,
  197. TEXT("Failed to allocate DateTime buffer of size %ld (ec: %ld)"),
  198. dwDateTimeLength,
  199. ec);
  200. goto Error;
  201. }
  202. hr = StringCchPrintf(lptstrDateTime,
  203. dwDateTimeLength,
  204. TEXT("%s %s"),
  205. lptstrDate,
  206. lptstrTime);
  207. if (FAILED(hr))
  208. {
  209. //
  210. // Should never happen, we just allocated large enough buffer.
  211. //
  212. ASSERT_FALSE;
  213. }
  214. //
  215. // Create lpCallerNumberPlusCompanyName
  216. //
  217. if (lptstrSenderCompany)
  218. {
  219. DWORD dwCallerNumberPlusCompanyNameCount = dwSenderTsidLen + dwSenderCompanyLen +2; // we add 2 chars, 1 for '\0' and one for the ' '.
  220. lptstrCallerNumberPlusCompanyName = (LPTSTR) MemAlloc( dwCallerNumberPlusCompanyNameCount * sizeof(TCHAR) );
  221. if (!lptstrCallerNumberPlusCompanyName)
  222. {
  223. ec = GetLastError();
  224. DebugPrintEx(
  225. DEBUG_ERR,
  226. TEXT("Failed to allocate CallerNumberPlusCompanyName buffer of size %ld (ec: %ld)"),
  227. dwCallerNumberPlusCompanyNameCount,
  228. ec);
  229. goto Error;
  230. }
  231. hr = StringCchPrintf(lptstrCallerNumberPlusCompanyName,
  232. dwCallerNumberPlusCompanyNameCount,
  233. TEXT("%s %s"),
  234. lptstrSenderTsid,
  235. lptstrSenderCompany);
  236. if (FAILED(hr))
  237. {
  238. //
  239. // Should never happen, we just allocated large enough buffer.
  240. //
  241. ASSERT_FALSE;
  242. }
  243. }
  244. else
  245. {
  246. lptstrCallerNumberPlusCompanyName = (LPTSTR)
  247. MemAlloc( (dwSenderTsidLen + 1) * sizeof(TCHAR));
  248. if (!lptstrCallerNumberPlusCompanyName)
  249. {
  250. ec = GetLastError();
  251. DebugPrintEx(
  252. DEBUG_ERR,
  253. TEXT("Failed to allocate CallerNumberPlusCompanyName buffer of size %ld (ec: %ld)"),
  254. (dwSenderTsidLen + 1) * sizeof(TCHAR),
  255. ec);
  256. goto Error;
  257. }
  258. hr = StringCchCopy(
  259. lptstrCallerNumberPlusCompanyName,
  260. dwSenderTsidLen + 1,
  261. lptstrSenderTsid);
  262. if (FAILED(hr))
  263. {
  264. //
  265. // Should never happen, we just allocated large enough buffer.
  266. //
  267. ASSERT_FALSE;
  268. }
  269. }
  270. //
  271. // Try to create a banner of the following format:
  272. // <szDateTime> FROM: <szCallerNumberPlusCompanyName> TO: <pcBrandInfo->lptstrRecipientPhoneNumber> PAGE: X OF Y
  273. // If it does not fit we will start chopping it off.
  274. //
  275. MsgPtr[0] = (LPDWORD) lptstrDateTime;
  276. MsgPtr[1] = (LPDWORD) lptstrCallerNumberPlusCompanyName;
  277. MsgPtr[2] = (LPDWORD) lptstrRecipientPhoneNumber;
  278. MsgPtr[3] = NULL;
  279. lenBranding = FormatMessage(
  280. FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER,
  281. g_hResource,
  282. MSG_BRANDING_FULL,
  283. 0,
  284. (LPTSTR)&lptstrBranding,
  285. 0,
  286. (va_list *) MsgPtr
  287. );
  288. if ( ! lenBranding )
  289. {
  290. ec = GetLastError();
  291. DebugPrintEx(
  292. DEBUG_ERR,
  293. TEXT("FormatMessage of MSG_BRANDING_FULL failed (ec: %ld)"),
  294. ec);
  295. goto Error;
  296. }
  297. Assert(lptstrBranding);
  298. lenBrandingEnd = FormatMessage(
  299. FORMAT_MESSAGE_FROM_HMODULE ,
  300. g_hResource,
  301. MSG_BRANDING_END,
  302. 0,
  303. szBrandingEnd,
  304. sizeof(szBrandingEnd)/sizeof(TCHAR),
  305. NULL
  306. );
  307. if ( !lenBrandingEnd)
  308. {
  309. ec = GetLastError();
  310. DebugPrintEx(
  311. DEBUG_ERR,
  312. TEXT("FormatMessage of MSG_BRANDING_END failed (ec: %ld)"),
  313. ec);
  314. goto Error;
  315. }
  316. //
  317. // Make sure we can fit everything.
  318. //
  319. if (lenBranding + lenBrandingEnd + 8 <= MAX_BRANDING_LEN)
  320. {
  321. //
  322. // It fits. Proceed with branding.
  323. //
  324. goto lDoBranding;
  325. }
  326. //
  327. // It did not fit. Try a message of the format:
  328. // <lpDateTime> FROM: <lpCallerNumberPlusCompanyName> PAGE: X OF Y
  329. // This skips the ReceiverNumber. The important part is the CallerNumberPlusCompanyName.
  330. //
  331. MsgPtr[0] = (LPDWORD) lptstrDateTime;
  332. MsgPtr[1] = (LPDWORD) lptstrCallerNumberPlusCompanyName;
  333. MsgPtr[2] = NULL;
  334. //
  335. // Free the previous attempt branding string
  336. //
  337. Assert(lptstrBranding);
  338. LocalFree(lptstrBranding);
  339. lptstrBranding = NULL;
  340. lenBranding = FormatMessage(
  341. FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER,
  342. g_hResource,
  343. MSG_BRANDING_SHORT,
  344. 0,
  345. (LPTSTR)&lptstrBranding,
  346. 0,
  347. (va_list *) MsgPtr
  348. );
  349. if ( !lenBranding )
  350. {
  351. ec = GetLastError();
  352. DebugPrintEx(
  353. DEBUG_ERR,
  354. TEXT("FormatMessage() failed for MSG_BRANDING_SHORT (ec: %ld)"),
  355. ec);
  356. goto Error;
  357. }
  358. Assert(lptstrBranding);
  359. if (lenBranding + lenBrandingEnd + 8 <= MAX_BRANDING_LEN) {
  360. goto lDoBranding;
  361. }
  362. //
  363. // It did not fit.
  364. // We have to truncate the caller number so it fits.
  365. // delta = how many chars of the company name we need to chop off.
  366. //
  367. delta = lenBranding + lenBrandingEnd + 8 - MAX_BRANDING_LEN;
  368. lenCallerNumberPlusCompanyName = _tcslen (lptstrCallerNumberPlusCompanyName);
  369. if (lenCallerNumberPlusCompanyName <= delta) {
  370. DebugPrintEx(
  371. DEBUG_ERR,
  372. TEXT("Can not truncate CallerNumberPlusCompanyName to fit brand limit.")
  373. TEXT(" Delta[%ld] >= lenCallerNumberPlusCompanyName[%ld]"),
  374. delta,
  375. lenCallerNumberPlusCompanyName);
  376. ec = ERROR_BAD_FORMAT;
  377. goto Error;
  378. }
  379. lptstrCallerNumberPlusCompanyName[ lenCallerNumberPlusCompanyName - delta] = TEXT('\0');
  380. MsgPtr[0] = (LPDWORD) lptstrDateTime;
  381. MsgPtr[1] = (LPDWORD) lptstrCallerNumberPlusCompanyName;
  382. MsgPtr[2] = NULL;
  383. //
  384. // Free the previous attempt branding string
  385. //
  386. Assert(lptstrBranding);
  387. LocalFree(lptstrBranding);
  388. lptstrBranding = NULL;
  389. lenBranding = FormatMessage(
  390. FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER,
  391. g_hResource,
  392. MSG_BRANDING_SHORT,
  393. 0,
  394. (LPTSTR)&lptstrBranding,
  395. 0,
  396. (va_list *) MsgPtr
  397. );
  398. if ( !lenBranding )
  399. {
  400. ec = GetLastError();
  401. DebugPrintEx(
  402. DEBUG_ERR,
  403. TEXT("FormatMessage() failed (ec: %ld). MSG_BRANDING_SHORT 2nd attempt"),
  404. ec);
  405. goto Error;
  406. }
  407. Assert(lptstrBranding);
  408. //
  409. // If it did noo fit now then we have a bug.
  410. //
  411. Assert(lenBranding + lenBrandingEnd + 8 <= MAX_BRANDING_LEN);
  412. lDoBranding:
  413. if (!MmrAddBranding(lpctstrFileName, lptstrBranding, szBrandingEnd, BRANDING_HEIGHT) )
  414. {
  415. ec = GetLastError();
  416. DebugPrintEx(
  417. DEBUG_ERR,
  418. TEXT("MmrAddBranding() failed (ec: %ld)")
  419. TEXT(" File: [%s]")
  420. TEXT(" Branding: [%s]")
  421. TEXT(" Branding End: [%s]")
  422. TEXT(" Branding Height: [%d]"),
  423. ec,
  424. lpctstrFileName,
  425. lptstrBranding,
  426. szBrandingEnd,
  427. BRANDING_HEIGHT);
  428. goto Error;
  429. }
  430. Assert( ERROR_SUCCESS == ec);
  431. goto Exit;
  432. Error:
  433. Assert (ERROR_SUCCESS != ec);
  434. Exit:
  435. if (lptstrBranding)
  436. {
  437. LocalFree(lptstrBranding);
  438. lptstrBranding = NULL;
  439. }
  440. MemFree(lptstrDate);
  441. lptstrDate = NULL;
  442. MemFree(lptstrTime);
  443. lptstrTime = NULL;
  444. MemFree(lptstrDateTime);
  445. lptstrDateTime = NULL;
  446. MemFree(lptstrCallerNumberPlusCompanyName);
  447. lptstrCallerNumberPlusCompanyName = NULL;
  448. return ec;
  449. }
  450. HRESULT
  451. WINAPI
  452. FaxBrandDocument(
  453. LPCTSTR lpctsrtFile,
  454. LPCFSPI_BRAND_INFO lpcBrandInfo)
  455. {
  456. DEBUG_FUNCTION_NAME(TEXT("FaxBrandDocument"));
  457. DWORD ec = ERROR_SUCCESS;
  458. if (!lpctsrtFile)
  459. {
  460. DebugPrintEx(DEBUG_ERR,
  461. TEXT("NULL target file name"));
  462. ec = ERROR_INVALID_PARAMETER;
  463. goto Error;
  464. }
  465. if (!lpcBrandInfo)
  466. {
  467. DebugPrintEx(DEBUG_ERR,
  468. TEXT("NULL branding info"));
  469. ec = ERROR_INVALID_PARAMETER;
  470. goto Error;
  471. }
  472. if (lpcBrandInfo->dwSizeOfStruct != sizeof(FSPI_BRAND_INFO))
  473. {
  474. DebugPrintEx(DEBUG_ERR,
  475. TEXT("Bad cover page info parameter, dwSizeOfStruct = %d"),
  476. lpcBrandInfo->dwSizeOfStruct);
  477. ec = ERROR_INVALID_PARAMETER;
  478. goto Error;
  479. }
  480. ec = BrandFax(lpctsrtFile, lpcBrandInfo);
  481. if (ERROR_SUCCESS != ec)
  482. {
  483. DebugPrintEx(
  484. DEBUG_ERR,
  485. TEXT("BrandFax() for file %s has failed (ec: %ld)"),
  486. lpctsrtFile,
  487. ec);
  488. goto Error;
  489. }
  490. Assert (ERROR_SUCCESS == ec);
  491. goto Exit;
  492. Error:
  493. Assert (ERROR_SUCCESS != ec);
  494. Exit:
  495. return HRESULT_FROM_WIN32(ec);
  496. }
  497. PJOB_ENTRY
  498. FindJob(
  499. IN HANDLE FaxHandle
  500. )
  501. /*++
  502. Routine Description:
  503. This fuction locates a FAX job by matching
  504. the FAX handle value.
  505. Arguments:
  506. FaxHandle - FAX handle returned from startjob
  507. Return Value:
  508. NULL for failure.
  509. Valid pointer to a JOB_ENTRY on success.
  510. --*/
  511. {
  512. PLIST_ENTRY Next;
  513. PJOB_ENTRY JobEntry;
  514. EnterCriticalSection( &g_CsJob );
  515. Next = g_JobListHead.Flink;
  516. if (Next == NULL) {
  517. LeaveCriticalSection( &g_CsJob );
  518. return NULL;
  519. }
  520. while ((ULONG_PTR)Next != (ULONG_PTR)&g_JobListHead) {
  521. JobEntry = CONTAINING_RECORD( Next, JOB_ENTRY, ListEntry );
  522. if ((ULONG_PTR)JobEntry->InstanceData == (ULONG_PTR)FaxHandle) {
  523. LeaveCriticalSection( &g_CsJob );
  524. return JobEntry;
  525. }
  526. Next = JobEntry->ListEntry.Flink;
  527. }
  528. LeaveCriticalSection( &g_CsJob );
  529. return NULL;
  530. }
  531. BOOL
  532. FindJobByJob(
  533. IN PJOB_ENTRY JobEntryToFind
  534. )
  535. /*++
  536. Routine Description:
  537. This fuction check whether a FAX job exist in g_JobListHead (Job's list)
  538. Arguments:
  539. JobEntryToFind - PJOB_ENTRY from StartJob()
  540. Return Value:
  541. TRUE - if the job was found
  542. FALSE - otherwise
  543. --*/
  544. {
  545. PLIST_ENTRY Next;
  546. PJOB_ENTRY JobEntry;
  547. Assert(JobEntryToFind);
  548. EnterCriticalSection( &g_CsJob );
  549. Next = g_JobListHead.Flink;
  550. if (Next == NULL) {
  551. LeaveCriticalSection( &g_CsJob );
  552. return FALSE;
  553. }
  554. while ((ULONG_PTR)Next != (ULONG_PTR)&g_JobListHead) {
  555. JobEntry = CONTAINING_RECORD( Next, JOB_ENTRY, ListEntry );
  556. if (JobEntry == JobEntryToFind) {
  557. LeaveCriticalSection( &g_CsJob );
  558. return TRUE;
  559. }
  560. Next = JobEntry->ListEntry.Flink;
  561. }
  562. LeaveCriticalSection( &g_CsJob );
  563. return FALSE;
  564. }
  565. BOOL
  566. FaxSendCallback(
  567. IN HANDLE FaxHandle,
  568. IN HCALL CallHandle,
  569. IN DWORD Reserved1,
  570. IN DWORD Reserved2
  571. )
  572. /*++
  573. Routine Description:
  574. This fuction is called asychronously by a FAX device
  575. provider after a call is established. The sole purpose
  576. of the callback is to communicate the call handle from the
  577. device provider to the FAX service.
  578. Arguments:
  579. FaxHandle - FAX handle returned from startjob
  580. CallHandle - Call handle for newly initiated call
  581. Reserved1 - Always zero.
  582. Reserved2 - Always zero.
  583. Return Value:
  584. TRUE for success, FAX operation continues.
  585. FALSE for failure, FAX operation is terminated.
  586. --*/
  587. {
  588. PJOB_ENTRY JobEntry = NULL;
  589. BOOL bRes = FALSE;
  590. EnterCriticalSection(&g_CsJob);
  591. JobEntry = FindJob( FaxHandle );
  592. if (JobEntry)
  593. {
  594. if (NULL == JobEntry->CallHandle)
  595. {
  596. JobEntry->CallHandle = CallHandle;
  597. }
  598. bRes = (JobEntry->CallHandle == CallHandle) ? TRUE : FALSE;
  599. }
  600. LeaveCriticalSection(&g_CsJob);
  601. if (FALSE == bRes)
  602. {
  603. SetLastError(ERROR_INVALID_PARAMETER);
  604. }
  605. return bRes;
  606. }
  607. //*********************************************************************************
  608. //* Name: CreateCoverpageTiffFileEx()
  609. //* Author: Ronen Barenboim
  610. //* Date: March 24, 1999
  611. //*********************************************************************************
  612. //* DESCRIPTION:
  613. //* Generates cover page TIFF file from the specified cover page template
  614. //* and new Client API parameters.
  615. //* The function returns the name of the generated file.
  616. //* PARAMETERS:
  617. //* Resolution [IN]
  618. //*
  619. //* dwPageCount [IN]
  620. //*
  621. //* lpcCoverpageEx [IN]
  622. //*
  623. //* lpcRecipient [IN]
  624. //*
  625. //* lpcSender [IN]
  626. //*
  627. //* lpcwstrExtension [IN] - File extension (optional).
  628. //*
  629. //* lpwstrCovTiffFile [OUT]
  630. //* A pointer to Unicode string buffer where the function will place
  631. //* the full path to the generated cover page TIFF file.
  632. //*
  633. //* dwCovTiffFileCount [IN] - size of the buffer pointed by lpwstrCovTiffFile.
  634. //*
  635. //* RETURN VALUE:
  636. //* TRUE
  637. //* If the operation succeeded.
  638. //* FALSE
  639. //* Otherwise. Use GetLastError() to figure out why it failed.
  640. //*
  641. //* REMARKS:
  642. //* The function does not allocate any memory.
  643. //*********************************************************************************
  644. BOOL
  645. CreateCoverpageTiffFileEx(
  646. IN short Resolution,
  647. IN DWORD dwPageCount,
  648. IN LPCFAX_COVERPAGE_INFO_EXW lpcCoverpageEx,
  649. IN LPCFAX_PERSONAL_PROFILEW lpcRecipient,
  650. IN LPCFAX_PERSONAL_PROFILEW lpcSender,
  651. IN LPCWSTR lpcwstrExtension,
  652. OUT LPWSTR lpwstrCovTiffFile,
  653. IN DWORD dwCovTiffFileCount)
  654. {
  655. FAX_COVERPAGE_INFOW2 covLegacy;
  656. BOOL bRes = TRUE;
  657. DEBUG_FUNCTION_NAME(TEXT("CreateCoverpageTiffFileEx"));
  658. Assert(lpcCoverpageEx);
  659. Assert(lpcRecipient);
  660. Assert(lpcSender);
  661. Assert(lpwstrCovTiffFile);
  662. //
  663. // Prepare a legacy FAX_COVERPAGE_INFO from the new cover page info
  664. //
  665. memset(&covLegacy,0,sizeof(covLegacy));
  666. covLegacy.SizeOfStruct=sizeof(covLegacy);
  667. covLegacy.CoverPageName=lpcCoverpageEx->lptstrCoverPageFileName;
  668. covLegacy.UseServerCoverPage=lpcCoverpageEx->bServerBased;
  669. covLegacy.RecCity=lpcRecipient->lptstrCity;
  670. covLegacy.RecCompany=lpcRecipient->lptstrCompany;
  671. covLegacy.RecCountry=lpcRecipient->lptstrCountry;
  672. covLegacy.RecDepartment=lpcRecipient->lptstrDepartment;
  673. covLegacy.RecFaxNumber=lpcRecipient->lptstrFaxNumber;
  674. covLegacy.RecHomePhone=lpcRecipient->lptstrHomePhone;
  675. covLegacy.RecName=lpcRecipient->lptstrName;
  676. covLegacy.RecOfficeLocation=lpcRecipient->lptstrOfficeLocation;
  677. covLegacy.RecOfficePhone=lpcRecipient->lptstrOfficePhone;
  678. covLegacy.RecState=lpcRecipient->lptstrState;
  679. covLegacy.RecStreetAddress=lpcRecipient->lptstrStreetAddress;
  680. covLegacy.RecTitle=lpcRecipient->lptstrTitle;
  681. covLegacy.RecZip=lpcRecipient->lptstrZip;
  682. covLegacy.SdrName=lpcSender->lptstrName;
  683. covLegacy.SdrFaxNumber=lpcSender->lptstrFaxNumber;
  684. covLegacy.SdrCompany=lpcSender->lptstrCompany;
  685. covLegacy.SdrTitle=lpcSender->lptstrTitle;
  686. covLegacy.SdrDepartment=lpcSender->lptstrDepartment;
  687. covLegacy.SdrOfficeLocation=lpcSender->lptstrOfficeLocation;
  688. covLegacy.SdrHomePhone=lpcSender->lptstrHomePhone;
  689. covLegacy.SdrAddress=lpcSender->lptstrStreetAddress;
  690. covLegacy.SdrOfficePhone=lpcSender->lptstrOfficePhone;
  691. covLegacy.SdrEmail=lpcSender->lptstrEmail;
  692. covLegacy.Note=lpcCoverpageEx->lptstrNote;
  693. covLegacy.Subject=lpcCoverpageEx->lptstrSubject;
  694. covLegacy.PageCount=dwPageCount;
  695. //
  696. // Note covLegacy.TimeSent is not set. This field's value is
  697. // generated by FaxPrintCoverPageW().
  698. //
  699. //
  700. // Now call the legacy CreateCoverPageTiffFile() to generate the cover page file
  701. //
  702. if (!CreateCoverpageTiffFile(Resolution, &covLegacy, lpcwstrExtension, lpwstrCovTiffFile, dwCovTiffFileCount))
  703. {
  704. DebugPrintEx(
  705. DEBUG_ERR,
  706. TEXT("Failed to generate cover page file for recipient %s@%s. (ec: %ld)"),
  707. lpcRecipient->lptstrName,
  708. lpcRecipient->lptstrFaxNumber,
  709. GetLastError()
  710. );
  711. bRes = FALSE;
  712. }
  713. return bRes;
  714. }
  715. LPWSTR
  716. GetFaxPrinterName(
  717. VOID
  718. )
  719. {
  720. PPRINTER_INFO_2 PrinterInfo;
  721. DWORD i;
  722. DWORD Count;
  723. PrinterInfo = (PPRINTER_INFO_2) MyEnumPrinters( NULL, 2, &Count, 0 );
  724. if (PrinterInfo == NULL)
  725. {
  726. if (ERROR_SUCCESS == GetLastError())
  727. {
  728. //
  729. // No printers are installed
  730. //
  731. SetLastError(ERROR_INVALID_PRINTER_NAME);
  732. }
  733. return NULL;
  734. }
  735. for (i=0; i<Count; i++)
  736. {
  737. if (_wcsicmp( PrinterInfo[i].pDriverName, FAX_DRIVER_NAME ) == 0 &&
  738. _wcsicmp( PrinterInfo[i].pPortName, FAX_PORT_NAME ) == 0)
  739. {
  740. LPWSTR p = (LPWSTR) StringDup( PrinterInfo[i].pPrinterName );
  741. MemFree( PrinterInfo );
  742. if (NULL == p )
  743. {
  744. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  745. }
  746. return p;
  747. }
  748. }
  749. MemFree( PrinterInfo );
  750. SetLastError (ERROR_INVALID_PRINTER_NAME);
  751. return NULL;
  752. }
  753. VOID
  754. FreeCpFields(
  755. PCOVERPAGEFIELDS pCpFields
  756. )
  757. /*++
  758. Routine Description:
  759. Frees all memory associated with a coverpage field structure.
  760. Arguments:
  761. CpFields - Pointer to a coverpage field structure.
  762. Return Value:
  763. None.
  764. --*/
  765. {
  766. DWORD i;
  767. LPTSTR* lpptstrString;
  768. for (i = 0; i < NUM_INSERTION_TAGS; i++)
  769. {
  770. lpptstrString = (LPTSTR*) ((LPBYTE)(&(pCpFields->RecName)) + (i * sizeof(LPTSTR)));
  771. MemFree (*lpptstrString) ;
  772. }
  773. }
  774. BOOL
  775. FillCoverPageFields(
  776. IN const FAX_COVERPAGE_INFOW2* pFaxCovInfo,
  777. OUT PCOVERPAGEFIELDS pCPFields)
  778. /*++
  779. Author:
  780. Oded Sacher 27-June-2001
  781. Routine Description:
  782. Fills a COVERPAGEFIELDS structure from the content of a FAX_COVERPAGE_INFO structure.
  783. Used to prepare a COVERPAGEFIELDS structure for cover page rendering before rendering cover page.
  784. Arguments:
  785. [IN] pFaxCovInfo - Pointer to a FAX_COVERPAGE_INFO that holds the information to be extracted.
  786. [OUT] pCPFields - Pointer to a COVERPAGEFIELDS structure that gets filled with
  787. the information from FAX_COVERPAGE_INFO.
  788. Return Value:
  789. BOOL
  790. Comments:
  791. The function allocates memory.
  792. Call FreeCoverPageFields to free resources.
  793. --*/
  794. {
  795. DWORD dwDateTimeLen;
  796. DWORD cch;
  797. LPTSTR s;
  798. DWORD ec = 0;
  799. LPCTSTR *src;
  800. LPCTSTR *dst;
  801. DWORD i;
  802. TCHAR szTimeBuffer[MAX_PATH] = {0};
  803. TCHAR szNumberOfPages[12] = {0};
  804. Assert(pFaxCovInfo);
  805. Assert(pCPFields);
  806. memset(pCPFields,0,sizeof(COVERPAGEFIELDS));
  807. pCPFields->ThisStructSize = sizeof(COVERPAGEFIELDS);
  808. pCPFields->RecName = StringDup(pFaxCovInfo->RecName);
  809. pCPFields->RecFaxNumber = StringDup(pFaxCovInfo->RecFaxNumber);
  810. pCPFields->Subject = StringDup(pFaxCovInfo->Subject);
  811. pCPFields->Note = StringDup(pFaxCovInfo->Note);
  812. pCPFields->NumberOfPages = StringDup(_itot( pFaxCovInfo->PageCount, szNumberOfPages, 10 ));
  813. for (i = 0;
  814. i <= ((LPBYTE)&pFaxCovInfo->SdrEmail - (LPBYTE)&pFaxCovInfo->RecCompany)/sizeof(LPCTSTR);
  815. i++)
  816. {
  817. src = (LPCTSTR *) ((LPBYTE)(&pFaxCovInfo->RecCompany) + (i*sizeof(LPCTSTR)));
  818. dst = (LPCTSTR *) ((LPBYTE)(&(pCPFields->RecCompany)) + (i*sizeof(LPCTSTR)));
  819. if (*dst)
  820. {
  821. MemFree ( (LPBYTE) *dst ) ;
  822. }
  823. *dst = (LPCTSTR) StringDup( *src );
  824. }
  825. //
  826. // the time the fax was sent
  827. //
  828. GetLocalTime((LPSYSTEMTIME)&pFaxCovInfo->TimeSent);
  829. //
  830. // dwDataTimeLen is the size of s in characters
  831. //
  832. dwDateTimeLen = ARR_SIZE(szTimeBuffer);
  833. s = szTimeBuffer;
  834. //
  835. // Get date into s
  836. //
  837. GetY2KCompliantDate( LOCALE_USER_DEFAULT, 0, &pFaxCovInfo->TimeSent, s, dwDateTimeLen );
  838. //
  839. // Advance s past the date string and attempt to append time
  840. //
  841. cch = _tcslen( s );
  842. s += cch;
  843. if (++cch < dwDateTimeLen)
  844. {
  845. *s++ = ' ';
  846. //
  847. // DateTimeLen is the decreased by the size of s in characters
  848. //
  849. dwDateTimeLen -= cch;
  850. //
  851. // Get the time here
  852. //
  853. FaxTimeFormat( LOCALE_USER_DEFAULT, 0, &pFaxCovInfo->TimeSent, NULL, s, dwDateTimeLen );
  854. }
  855. pCPFields->TimeSent = StringDup( szTimeBuffer );
  856. return TRUE;
  857. }
  858. //*****************************************************************************
  859. //* Name: CreateCoverpageTiffFile
  860. //* Author:
  861. //*****************************************************************************
  862. //* DESCRIPTION:
  863. //* Renders the specified coverpage into a temp TIFF file and returns the name
  864. //* of the temp TIFF file.
  865. //* PARAMETERS:
  866. //* [IN] IN short Resolution:
  867. //* 196 for 200x200 resolution.
  868. //* 98 for 200x100 resolution.
  869. //* [IN] FAX_COVERPAGE_INFOW *CoverpageInfo:
  870. //* A pointer to a FAX_COVERPAGE_INFOW structure that contains the cover page
  871. //* template information (see SDK help).
  872. //* [IM] LPCWSTR lpcwstrExtension - File extension (".TIF" if NULL)
  873. //*
  874. //* [OUT] LPWSTR lpwstrCovTiffFile:
  875. //* A pointer to a buffer where the function returns the name of the temp file
  876. //* that contains the rendered cover page TIFF file.
  877. //*
  878. //* [IN] DWORD dwCovTiffFileCount:
  879. //* Size in TCHARs of the buffer pointed by lpwstrCovTiffFile.
  880. //* RETURN VALUE:
  881. //* FALSE if the operation failed.
  882. //* TRUE is succeeded.
  883. //* Comments:
  884. //* If the operation failes the function takes care of deleting any temp files.
  885. //*****************************************************************************
  886. BOOL
  887. CreateCoverpageTiffFile(
  888. IN short Resolution,
  889. IN const FAX_COVERPAGE_INFOW2 *CoverpageInfo,
  890. IN LPCWSTR lpcwstrExtension,
  891. OUT LPWSTR lpwstrCovTiffFile,
  892. IN DWORD dwCovTiffFileCount
  893. )
  894. {
  895. WCHAR TempFile[MAX_PATH];
  896. WCHAR wszCpName[MAX_PATH];
  897. LPWSTR FaxPrinter = NULL;
  898. BOOL Rslt = TRUE;
  899. COVDOCINFO covDocInfo;
  900. short Orientation = DMORIENT_PORTRAIT;
  901. DWORD ec = ERROR_SUCCESS;
  902. COVERPAGEFIELDS CpFields = {0};
  903. HRESULT hr;
  904. DEBUG_FUNCTION_NAME(TEXT("CreateCoverpageTiffFile()"));
  905. LPCWSTR lpcwstrFileExt = lpcwstrExtension ? lpcwstrExtension : FAX_TIF_FILE_EXT;
  906. TempFile[0] = L'\0';
  907. //
  908. // Validate the cover page and resolve the full path
  909. //
  910. if (!ValidateCoverpage((LPWSTR)CoverpageInfo->CoverPageName,
  911. NULL,
  912. CoverpageInfo->UseServerCoverPage,
  913. wszCpName,
  914. ARR_SIZE(wszCpName)))
  915. {
  916. ec = GetLastError();
  917. DebugPrintEx(DEBUG_ERR,
  918. TEXT("ValidateCoverpage failed. ec = %ld"),
  919. ec);
  920. Rslt=FALSE;
  921. goto Exit;
  922. }
  923. //
  924. // Collect the cover page fields
  925. //
  926. FillCoverPageFields( CoverpageInfo, &CpFields);
  927. FaxPrinter = GetFaxPrinterName();
  928. if (FaxPrinter == NULL)
  929. {
  930. ec = GetLastError();
  931. DebugPrintEx(
  932. DEBUG_ERR,
  933. TEXT("GetFaxPrinterName failed. ec = %ld"),
  934. ec);
  935. Rslt=FALSE;
  936. goto Exit;
  937. }
  938. //
  939. // Get the cover page orientation
  940. //
  941. ec = PrintCoverPage(NULL, NULL, wszCpName, &covDocInfo);
  942. if (ERROR_SUCCESS != ec)
  943. {
  944. DebugPrintEx(
  945. DEBUG_ERR,
  946. TEXT("PrintCoverPage for coverpage %s failed (ec: %ld)"),
  947. CoverpageInfo->CoverPageName,
  948. ec);
  949. Rslt=FALSE;
  950. goto Exit;
  951. }
  952. if (!GenerateUniqueFileName( g_wszFaxQueueDir, (LPWSTR)lpcwstrFileExt, TempFile, sizeof(TempFile)/sizeof(WCHAR) ))
  953. {
  954. ec = GetLastError();
  955. DebugPrintEx(DEBUG_ERR,TEXT("Failed to generate unique file name for merged TIFF file (ec: %ld)."), ec);
  956. Rslt=FALSE;
  957. goto Exit;
  958. }
  959. //
  960. // Change the default orientation if needed
  961. //
  962. if (covDocInfo.Orientation == DMORIENT_LANDSCAPE)
  963. {
  964. Orientation = DMORIENT_LANDSCAPE;
  965. }
  966. //
  967. // Render the cover page to a file
  968. //
  969. ec = PrintCoverPageToFile(
  970. wszCpName,
  971. TempFile,
  972. FaxPrinter,
  973. Orientation,
  974. Resolution,
  975. &CpFields);
  976. if (ERROR_SUCCESS != ec)
  977. {
  978. DebugPrintEx(
  979. DEBUG_ERR,
  980. TEXT("PrintCoverPageToFile for coverpage %s failed (ec: %ld)"),
  981. CoverpageInfo->CoverPageName,
  982. ec);
  983. Rslt=FALSE;
  984. if (!DeleteFile( TempFile ))
  985. {
  986. DebugPrintEx(
  987. DEBUG_ERR,
  988. TEXT("DeleteFile for file %s failed (ec: %ld)"),
  989. TempFile,
  990. GetLastError());
  991. }
  992. goto Exit;
  993. }
  994. hr = StringCchCopy(
  995. lpwstrCovTiffFile,
  996. dwCovTiffFileCount,
  997. TempFile);
  998. if (FAILED(hr))
  999. {
  1000. DebugPrintEx(
  1001. DEBUG_ERR,
  1002. TEXT("StringCchCopy for coverpage %s failed (ec: %ld)"),
  1003. CoverpageInfo->CoverPageName,
  1004. hr);
  1005. Rslt=FALSE;
  1006. ec = HRESULT_CODE(hr);
  1007. if (!DeleteFile( TempFile ))
  1008. {
  1009. DebugPrintEx(
  1010. DEBUG_ERR,
  1011. TEXT("DeleteFile for file %s failed (ec: %ld)"),
  1012. TempFile,
  1013. GetLastError());
  1014. }
  1015. goto Exit;
  1016. }
  1017. Rslt = TRUE;
  1018. Exit:
  1019. MemFree(FaxPrinter);
  1020. FreeCpFields(&CpFields);
  1021. if (FALSE == Rslt)
  1022. {
  1023. ec = (ERROR_SUCCESS != ec) ? ec : ERROR_GEN_FAILURE;
  1024. SetLastError(ec);
  1025. }
  1026. return Rslt;
  1027. }
  1028. //*****************************************************************************
  1029. //* Name: GetBodyTiffResolution
  1030. //* Author:
  1031. //*****************************************************************************
  1032. //* DESCRIPTION:
  1033. //* Returns the body tiff file resolution. (200x200 or 200x100)
  1034. //* The resolution is determined by the first page only!!
  1035. //* PARAMETERS:
  1036. //*
  1037. //* [IN] LPCWSTR lpcwstrBodyFile - Body tiff file
  1038. //*
  1039. //* [OUT] short* pResolution:
  1040. //* A pointer to a short where the function returns the tiff resolution.
  1041. //* TRUE is 200x200. FALSE is 200x100
  1042. //* RETURN VALUE:
  1043. //* FALSE if the operation failed.
  1044. //* TRUE is succeeded.
  1045. //* Comments:
  1046. //*****************************************************************************
  1047. BOOL
  1048. GetBodyTiffResolution(
  1049. IN LPCWSTR lpcwstrBodyFile,
  1050. OUT short* pResolution
  1051. )
  1052. {
  1053. DEBUG_FUNCTION_NAME(TEXT("GetBodyTiffResolution"));
  1054. TIFF_INFO TiffInfo;
  1055. HANDLE hTiff = NULL;
  1056. BOOL RetVal = TRUE;
  1057. Assert (lpcwstrBodyFile && pResolution);
  1058. //
  1059. // open the tiff file
  1060. //
  1061. hTiff = TiffOpen( lpcwstrBodyFile, &TiffInfo, TRUE, FILLORDER_MSB2LSB );
  1062. if (hTiff == NULL)
  1063. {
  1064. DebugPrintEx(
  1065. DEBUG_ERR,
  1066. TEXT("TiffOpen() failed. Tiff file: %s"),
  1067. lpcwstrBodyFile);
  1068. RetVal = FALSE;
  1069. goto exit;
  1070. }
  1071. if (TiffInfo.YResolution != 98 &&
  1072. TiffInfo.YResolution != 196)
  1073. {
  1074. DebugPrintEx(
  1075. DEBUG_ERR,
  1076. TEXT("Invalid Tiff Resolutoin. Tiff file: %s, YRes: %ld."),
  1077. lpcwstrBodyFile,
  1078. TiffInfo.YResolution);
  1079. RetVal = FALSE;
  1080. goto exit;
  1081. }
  1082. *pResolution = TiffInfo.YResolution;
  1083. Assert (TRUE == RetVal);
  1084. exit:
  1085. if (NULL != hTiff)
  1086. {
  1087. if (!TiffClose(hTiff))
  1088. {
  1089. DebugPrintEx(
  1090. DEBUG_ERR,
  1091. TEXT("TiffClose() failed. Tiff file: %s"),
  1092. lpcwstrBodyFile);
  1093. }
  1094. }
  1095. return RetVal;
  1096. }
  1097. //*********************************************************************************
  1098. //* Name: CreateTiffFile ()
  1099. //* Author: Ronen Barenboim
  1100. //* Date: March 24, 1999
  1101. //*********************************************************************************
  1102. //* DESCRIPTION:
  1103. //* Creates the TIFF file for a job queue.
  1104. //*
  1105. //* The function deals with generating the cover page file and merging it
  1106. //* with the body file (if a body exists).
  1107. //* It returns the name of the TIFF file it generated. The caller must delete
  1108. //* this file when it is no longer needed.
  1109. //* PARAMETERS:
  1110. //* PJOB_QUEUE lpJob
  1111. //* A pointer to a JOB_QUEUE structure that holds the recipient or routing job
  1112. //* information.
  1113. //* LPCWSTR lpcwstrFileExt - The new file extension (Null will create the default "*.TIF"
  1114. //*
  1115. //* LPWSTR lpwstrFullPath - Pointer to a buffer to receive the full path to the new file
  1116. //*
  1117. //* DWORD dwFullPathCount - size in TCHARs of the buffer pointed by lpwstrFullPath.
  1118. //*
  1119. //* RETURN VALUE:
  1120. //* TRUE if successful.
  1121. //* FALSE otherwise. Set last erorr on failure
  1122. //*********************************************************************************
  1123. BOOL
  1124. CreateTiffFile (
  1125. PJOB_QUEUE lpJob,
  1126. LPCWSTR lpcwstrFileExt,
  1127. LPWSTR lpwstrFullPath,
  1128. DWORD dwFullPathCount
  1129. )
  1130. {
  1131. DEBUG_FUNCTION_NAME(TEXT("CreateTiffFile"));
  1132. Assert(lpJob && lpwstrFullPath);
  1133. Assert(JT_SEND == lpJob->JobType ||
  1134. JT_ROUTING == lpJob->JobType);
  1135. PJOB_QUEUE lpParentJob = NULL;
  1136. WCHAR szCoverPageTiffFile[MAX_PATH] = {0};
  1137. LPCWSTR lpcwstrCoverPageFileName;
  1138. LPCWSTR lpcwstrBodyFileName;
  1139. short Resolution = 0; // Default resolution
  1140. BOOL bRes = FALSE;
  1141. HRESULT hr;
  1142. if (JT_SEND == lpJob->JobType)
  1143. {
  1144. lpParentJob = lpJob->lpParentJob;
  1145. Assert(lpParentJob);
  1146. }
  1147. lpcwstrCoverPageFileName = lpParentJob ? lpParentJob->CoverPageEx.lptstrCoverPageFileName : NULL;
  1148. lpcwstrBodyFileName = lpParentJob ? lpParentJob->FileName : lpJob->FileName;
  1149. if (!lpcwstrCoverPageFileName)
  1150. {
  1151. //
  1152. // No cover page specified.
  1153. // The TIFF to send is the body only.
  1154. // Copy the body for each recipient
  1155. //
  1156. Assert(lpcwstrBodyFileName); // must have a body in this case.
  1157. LPCWSTR lpcwstrExt = lpcwstrFileExt ? lpcwstrFileExt : FAX_TIF_FILE_EXT;
  1158. if (!GenerateUniqueFileName( g_wszFaxQueueDir,
  1159. (LPWSTR)lpcwstrExt,
  1160. szCoverPageTiffFile,
  1161. sizeof(szCoverPageTiffFile)/sizeof(WCHAR) ))
  1162. {
  1163. DebugPrintEx(
  1164. DEBUG_ERR,
  1165. TEXT("GenerateUniqueFileName() failed (ec: %ld)."),
  1166. GetLastError());
  1167. goto Exit;
  1168. }
  1169. if (!CopyFile (lpcwstrBodyFileName, szCoverPageTiffFile, FALSE)) // FALSE - File already exist
  1170. {
  1171. DebugPrintEx(DEBUG_ERR,
  1172. TEXT("CopyFile Failed with %ld "),
  1173. GetLastError());
  1174. DeleteFile(szCoverPageTiffFile);
  1175. goto Exit;
  1176. }
  1177. hr = StringCchCopy(
  1178. lpwstrFullPath,
  1179. dwFullPathCount,
  1180. szCoverPageTiffFile);
  1181. if (FAILED(hr))
  1182. {
  1183. DebugPrintEx(DEBUG_ERR,
  1184. TEXT("StringCchCopy Failed with %ld "),
  1185. hr);
  1186. DeleteFile(szCoverPageTiffFile);
  1187. SetLastError(HRESULT_CODE(hr));
  1188. }
  1189. else
  1190. {
  1191. bRes = TRUE;
  1192. }
  1193. goto Exit;
  1194. }
  1195. //
  1196. // There is a cover page so the tiff is either just the cover page or the cover page
  1197. // merged with the body.
  1198. //
  1199. if (lpParentJob->FileName)
  1200. {
  1201. if (!GetBodyTiffResolution(lpParentJob->FileName, &Resolution))
  1202. {
  1203. DebugPrintEx(
  1204. DEBUG_ERR,
  1205. TEXT("GetBodyTiffResolution() failed (ec: %ld)."),
  1206. GetLastError());
  1207. goto Exit;
  1208. }
  1209. }
  1210. Assert (Resolution == 0 || Resolution == 98 || Resolution == 196);
  1211. //
  1212. // First create the cover page (This generates a file and returns its name).
  1213. //
  1214. if (!CreateCoverpageTiffFileEx(
  1215. Resolution,
  1216. lpJob->PageCount,
  1217. &lpParentJob->CoverPageEx,
  1218. &lpJob->RecipientProfile,
  1219. &lpParentJob->SenderProfile,
  1220. lpcwstrFileExt,
  1221. szCoverPageTiffFile,
  1222. ARR_SIZE(szCoverPageTiffFile)))
  1223. {
  1224. DebugPrintEx(DEBUG_ERR,
  1225. TEXT("[JobId: %ld] Failed to render cover page template %s"),
  1226. lpJob->JobId,
  1227. lpParentJob->CoverPageEx.lptstrCoverPageFileName);
  1228. goto Exit;
  1229. }
  1230. if (lpParentJob->FileName)
  1231. {
  1232. //
  1233. // There is a body file specified so merge the body and the cover page into
  1234. // the file specified in szCoverPageTiffFile.
  1235. //
  1236. if (!MergeTiffFiles( szCoverPageTiffFile, lpParentJob->FileName))
  1237. {
  1238. DebugPrintEx(DEBUG_ERR,
  1239. TEXT("[JobId: %ld] Failed to merge cover (%ws) and body (%ws). (ec: %ld)"),
  1240. lpJob->JobId,
  1241. szCoverPageTiffFile,
  1242. lpParentJob->FileName,
  1243. GetLastError());
  1244. //
  1245. // Get rid of the coverpage TIFF we generated.
  1246. //
  1247. if (!DeleteFile(szCoverPageTiffFile))
  1248. {
  1249. DebugPrintEx(DEBUG_ERR,
  1250. TEXT("[JobId: %ld] Failed to delete cover page TIFF file %ws. (ec: %ld)"),
  1251. lpJob->JobId,
  1252. szCoverPageTiffFile,
  1253. GetLastError());
  1254. }
  1255. goto Exit;
  1256. }
  1257. }
  1258. hr = StringCchCopy(
  1259. lpwstrFullPath,
  1260. dwFullPathCount,
  1261. szCoverPageTiffFile);
  1262. if (FAILED(hr))
  1263. {
  1264. DebugPrintEx(DEBUG_ERR,
  1265. TEXT("StringCchCopy Failed with %ld "),
  1266. hr);
  1267. DeleteFile(szCoverPageTiffFile);
  1268. SetLastError(HRESULT_CODE(hr));
  1269. goto Exit;
  1270. }
  1271. bRes = TRUE;
  1272. Exit:
  1273. if (FALSE == bRes)
  1274. {
  1275. //
  1276. // Make sure we set last error
  1277. //
  1278. if (ERROR_SUCCESS == GetLastError())
  1279. {
  1280. SetLastError (ERROR_GEN_FAILURE);
  1281. }
  1282. }
  1283. return bRes;
  1284. } // CreateTiffFile
  1285. BOOL
  1286. CreateTiffFileForJob (
  1287. PJOB_QUEUE lpRecpJob
  1288. )
  1289. {
  1290. DEBUG_FUNCTION_NAME(TEXT("CreateTiffFileForJob"));
  1291. WCHAR wszFullPath[MAX_PATH] = {0};
  1292. Assert(lpRecpJob);
  1293. if (!CreateTiffFile (lpRecpJob, TEXT("FRT"), wszFullPath, ARR_SIZE(wszFullPath)))
  1294. {
  1295. DebugPrintEx(DEBUG_ERR,
  1296. TEXT("CreateTiffFile failed. (ec: %ld)"),
  1297. GetLastError());
  1298. return FALSE;
  1299. }
  1300. if (NULL == (lpRecpJob->FileName = StringDup(wszFullPath)))
  1301. {
  1302. DWORD dwErr = GetLastError();
  1303. DebugPrintEx(DEBUG_ERR,
  1304. TEXT("StringDup failed. (ec: %ld)"),
  1305. dwErr);
  1306. if (!DeleteFile(wszFullPath))
  1307. {
  1308. DebugPrintEx(DEBUG_ERR,
  1309. TEXT("[JobId: %ld] Failed to delete TIFF file %ws. (ec: %ld)"),
  1310. lpRecpJob->JobId,
  1311. wszFullPath,
  1312. GetLastError());
  1313. }
  1314. SetLastError(dwErr);
  1315. return FALSE;
  1316. }
  1317. return TRUE;
  1318. }
  1319. BOOL
  1320. CreateTiffFileForPreview (
  1321. PJOB_QUEUE lpRecpJob
  1322. )
  1323. {
  1324. DEBUG_FUNCTION_NAME(TEXT("CreateTiffFileForPreview"));
  1325. WCHAR wszFullPath[MAX_PATH] = {0};
  1326. Assert(lpRecpJob);
  1327. if (lpRecpJob->PreviewFileName)
  1328. {
  1329. return TRUE;
  1330. }
  1331. if (!CreateTiffFile (lpRecpJob, TEXT("PRV"), wszFullPath, ARR_SIZE(wszFullPath)))
  1332. {
  1333. DebugPrintEx(DEBUG_ERR,
  1334. TEXT("CreateTiffFile failed. (ec: %ld)"),
  1335. GetLastError());
  1336. return FALSE;
  1337. }
  1338. if (NULL == (lpRecpJob->PreviewFileName = StringDup(wszFullPath)))
  1339. {
  1340. DWORD dwErr = GetLastError();
  1341. DebugPrintEx(DEBUG_ERR,
  1342. TEXT("StringDup failed. (ec: %ld)"),
  1343. dwErr);
  1344. if (!DeleteFile(wszFullPath))
  1345. {
  1346. DebugPrintEx(DEBUG_ERR,
  1347. TEXT("[JobId: %ld] Failed to delete TIFF file %ws. (ec: %ld)"),
  1348. lpRecpJob->JobId,
  1349. wszFullPath,
  1350. GetLastError());
  1351. }
  1352. SetLastError(dwErr);
  1353. return FALSE;
  1354. }
  1355. return TRUE;
  1356. }
  1357. DWORD
  1358. FaxRouteThread(
  1359. PJOB_QUEUE lpJobQueueEntry
  1360. )
  1361. /*++
  1362. Routine Description:
  1363. This fuction runs asychronously as a separate thread to
  1364. route an incoming job.
  1365. Arguments:
  1366. lpJobQueueEntry - A pointer to the job for which the routing
  1367. operation is to be performed.
  1368. Return Value:
  1369. Always zero.
  1370. --*/
  1371. {
  1372. BOOL Routed = TRUE;
  1373. DWORD i;
  1374. DWORD dwRes;
  1375. DWORD CountFailureInfo = 0;
  1376. DEBUG_FUNCTION_NAME(TEXT("FaxRouteThread"));
  1377. EnterCriticalSectionJobAndQueue;
  1378. CountFailureInfo = lpJobQueueEntry->CountFailureInfo;
  1379. LeaveCriticalSectionJobAndQueue;
  1380. for (i = 0; i < lpJobQueueEntry->CountFailureInfo; i++)
  1381. {
  1382. BOOL fRouteSucceed;
  1383. fRouteSucceed = FaxRouteRetry( lpJobQueueEntry->FaxRoute, &lpJobQueueEntry->pRouteFailureInfo[i] );
  1384. if (FALSE == fRouteSucceed)
  1385. {
  1386. PROUTING_METHOD pRoutingMethod = FindRoutingMethodByGuid( (lpJobQueueEntry->pRouteFailureInfo[i]).GuidString );
  1387. if (pRoutingMethod)
  1388. {
  1389. WCHAR TmpStr[20] = {0};
  1390. HRESULT hr = StringCchPrintf(
  1391. TmpStr,
  1392. ARR_SIZE(TmpStr),
  1393. TEXT("0x%016I64x"),
  1394. lpJobQueueEntry->UniqueId);
  1395. if (FAILED(hr))
  1396. {
  1397. //
  1398. // Should never happen, we use large enough buffer.
  1399. //
  1400. ASSERT_FALSE;
  1401. }
  1402. FaxLog(FAXLOG_CATEGORY_INBOUND,
  1403. FAXLOG_LEVEL_MIN,
  1404. 6,
  1405. MSG_FAX_ROUTE_METHOD_FAILED,
  1406. TmpStr,
  1407. lpJobQueueEntry->FaxRoute->DeviceName,
  1408. lpJobQueueEntry->FaxRoute->Tsid,
  1409. lpJobQueueEntry->FileName,
  1410. pRoutingMethod->RoutingExtension->FriendlyName,
  1411. pRoutingMethod->FriendlyName
  1412. );
  1413. }
  1414. }
  1415. Routed &= fRouteSucceed;
  1416. }
  1417. EnterCriticalSectionJobAndQueue;
  1418. lpJobQueueEntry->dwLastJobExtendedStatus = 0;
  1419. lpJobQueueEntry->ExStatusString[0] = TEXT('\0');
  1420. if ( Routed )
  1421. {
  1422. lpJobQueueEntry->JobStatus = JS_DELETING;
  1423. DecreaseJobRefCount (lpJobQueueEntry, TRUE);
  1424. }
  1425. else
  1426. {
  1427. //
  1428. // We failed to execute the routing method.
  1429. // reschedule the job.
  1430. //
  1431. DWORD dwMaxRetries;
  1432. EnterCriticalSection (&g_CsConfig);
  1433. dwMaxRetries = g_dwFaxSendRetries;
  1434. LeaveCriticalSection (&g_CsConfig);
  1435. lpJobQueueEntry->SendRetries++;
  1436. if (lpJobQueueEntry->SendRetries <= dwMaxRetries)
  1437. {
  1438. lpJobQueueEntry->JobStatus = JS_RETRYING;
  1439. RescheduleJobQueueEntry( lpJobQueueEntry );
  1440. }
  1441. else
  1442. {
  1443. //
  1444. // retries exceeded, mark job as expired
  1445. //
  1446. MarkJobAsExpired(lpJobQueueEntry);
  1447. WCHAR TmpStr[20] = {0};
  1448. HRESULT hr = StringCchPrintf(
  1449. TmpStr,
  1450. ARR_SIZE(TmpStr),
  1451. TEXT("0x%016I64x"),
  1452. lpJobQueueEntry->UniqueId);
  1453. if (FAILED(hr))
  1454. {
  1455. //
  1456. // Should never happen, we use large enough buffer.
  1457. //
  1458. ASSERT_FALSE;
  1459. }
  1460. FaxLog(FAXLOG_CATEGORY_INBOUND,
  1461. FAXLOG_LEVEL_MIN,
  1462. 3,
  1463. MSG_FAX_ROUTE_FAILED,
  1464. TmpStr,
  1465. lpJobQueueEntry->FaxRoute->DeviceName,
  1466. lpJobQueueEntry->FaxRoute->Tsid
  1467. );
  1468. }
  1469. //
  1470. // Create Fax EventEx
  1471. //
  1472. dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS,
  1473. lpJobQueueEntry
  1474. );
  1475. if (ERROR_SUCCESS != dwRes)
  1476. {
  1477. DebugPrintEx( DEBUG_ERR,
  1478. _T("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) ")
  1479. _T("failed for job id %ld (ec: %lc)"),
  1480. lpJobQueueEntry->UniqueId,
  1481. dwRes);
  1482. }
  1483. if (!UpdatePersistentJobStatus(lpJobQueueEntry))
  1484. {
  1485. DebugPrintEx( DEBUG_ERR,
  1486. _T("Failed to update persistent job status to 0x%08x"),
  1487. lpJobQueueEntry->JobStatus);
  1488. }
  1489. }
  1490. LeaveCriticalSectionJobAndQueue;
  1491. if (!DecreaseServiceThreadsCount())
  1492. {
  1493. DebugPrintEx(
  1494. DEBUG_ERR,
  1495. TEXT("DecreaseServiceThreadsCount() failed (ec: %ld)"),
  1496. GetLastError());
  1497. }
  1498. return ERROR_SUCCESS;
  1499. }
  1500. DWORD
  1501. FaxSendThread(
  1502. PFAX_SEND_ITEM FaxSendItem
  1503. )
  1504. /*++
  1505. Routine Description:
  1506. This fuction runs asychronously as a separate thread to
  1507. send a FAX document. There is one send thread per outstanding
  1508. FAX send operation. The thread ends when the document is
  1509. either successfuly sent or the operation is aborted.
  1510. Arguments:
  1511. FaxSendItem - pointer to a FAX send item packet that
  1512. describes the requested FAX send operation.
  1513. Return Value:
  1514. Always zero.
  1515. --*/
  1516. {
  1517. FAX_SEND FaxSend; // This structure is passed to FaxDevSend()
  1518. BOOL Rslt = FALSE;
  1519. BOOL Retrying = FALSE;
  1520. BOOL bFakeJobStatus = FALSE;
  1521. FSPI_JOB_STATUS FakedJobStatus = {0};
  1522. DWORD PageCount = 0;
  1523. BOOL bRemoveParentJob = FALSE; // TRUE if at the end of the send the parent job and all
  1524. // recipients need to be removed.
  1525. PJOB_QUEUE lpJobQueue = NULL ; // Points to the Queue entry attached to the running job.
  1526. LPFSPI_JOB_STATUS lpFSPStatus = NULL;
  1527. LPFSPI_JOB_STATUS pOrigFaxStatus = NULL;
  1528. DWORD dwSttRes;
  1529. BOOL bBranding;
  1530. DWORD dwJobId;
  1531. BOOL bCreateTiffFailed = FALSE;
  1532. BOOL fSetSystemIdleTimer = TRUE;
  1533. DEBUG_FUNCTION_NAME(TEXT("FaxSendThread"));
  1534. Assert (FaxSendItem &&
  1535. FaxSendItem->JobEntry &&
  1536. FaxSendItem->JobEntry->LineInfo &&
  1537. FaxSendItem->JobEntry->LineInfo->Provider);
  1538. //
  1539. // Don't let the system go to sleep in the middle of the fax transmission.
  1540. //
  1541. if (NULL == SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_CONTINUOUS))
  1542. {
  1543. fSetSystemIdleTimer = FALSE;
  1544. DebugPrintEx(DEBUG_ERR,
  1545. TEXT("SetThreadExecutionState() failed"));
  1546. }
  1547. lpJobQueue=FaxSendItem->JobEntry->lpJobQueueEntry;
  1548. Assert(lpJobQueue);
  1549. //
  1550. // Set the information to be sent to FaxDevSend()
  1551. // Note:
  1552. // The caller number is the sender TSID ! (we have no other indication of the sender phone number)
  1553. // This means that the FSP will get the sender TSID which might contain text as well (not just a number)
  1554. //
  1555. FaxSend.SizeOfStruct = sizeof(FAX_SEND);
  1556. FaxSend.CallerName = FaxSendItem->SenderName;
  1557. FaxSend.CallerNumber = FaxSendItem->Tsid;
  1558. FaxSend.ReceiverName = FaxSendItem->RecipientName;
  1559. FaxSend.ReceiverNumber = FaxSendItem->PhoneNumber;
  1560. FaxSend.CallHandle = 0; // filled in later via TapiStatusThread, if appropriate
  1561. FaxSend.Reserved[0] = 0;
  1562. FaxSend.Reserved[1] = 0;
  1563. FaxSend.Reserved[2] = 0;
  1564. //
  1565. // Successfully created a new send job on a device. Update counter.
  1566. //
  1567. (VOID)UpdateDeviceJobsCounter ( FaxSendItem->JobEntry->LineInfo, // Device to update
  1568. TRUE, // Sending
  1569. 1, // Number of new jobs
  1570. TRUE); // Enable events
  1571. if (!lpJobQueue->FileName)
  1572. {
  1573. //
  1574. // We did not generate a body for this recipient yet. This is the
  1575. // time to do so.
  1576. //
  1577. //
  1578. // Set the right body for this job.
  1579. // This is either the body specified at the parent or a merge of the body
  1580. // with the cover page specified in the parent.
  1581. //
  1582. DebugPrintEx(
  1583. DEBUG_MSG,
  1584. TEXT("[JobId: %ld] Generating body for recipient job."),
  1585. lpJobQueue->JobId
  1586. );
  1587. if (!CreateTiffFileForJob(lpJobQueue))
  1588. {
  1589. DebugPrintEx(
  1590. DEBUG_ERR,
  1591. TEXT("[JobId: %ld] CreateTiffFileForJob failed. (ec: %ld)"),
  1592. lpJobQueue->JobId,
  1593. GetLastError()
  1594. );
  1595. bCreateTiffFailed = TRUE;
  1596. }
  1597. }
  1598. else
  1599. {
  1600. //
  1601. // We already generated a body for this recipient.
  1602. // somthing is wrong
  1603. //
  1604. DebugPrintEx(
  1605. DEBUG_ERR,
  1606. TEXT("[JobId: %ld] Using cached body in %s."),
  1607. lpJobQueue->JobId,
  1608. lpJobQueue->FileName
  1609. );
  1610. Assert(FALSE);
  1611. }
  1612. if (bCreateTiffFailed ||
  1613. NULL == (FaxSendItem->FileName = StringDup(lpJobQueue->FileName)))
  1614. {
  1615. DebugPrintEx(
  1616. DEBUG_ERR,
  1617. TEXT("[JobId: %ld] CreateTiffFileForJob or StringDup failed"),
  1618. FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
  1619. GetLastError());
  1620. //
  1621. // Simulate an FSP returning a FS_FATAL_ERROR code.
  1622. //
  1623. EnterCriticalSection(&g_CsJob);
  1624. FreeFSPIJobStatus(&FaxSendItem->JobEntry->FSPIJobStatus, FALSE);
  1625. FaxSendItem->JobEntry->FSPIJobStatus.dwJobStatus = FSPI_JS_FAILED;
  1626. FaxSendItem->JobEntry->FSPIJobStatus.dwExtendedStatus = FSPI_ES_FATAL_ERROR;
  1627. if (!HandleFailedSendJob(FaxSendItem->JobEntry))
  1628. {
  1629. DebugPrintEx(
  1630. DEBUG_ERR,
  1631. TEXT("[JobId: %ld] HandleFailedSendJob() failed (ec: %ld)."),
  1632. FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
  1633. GetLastError());
  1634. }
  1635. LeaveCriticalSection(&g_CsJob);
  1636. goto Exit;
  1637. }
  1638. FaxSend.FileName = FaxSendItem->FileName;
  1639. //
  1640. // Add branding banner (the line at the top of each page) to the fax if necessary.
  1641. //
  1642. //
  1643. // Our service takes care of branding so notify FSP not to brand
  1644. //
  1645. FaxSend.Branding = FALSE;
  1646. EnterCriticalSection (&g_CsConfig);
  1647. bBranding = g_fFaxUseBranding;
  1648. LeaveCriticalSection (&g_CsConfig);
  1649. if (bBranding)
  1650. {
  1651. FSPI_BRAND_INFO brandInfo;
  1652. HRESULT hr;
  1653. memset(&brandInfo,0,sizeof(FSPI_BRAND_INFO));
  1654. brandInfo.dwSizeOfStruct=sizeof(FSPI_BRAND_INFO);
  1655. brandInfo.lptstrRecipientPhoneNumber = FaxSendItem->JobEntry->lpJobQueueEntry->RecipientProfile.lptstrFaxNumber;
  1656. brandInfo.lptstrSenderCompany = FaxSendItem->SenderCompany;
  1657. brandInfo.lptstrSenderTsid = FaxSendItem->Tsid;
  1658. GetLocalTime( &brandInfo.tmDateTime); // can't fail
  1659. hr = FaxBrandDocument(FaxSendItem->FileName,&brandInfo);
  1660. if (FAILED(hr))
  1661. {
  1662. DebugPrintEx(
  1663. DEBUG_ERR,
  1664. TEXT("[JobId: %ld] FaxBrandDocument() failed. (hr: 0x%08X)"),
  1665. lpJobQueue->JobId,
  1666. hr);
  1667. //
  1668. // But we go on since it is better to send the fax without the branding
  1669. // then lose it altogether.
  1670. //
  1671. }
  1672. }
  1673. FaxSendItem->JobEntry->LineInfo->State = FPS_INITIALIZING;
  1674. DebugPrintEx(
  1675. DEBUG_MSG,
  1676. TEXT("[JobId: %ld] Calling FaxDevSend().\n\t File: %s\n\tNumber [%s]\n\thLine = 0x%08X\n\tCallHandle = 0x%08X"),
  1677. lpJobQueue->JobId,
  1678. FaxSend.FileName,
  1679. FaxSendItem->JobEntry->DialablePhoneNumber,
  1680. FaxSendItem->JobEntry->LineInfo->hLine,
  1681. FaxSend.CallHandle
  1682. );
  1683. __try
  1684. {
  1685. //
  1686. // Send the fax (This call is blocking)
  1687. //
  1688. Rslt = FaxSendItem->JobEntry->LineInfo->Provider->FaxDevSend(
  1689. (HANDLE) FaxSendItem->JobEntry->InstanceData,
  1690. &FaxSend,
  1691. FaxSendCallback
  1692. );
  1693. if (!Rslt)
  1694. {
  1695. DebugPrintEx(
  1696. DEBUG_ERR,
  1697. TEXT("[JobId: %ld] FaxDevSend() failed (ec: 0x%0X)"),
  1698. lpJobQueue->JobId,
  1699. GetLastError());
  1700. }
  1701. }
  1702. __except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, FaxSendItem->JobEntry->LineInfo->Provider->FriendlyName, GetExceptionCode()))
  1703. {
  1704. ASSERT_FALSE;
  1705. }
  1706. //
  1707. // Get the final status of the job.
  1708. //
  1709. dwSttRes = GetDevStatus((HANDLE) FaxSendItem->JobEntry->InstanceData,
  1710. FaxSendItem->JobEntry->LineInfo,
  1711. &lpFSPStatus);
  1712. if (ERROR_SUCCESS != dwSttRes)
  1713. {
  1714. //
  1715. // Couldn't retrieve device status.
  1716. // Fake one.
  1717. //
  1718. bFakeJobStatus = TRUE;
  1719. DebugPrintEx(DEBUG_ERR,
  1720. TEXT("[Job: %ld] GetDevStatus failed - %d"),
  1721. FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
  1722. dwSttRes);
  1723. }
  1724. else if ((FSPI_JS_COMPLETED != lpFSPStatus->dwJobStatus) &&
  1725. (FSPI_JS_ABORTED != lpFSPStatus->dwJobStatus) &&
  1726. (FSPI_JS_FAILED != lpFSPStatus->dwJobStatus) &&
  1727. (FSPI_JS_DELETED != lpFSPStatus->dwJobStatus) &&
  1728. (FSPI_JS_FAILED_NO_RETRY != lpFSPStatus->dwJobStatus))
  1729. {
  1730. //
  1731. // Status returned is unacceptable - fake one.
  1732. //
  1733. bFakeJobStatus = TRUE;
  1734. DebugPrintEx(DEBUG_WRN,
  1735. TEXT("[Job: %ld] GetDevStatus return unacceptable status - %d. Faking the status"),
  1736. FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
  1737. lpFSPStatus->dwJobStatus);
  1738. pOrigFaxStatus = lpFSPStatus;
  1739. memcpy (&FakedJobStatus, lpFSPStatus, sizeof (FakedJobStatus));
  1740. if (lpFSPStatus->fAvailableStatusInfo & FSPI_JOB_STATUS_INFO_FSP_PRIVATE_STATUS_CODE)
  1741. {
  1742. //
  1743. // The FSP returned proprietary status.
  1744. //
  1745. FakedJobStatus.dwExtendedStatus = lpFSPStatus->dwExtendedStatus;
  1746. FakedJobStatus.dwExtendedStatusStringId = lpFSPStatus->dwExtendedStatusStringId;
  1747. }
  1748. lpFSPStatus = NULL;
  1749. }
  1750. //
  1751. // Enter critical section to block out FaxStatusThread
  1752. //
  1753. EnterCriticalSection( &g_CsJob );
  1754. if (bFakeJobStatus)
  1755. {
  1756. //
  1757. // Fake a job status
  1758. //
  1759. lpFSPStatus = &FakedJobStatus;
  1760. FakedJobStatus.dwSizeOfStruct = sizeof (FakedJobStatus);
  1761. if (Rslt)
  1762. {
  1763. //
  1764. // Fake success
  1765. //
  1766. FakedJobStatus.dwJobStatus = FSPI_JS_COMPLETED;
  1767. if (0 == FakedJobStatus.dwExtendedStatus)
  1768. {
  1769. //
  1770. // The FSP did not report proprietary status
  1771. //
  1772. FakedJobStatus.dwExtendedStatus = FSPI_ES_CALL_COMPLETED;
  1773. }
  1774. }
  1775. else
  1776. {
  1777. //
  1778. // Fake failure
  1779. //
  1780. FakedJobStatus.dwJobStatus = FSPI_JS_FAILED;
  1781. if (0 == FakedJobStatus.dwExtendedStatus)
  1782. {
  1783. //
  1784. // The FSP did not report proprietary status
  1785. //
  1786. FakedJobStatus.dwExtendedStatus = FSPI_ES_FATAL_ERROR;
  1787. }
  1788. }
  1789. }
  1790. if (!UpdateJobStatus(FaxSendItem->JobEntry, lpFSPStatus))
  1791. {
  1792. DebugPrintEx(
  1793. DEBUG_ERR,
  1794. TEXT("[JobId: %ld] UpdateJobStatus() failed (ec: %ld)."),
  1795. FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
  1796. GetLastError());
  1797. //
  1798. // Fake a status (we must have some valid status in job entry)
  1799. //
  1800. FreeFSPIJobStatus(&FaxSendItem->JobEntry->FSPIJobStatus, FALSE);
  1801. if (Rslt)
  1802. {
  1803. FaxSendItem->JobEntry->FSPIJobStatus.dwJobStatus = FSPI_JS_COMPLETED;
  1804. FaxSendItem->JobEntry->FSPIJobStatus.dwExtendedStatus = FSPI_ES_CALL_COMPLETED;
  1805. }
  1806. else
  1807. {
  1808. FaxSendItem->JobEntry->FSPIJobStatus.dwJobStatus = FSPI_JS_FAILED;
  1809. FaxSendItem->JobEntry->FSPIJobStatus.dwExtendedStatus = FSPI_ES_FATAL_ERROR;
  1810. }
  1811. }
  1812. if (!bFakeJobStatus)
  1813. {
  1814. //
  1815. // Note: The FSPI_JOB_STATUS that is returned by GetDevStatus() is
  1816. // to be freed as one block.
  1817. //
  1818. MemFree(lpFSPStatus);
  1819. lpFSPStatus = NULL;
  1820. }
  1821. else
  1822. {
  1823. //
  1824. // This is a faked job status - pointing to a structure on the stack.
  1825. //
  1826. if (pOrigFaxStatus)
  1827. {
  1828. //
  1829. // The FSP reported some status but we faked it.
  1830. // This is a good time to also free it
  1831. //
  1832. MemFree (pOrigFaxStatus);
  1833. pOrigFaxStatus = NULL;
  1834. }
  1835. }
  1836. //
  1837. // Block FaxStatusThread from changing this status
  1838. //
  1839. FaxSendItem->JobEntry->fStopUpdateStatus = TRUE;
  1840. LeaveCriticalSection( &g_CsJob );
  1841. if (!Rslt)
  1842. {
  1843. if (!HandleFailedSendJob(FaxSendItem->JobEntry))
  1844. {
  1845. DebugPrintEx(
  1846. DEBUG_ERR,
  1847. TEXT("[JobId: %ld] HandleFailedSendJob() failed (ec: %ld)."),
  1848. FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
  1849. GetLastError());
  1850. }
  1851. }
  1852. else
  1853. {
  1854. //
  1855. // cache the job id since we need id to create the FEI_COMPLETED event
  1856. // and when it is generated the job may alrady be gone
  1857. //
  1858. dwJobId = FaxSendItem->JobEntry->lpJobQueueEntry->JobId;
  1859. if (!HandleCompletedSendJob(FaxSendItem->JobEntry))
  1860. {
  1861. DebugPrintEx(
  1862. DEBUG_ERR,
  1863. TEXT("[JobId: %ld] HandleCompletedSendJob() failed (ec: %ld)."),
  1864. FaxSendItem->JobEntry->lpJobQueueEntry->JobId,
  1865. GetLastError());
  1866. }
  1867. //
  1868. // The send job is completed. For W2K backward compatibility we should notify
  1869. // FEI_DELETED since the job was allways removed when completed.
  1870. //
  1871. if (!CreateFaxEvent(0, FEI_DELETED, dwJobId))
  1872. {
  1873. DebugPrintEx(
  1874. DEBUG_ERR,
  1875. TEXT("CreateFaxEvent() failed. Event: 0x%08X JobId: %ld DeviceId: (ec: %ld)"),
  1876. FEI_DELETED,
  1877. dwJobId,
  1878. 0,
  1879. GetLastError());
  1880. }
  1881. }
  1882. Exit:
  1883. MemFree( FaxSendItem->FileName );
  1884. MemFree( FaxSendItem->PhoneNumber );
  1885. MemFree( FaxSendItem->Tsid );
  1886. MemFree( FaxSendItem->RecipientName );
  1887. MemFree( FaxSendItem->SenderName );
  1888. MemFree( FaxSendItem->SenderDept );
  1889. MemFree( FaxSendItem->SenderCompany );
  1890. MemFree( FaxSendItem->BillingCode );
  1891. MemFree( FaxSendItem->DocumentName );
  1892. MemFree( FaxSendItem );
  1893. //
  1894. // Let the system go back to sleep. Set the system idle timer.
  1895. //
  1896. if (TRUE == fSetSystemIdleTimer)
  1897. {
  1898. if (NULL == SetThreadExecutionState(ES_CONTINUOUS))
  1899. {
  1900. DebugPrintEx(DEBUG_ERR,
  1901. TEXT("SetThreadExecutionState() failed"));
  1902. }
  1903. }
  1904. if (!DecreaseServiceThreadsCount())
  1905. {
  1906. DebugPrintEx(
  1907. DEBUG_ERR,
  1908. TEXT("DecreaseServiceThreadsCount() failed (ec: %ld)"),
  1909. GetLastError());
  1910. }
  1911. return 0;
  1912. }
  1913. //*********************************************************************************
  1914. //* Name: IsSendJobReadyForDeleting()
  1915. //* Author: Ronen Barenboim
  1916. //* Date: April 19, 1999
  1917. //*********************************************************************************
  1918. //* DESCRIPTION:
  1919. //* Determines if an outgoing job is ready for deleting.
  1920. //* A job is ready for deleting when all of the recipients
  1921. //* are in the canceled state or or in the completed state.
  1922. //* PARAMETERS:
  1923. //* [IN] PJOB_QUEUE lpRecipientJob
  1924. //*
  1925. //* RETURN VALUE:
  1926. //* TRUE
  1927. //* If the job is ready for deleting.
  1928. //* FALSE
  1929. //* If the job is not ready for deleting.
  1930. //*********************************************************************************
  1931. BOOL IsSendJobReadyForDeleting(PJOB_QUEUE lpRecipientJob)
  1932. {
  1933. DEBUG_FUNCTION_NAME(TEXT("IsSendJobReadyForDeleting"));
  1934. Assert (lpRecipientJob);
  1935. Assert (lpRecipientJob->JobType == JT_SEND);
  1936. PJOB_QUEUE lpParentJob = lpRecipientJob->lpParentJob;
  1937. Assert(lpParentJob); // must have a parent job
  1938. Assert(lpParentJob->dwRecipientJobsCount>0);
  1939. Assert(lpParentJob->dwCompletedRecipientJobsCount +
  1940. lpParentJob->dwCanceledRecipientJobsCount +
  1941. lpParentJob->dwFailedRecipientJobsCount
  1942. <= lpParentJob->dwRecipientJobsCount);
  1943. DebugPrintEx(
  1944. DEBUG_MSG,
  1945. TEXT("[JobId: %ld] [Total Rec = %ld] [Canceled Rec = %ld] [Completed Rec = %ld] [Failed Rec = %ld] [RefCount = %ld]"),
  1946. lpParentJob->JobId,
  1947. lpParentJob->dwRecipientJobsCount,
  1948. lpParentJob->dwCanceledRecipientJobsCount,
  1949. lpParentJob->dwCompletedRecipientJobsCount,
  1950. lpParentJob->dwFailedRecipientJobsCount,
  1951. lpParentJob->RefCount);
  1952. if ( (lpParentJob->dwCompletedRecipientJobsCount +
  1953. lpParentJob->dwCanceledRecipientJobsCount +
  1954. lpParentJob->dwFailedRecipientJobsCount) == lpParentJob->dwRecipientJobsCount )
  1955. {
  1956. return TRUE;
  1957. }
  1958. return FALSE;
  1959. }
  1960. BOOL FreeJobEntry(PJOB_ENTRY lpJobEntry , BOOL bDestroy)
  1961. {
  1962. DEBUG_FUNCTION_NAME(TEXT("FreeJobEntry"));
  1963. Assert(lpJobEntry);
  1964. DWORD ec = ERROR_SUCCESS;
  1965. DWORD dwJobID = lpJobEntry->lpJobQueueEntry ? lpJobEntry->lpJobQueueEntry->JobId : 0xffffffff; // 0xffffffff for invalid job ID
  1966. EnterCriticalSection(&g_CsJob);
  1967. //
  1968. // Since CreateJobEntry() called OpenTapiLine() for TAPI lines
  1969. // we need to close it here.
  1970. // Note that the line might alrady be released since ReleaseJob()
  1971. // releases the line but does not free the job entry.
  1972. //
  1973. if (!lpJobEntry->Released)
  1974. {
  1975. if (lpJobEntry->LineInfo->State != FPS_NOT_FAX_CALL) {
  1976. DebugPrintEx( DEBUG_MSG,
  1977. TEXT("[Job Id: %ld] Before Releasing tapi line hCall=0x%08X hLine=0x%08X"),
  1978. dwJobID,
  1979. lpJobEntry->CallHandle,
  1980. lpJobEntry->LineInfo->hLine
  1981. );
  1982. ReleaseTapiLine( lpJobEntry->LineInfo, lpJobEntry->CallHandle );
  1983. lpJobEntry->CallHandle = 0;
  1984. lpJobEntry->Released = TRUE;
  1985. }
  1986. }
  1987. //
  1988. // Remove the job from the running job list
  1989. //
  1990. RemoveEntryList( &lpJobEntry->ListEntry );
  1991. //
  1992. // Cut the link between the line and the job
  1993. //
  1994. EnterCriticalSection( &g_CsLine );
  1995. lpJobEntry->LineInfo->JobEntry = NULL;
  1996. LeaveCriticalSection( &g_CsLine );
  1997. if (!FreeFSPIJobStatus(&lpJobEntry->FSPIJobStatus, FALSE))
  1998. {
  1999. DebugPrintEx(
  2000. DEBUG_ERR,
  2001. TEXT("[Job Id: %ld] FreeFSPIJobStatus() failed (ec: %ld)"),
  2002. dwJobID,
  2003. GetLastError);
  2004. }
  2005. MemFree(lpJobEntry->lpwstrJobTsid);
  2006. lpJobEntry->lpwstrJobTsid = NULL;
  2007. if (bDestroy)
  2008. {
  2009. MemFree(lpJobEntry);
  2010. }
  2011. LeaveCriticalSection(&g_CsJob);
  2012. return TRUE;
  2013. }
  2014. BOOL
  2015. EndJob(
  2016. IN PJOB_ENTRY JobEntry
  2017. )
  2018. /*++
  2019. Routine Description:
  2020. This fuction calls the device provider's EndJob function.
  2021. Arguments:
  2022. None.
  2023. Return Value:
  2024. Error code.
  2025. --*/
  2026. {
  2027. BOOL rVal = TRUE;
  2028. PJOB_INFO_1 JobInfo = NULL;
  2029. DEBUG_FUNCTION_NAME(TEXT("End Job"));
  2030. Assert(JobEntry);
  2031. DWORD dwJobID = JobEntry->lpJobQueueEntry ? JobEntry->lpJobQueueEntry->JobId : 0xffffffff; // 0xffffffff for invalid job ID
  2032. EnterCriticalSection( &g_CsJob );
  2033. if (!FindJobByJob( JobEntry ))
  2034. {
  2035. //
  2036. // if we get here then it means we hit a race
  2037. // condition where the FaxSendThread called EndJob
  2038. // at the same time that a client app did.
  2039. //
  2040. DebugPrintEx(DEBUG_WRN,TEXT("EndJob() could not find the Job"), dwJobID);
  2041. LeaveCriticalSection( &g_CsJob );
  2042. return ERROR_SUCCESS;
  2043. }
  2044. if (JobEntry->bFSPJobInProgress)
  2045. {
  2046. //
  2047. // If FaxDevEndJob was not yet called for the job then do it now.
  2048. // ( The case in which the line is already released occcurs in a
  2049. // receive job where we first ReleaseJob() to release the line but
  2050. // continue to do the inbound routing and only then call EndJob()).
  2051. //
  2052. __try
  2053. {
  2054. DebugPrintEx( DEBUG_MSG,
  2055. TEXT("[Job Id: %ld] Legacy FSP job is in progress. Calling FaxDevEndJob()"),
  2056. dwJobID);
  2057. rVal = JobEntry->LineInfo->Provider->FaxDevEndJob(
  2058. (HANDLE) JobEntry->InstanceData
  2059. );
  2060. if (!rVal)
  2061. {
  2062. DebugPrintEx(
  2063. DEBUG_ERR,
  2064. TEXT("[Job Id: %ld] FaxDevEndJob() failed"),
  2065. dwJobID);
  2066. }
  2067. else
  2068. {
  2069. DebugPrintEx( DEBUG_MSG,
  2070. TEXT("[Job Id: %ld] FaxDevEndJob() succeeded."),
  2071. dwJobID);
  2072. JobEntry->bFSPJobInProgress = FALSE;
  2073. }
  2074. }
  2075. __except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, JobEntry->LineInfo->Provider->FriendlyName, GetExceptionCode()))
  2076. {
  2077. ASSERT_FALSE;
  2078. }
  2079. }
  2080. else
  2081. {
  2082. DebugPrintEx(
  2083. DEBUG_MSG,
  2084. TEXT("[Job Id: %ld] FaxDevEndJob() NOT CALLED since legacy FSP job is not in progress."),
  2085. dwJobID);
  2086. }
  2087. if (FreeJobEntry(JobEntry, TRUE))
  2088. {
  2089. JobEntry = NULL;
  2090. }
  2091. else
  2092. {
  2093. DebugPrintEx(
  2094. DEBUG_ERR,
  2095. TEXT("Failed to free a job entry (%x)."),
  2096. JobEntry);
  2097. ASSERT_FALSE;
  2098. }
  2099. //
  2100. // There could have been a request to change the port status while we were handling this job.
  2101. // We allow the caller to modify a few of these requests to succeed, like the ring count for instance.
  2102. // While we still have the job critical section, let's make sure that we commit any requested changes to the
  2103. // registry. This should be a fairly quick operation.
  2104. //
  2105. LeaveCriticalSection( &g_CsJob );
  2106. return rVal;
  2107. }
  2108. //*********************************************************************************
  2109. //* Name: ReleaseJob()
  2110. //* Author: Ronen Barenboim
  2111. //* Date: April 18, 1999
  2112. //*********************************************************************************
  2113. //* DESCRIPTION:
  2114. //* Calls the FSP to end the specified job (FaxDevEndJob()).
  2115. //* Releases the line that was assigned to the job.
  2116. //* NOTE: The job itself is NOT DELETED and is NOT remvoed from the running
  2117. //* job list !!!
  2118. //*
  2119. //* PARAMETERS:
  2120. //* [IN/OUT] PJOB_ENTRY JobEntry
  2121. //* A pointer to the JOB_ENTRY to be ended.
  2122. //* RETURN VALUE:
  2123. //* REMARKS:
  2124. //* If the function is successful then:
  2125. //* JobEntry->Released = TRUE
  2126. //* JobEntry->hLine = 0
  2127. //* JobEntry->CallHandle = 0
  2128. //*********************************************************************************
  2129. BOOL
  2130. ReleaseJob(
  2131. IN PJOB_ENTRY JobEntry
  2132. )
  2133. {
  2134. BOOL rVal = TRUE;
  2135. DEBUG_FUNCTION_NAME(TEXT("ReleaseJob"));
  2136. Assert(JobEntry);
  2137. Assert(JobEntry->lpJobQueueEntry);
  2138. if (!FindJobByJob( JobEntry )) {
  2139. DebugPrintEx(
  2140. DEBUG_WRN,
  2141. TEXT("[JobId: %ld] was not found in the running job list."),
  2142. JobEntry->lpJobQueueEntry->JobId);
  2143. return TRUE;
  2144. }
  2145. EnterCriticalSection( &g_CsJob );
  2146. Assert(JobEntry->LineInfo);
  2147. Assert(JobEntry->LineInfo->Provider);
  2148. Assert(JobEntry->bFSPJobInProgress);
  2149. __try
  2150. {
  2151. rVal = JobEntry->LineInfo->Provider->FaxDevEndJob(
  2152. (HANDLE) JobEntry->InstanceData
  2153. );
  2154. if (!rVal)
  2155. {
  2156. DebugPrintEx(
  2157. DEBUG_ERR,
  2158. TEXT("[JobId: %ld] FaxDevEndJob() failed (ec: 0x%0X)"),
  2159. JobEntry->lpJobQueueEntry->JobId,
  2160. GetLastError());
  2161. }
  2162. else
  2163. {
  2164. DebugPrintEx(
  2165. DEBUG_MSG,
  2166. TEXT("[Job Id: %ld] FaxDevEndJob() succeeded."),
  2167. JobEntry->lpJobQueueEntry->JobId);
  2168. JobEntry->bFSPJobInProgress = FALSE;
  2169. }
  2170. }
  2171. __except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, JobEntry->LineInfo->Provider->FriendlyName, GetExceptionCode()))
  2172. {
  2173. ASSERT_FALSE;
  2174. }
  2175. if (JobEntry->LineInfo->State != FPS_NOT_FAX_CALL)
  2176. {
  2177. if( !ReleaseTapiLine( JobEntry->LineInfo, JobEntry->CallHandle ))
  2178. {
  2179. DebugPrintEx(
  2180. DEBUG_ERR,
  2181. TEXT("ReleaseTapiLine() failed "));
  2182. }
  2183. JobEntry->CallHandle = 0;
  2184. }
  2185. else
  2186. {
  2187. //
  2188. // FSP_NOT_FAX_CALL indicates a received call that was handed off to RAS.
  2189. // In this case we do not want to mark the line as released since it is in
  2190. // use by RAS. We will use TAPI evens that indicate the line was released to update
  2191. // the line info.
  2192. //
  2193. DebugPrintEx(
  2194. DEBUG_MSG,
  2195. TEXT("[JobId: %ld] A call is being handed off to RAS. Line 0x%08X not marked as released."),
  2196. JobEntry->lpJobQueueEntry->JobId,
  2197. JobEntry->LineInfo->hLine);
  2198. }
  2199. JobEntry->Released = TRUE;
  2200. //
  2201. // Cut the link between the line and the job
  2202. //
  2203. EnterCriticalSection( &g_CsLine );
  2204. JobEntry->LineInfo->JobEntry = NULL;
  2205. LeaveCriticalSection( &g_CsLine );
  2206. LeaveCriticalSection( &g_CsJob );
  2207. return rVal;
  2208. }
  2209. //*********************************************************************************
  2210. //* Name: SendDocument()
  2211. //* Author: Ronen Barenboim
  2212. //* Date: March 21, 1999
  2213. //*********************************************************************************
  2214. //* DESCRIPTION:
  2215. //*
  2216. //* PARAMETERS:
  2217. //* lpJobEntry
  2218. //* A pointer to a JOB_ENTRY structure that was created using StartJob().
  2219. //* FileName
  2220. //* The path to the TIFF containing the TIFF to send
  2221. //*
  2222. //* RETURN VALUE:
  2223. //*
  2224. //*********************************************************************************
  2225. DWORD
  2226. SendDocument(
  2227. PJOB_ENTRY lpJobEntry,
  2228. LPTSTR FileName
  2229. )
  2230. {
  2231. PFAX_SEND_ITEM FaxSendItem;
  2232. DWORD ThreadId;
  2233. HANDLE hThread;
  2234. PJOB_QUEUE lpJobQueue;
  2235. DWORD nRes;
  2236. DWORD ec = ERROR_SUCCESS;
  2237. BOOL bUseDeviceTsid;
  2238. WCHAR wcZero = L'\0';
  2239. STRING_PAIR pairs[8];
  2240. DEBUG_FUNCTION_NAME(TEXT("SendDocument"));
  2241. Assert(lpJobEntry);
  2242. lpJobQueue=lpJobEntry->lpJobQueueEntry;
  2243. Assert(lpJobQueue &&
  2244. JS_INPROGRESS == lpJobQueue->JobStatus);
  2245. FaxSendItem = (PFAX_SEND_ITEM) MemAlloc(sizeof(FAX_SEND_ITEM));
  2246. if (!FaxSendItem)
  2247. {
  2248. ec = ERROR_NOT_ENOUGH_MEMORY;
  2249. goto Error;
  2250. }
  2251. //
  2252. // Pack all the thread parameters into a FAX_SEND_ITEM structure.
  2253. //
  2254. pairs[0].lptstrSrc = lpJobEntry->DialablePhoneNumber; // Use the job entry phone number since it is alrady translated
  2255. pairs[0].lpptstrDst = &FaxSendItem->PhoneNumber;
  2256. pairs[1].lptstrSrc = lpJobQueue->RecipientProfile.lptstrName;
  2257. pairs[1].lpptstrDst = &FaxSendItem->RecipientName;
  2258. pairs[2].lptstrSrc = lpJobQueue->SenderProfile.lptstrName;
  2259. pairs[2].lpptstrDst = &FaxSendItem->SenderName;
  2260. pairs[3].lptstrSrc = lpJobQueue->SenderProfile.lptstrDepartment;
  2261. pairs[3].lpptstrDst = &FaxSendItem->SenderDept;
  2262. pairs[4].lptstrSrc = lpJobQueue->SenderProfile.lptstrCompany;
  2263. pairs[4].lpptstrDst = &FaxSendItem->SenderCompany;
  2264. pairs[5].lptstrSrc = lpJobQueue->SenderProfile.lptstrBillingCode;
  2265. pairs[5].lpptstrDst = &FaxSendItem->BillingCode;
  2266. pairs[6].lptstrSrc = lpJobQueue->JobParamsEx.lptstrDocumentName;
  2267. pairs[6].lpptstrDst = &FaxSendItem->DocumentName;
  2268. pairs[7].lptstrSrc = NULL;
  2269. pairs[7].lpptstrDst = &FaxSendItem->Tsid;
  2270. FaxSendItem->JobEntry = lpJobEntry;
  2271. FaxSendItem->FileName = NULL; // Set by FaxSendThread
  2272. EnterCriticalSection (&g_CsConfig);
  2273. bUseDeviceTsid = g_fFaxUseDeviceTsid;
  2274. LeaveCriticalSection (&g_CsConfig);
  2275. if (!bUseDeviceTsid)
  2276. {
  2277. // Check Sender Tsid
  2278. if ( lpJobQueue->SenderProfile.lptstrTSID &&
  2279. (lpJobQueue->SenderProfile.lptstrTSID[0] != wcZero))
  2280. {
  2281. pairs[7].lptstrSrc = lpJobQueue->SenderProfile.lptstrTSID;
  2282. }
  2283. else
  2284. {
  2285. // Use Fax number
  2286. if ( lpJobQueue->SenderProfile.lptstrFaxNumber &&
  2287. (lpJobQueue->SenderProfile.lptstrFaxNumber[0] != wcZero))
  2288. {
  2289. pairs[7].lptstrSrc = lpJobQueue->SenderProfile.lptstrFaxNumber;
  2290. }
  2291. }
  2292. }
  2293. else
  2294. {
  2295. // Use device Tsid
  2296. pairs[7].lptstrSrc = lpJobEntry->LineInfo->Tsid;
  2297. }
  2298. nRes=MultiStringDup(pairs, sizeof(pairs)/sizeof(STRING_PAIR));
  2299. if (nRes!=0) {
  2300. ec=GetLastError();
  2301. // MultiStringDup takes care of freeing the memory for the pairs for which the copy succeeded
  2302. DebugPrintEx(
  2303. DEBUG_ERR,
  2304. TEXT("MultiStringDup failed to copy string with index %d. (ec: %ld)"),
  2305. nRes-1,
  2306. ec);
  2307. goto Error;
  2308. }
  2309. EnterCriticalSection (&g_CsJob);
  2310. lpJobEntry->lpwstrJobTsid = StringDup (FaxSendItem->Tsid);
  2311. LeaveCriticalSection (&g_CsJob);
  2312. if (NULL != FaxSendItem->Tsid && NULL == lpJobEntry->lpwstrJobTsid)
  2313. {
  2314. ec = GetLastError();
  2315. DebugPrintEx(DEBUG_ERR,TEXT("StringDup failed (ec: 0x%0X)"),ec);
  2316. goto Error;
  2317. }
  2318. hThread = CreateThreadAndRefCount(
  2319. NULL,
  2320. 0,
  2321. (LPTHREAD_START_ROUTINE) FaxSendThread,
  2322. (LPVOID) FaxSendItem,
  2323. 0,
  2324. &ThreadId
  2325. );
  2326. if (!hThread)
  2327. {
  2328. ec=GetLastError();
  2329. DebugPrintEx(DEBUG_ERR,TEXT("CreateThreadAndRefCount for FaxSendThread failed (ec: 0x%0X)"),ec);
  2330. goto Error;
  2331. }
  2332. else
  2333. {
  2334. DebugPrintEx(DEBUG_MSG,TEXT("FaxSendThread thread created for job id %d (thread id: 0x%0x)"),lpJobQueue->JobId,ThreadId);
  2335. }
  2336. CloseHandle( hThread );
  2337. Assert (ERROR_SUCCESS == ec);
  2338. goto Exit;
  2339. Error:
  2340. Assert (ERROR_SUCCESS != ec);
  2341. if ( FaxSendItem )
  2342. {
  2343. MemFree( FaxSendItem->FileName );
  2344. MemFree( FaxSendItem->PhoneNumber );
  2345. MemFree( FaxSendItem->Tsid );
  2346. MemFree( FaxSendItem->RecipientName );
  2347. MemFree( FaxSendItem->SenderName );
  2348. MemFree( FaxSendItem->SenderDept );
  2349. MemFree( FaxSendItem->SenderCompany );
  2350. MemFree( FaxSendItem->BillingCode );
  2351. MemFree( FaxSendItem->DocumentName );
  2352. MemFree( FaxSendItem );
  2353. }
  2354. if (0 == lpJobQueue->dwLastJobExtendedStatus)
  2355. {
  2356. //
  2357. // Job was never really executed - this is a fatal error
  2358. //
  2359. lpJobQueue->dwLastJobExtendedStatus = FSPI_ES_FATAL_ERROR;
  2360. lpJobQueue->ExStatusString[0] = L'\0';
  2361. }
  2362. if (!MarkJobAsExpired(lpJobQueue))
  2363. {
  2364. DEBUG_ERR,
  2365. TEXT("[JobId: %ld] MarkJobAsExpired failed (ec: %ld)"),
  2366. lpJobQueue->JobId,
  2367. GetLastError();
  2368. }
  2369. EndJob(lpJobEntry);
  2370. lpJobQueue->JobEntry = NULL;
  2371. Exit:
  2372. return ec;
  2373. }
  2374. DWORD
  2375. FaxStatusThread(
  2376. LPVOID UnUsed
  2377. )
  2378. /*++
  2379. Routine Description:
  2380. This fuction runs asychronously as a separate thread to
  2381. query the status of all outstanding fax jobs. The status
  2382. is updated in the JOB_ENTRY structure and the print job
  2383. is updated with a explanitory string.
  2384. Arguments:
  2385. UnUsed - UnUsed pointer
  2386. Return Value:
  2387. Always zero.
  2388. --*/
  2389. {
  2390. PJOB_ENTRY JobEntry;
  2391. PFAX_DEV_STATUS FaxStatus;
  2392. BOOL Rval;
  2393. DWORD Bytes;
  2394. ULONG_PTR CompletionKey;
  2395. DWORD dwEventId;
  2396. DEBUG_FUNCTION_NAME(TEXT("FaxStatusThread"));
  2397. while( TRUE )
  2398. {
  2399. Rval = GetQueuedCompletionStatus(
  2400. g_StatusCompletionPortHandle,
  2401. &Bytes,
  2402. &CompletionKey,
  2403. (LPOVERLAPPED*) &FaxStatus,
  2404. INFINITE
  2405. );
  2406. if (!Rval)
  2407. {
  2408. DebugPrintEx(DEBUG_ERR,TEXT("GetQueuedCompletionStatus() failed, ec=0x%08x"), GetLastError() );
  2409. continue;
  2410. }
  2411. if (SERVICE_SHUT_DOWN_KEY == CompletionKey)
  2412. {
  2413. //
  2414. // Service is shutting down
  2415. //
  2416. DebugPrintEx(
  2417. DEBUG_MSG,
  2418. TEXT("Service is shutting down"));
  2419. //
  2420. // Notify all FaxStatusThreads to terminate
  2421. //
  2422. if (!PostQueuedCompletionStatus( g_StatusCompletionPortHandle,
  2423. 0,
  2424. SERVICE_SHUT_DOWN_KEY,
  2425. (LPOVERLAPPED) NULL))
  2426. {
  2427. DebugPrintEx(
  2428. DEBUG_ERR,
  2429. TEXT("PostQueuedCompletionStatus failed (SERVICE_SHUT_DOWN_KEY - FaxStatusThread). (ec: %ld)"),
  2430. GetLastError());
  2431. }
  2432. break;
  2433. }
  2434. //
  2435. // (else we're dealing with a status update from an FSP)
  2436. //
  2437. BOOL fBadComletionKey = TRUE;
  2438. PLINE_INFO pLineInfo = (PLINE_INFO)CompletionKey;
  2439. fBadComletionKey = pLineInfo->Signature != LINE_SIGNATURE;
  2440. if (fBadComletionKey)
  2441. {
  2442. DebugPrintEx(DEBUG_WRN,
  2443. TEXT("Bad completion key: 0x%08x"),
  2444. CompletionKey);
  2445. continue;
  2446. }
  2447. BOOL fBadFaxStatus = TRUE;
  2448. fBadFaxStatus = FaxStatus->SizeOfStruct != sizeof(FAX_DEV_STATUS);
  2449. if (fBadFaxStatus)
  2450. {
  2451. DebugPrintEx(DEBUG_WRN,
  2452. TEXT("Bad FAX_DEV_STATUS: 0x%08x"),
  2453. FaxStatus);
  2454. continue;
  2455. }
  2456. EnterCriticalSection( &g_CsJob );
  2457. JobEntry = pLineInfo->JobEntry;
  2458. if (!JobEntry)
  2459. {
  2460. //
  2461. // The FSP reported a status on a LineInfo for which the running
  2462. // job no longer exists.
  2463. //
  2464. //
  2465. // Free the completion packet memory
  2466. //
  2467. DebugPrintEx(
  2468. DEBUG_WRN,
  2469. TEXT("Provider [%s] reported a status packet that was processed after the job entry was already released.\n")
  2470. TEXT("StatusId : 0x%08x\n")
  2471. TEXT("Line: %s\n")
  2472. TEXT("Packet address: %p\n")
  2473. TEXT("Heap: %p"),
  2474. pLineInfo->Provider->ProviderName,
  2475. FaxStatus->StatusId,
  2476. pLineInfo->DeviceName,
  2477. FaxStatus,
  2478. pLineInfo->Provider->HeapHandle);
  2479. if (!HeapFree(pLineInfo->Provider->HeapHandle, 0, FaxStatus ))
  2480. {
  2481. DebugPrintEx(
  2482. DEBUG_ERR,
  2483. TEXT("Failed to free orphan device status (ec: %ld)"),
  2484. GetLastError());
  2485. //
  2486. // Nothing else we can do but report it in debug mode
  2487. //
  2488. }
  2489. FaxStatus = NULL;
  2490. LeaveCriticalSection( &g_CsJob );
  2491. continue;
  2492. }
  2493. {
  2494. DWORD dwJobStatus;
  2495. DWORD dwExtendedStatus;
  2496. BOOL bPrivateStatusCode;
  2497. /*
  2498. *****
  2499. NTRAID#EdgeBugs-12680-2001/05/14-t-nicali
  2500. What if in the meantime another job is executing on the
  2501. same line. In this case ->JobEntry will point to ANOTHER job !!!
  2502. The solution should be to provide as a completion key the
  2503. JobEntry and not the LineInfo !!!
  2504. *****
  2505. */
  2506. Assert (JobEntry->lpJobQueueEntry);
  2507. if (TRUE == JobEntry->fStopUpdateStatus)
  2508. {
  2509. DebugPrintEx(
  2510. DEBUG_WRN,
  2511. TEXT("JobId: %ld. fStopUpdateStatus was set. Not updating status %ld"),
  2512. JobEntry->lpJobQueueEntry->JobId,
  2513. JobEntry->lpJobQueueEntry->JobStatus);
  2514. if (!HeapFree(pLineInfo->Provider->HeapHandle, 0, FaxStatus ))
  2515. {
  2516. DebugPrintEx(
  2517. DEBUG_ERR,
  2518. TEXT("Failed to free orphan device status (ec: %ld)"),
  2519. GetLastError());
  2520. //
  2521. // Nothing else we can do but report it in debug mode
  2522. //
  2523. }
  2524. FaxStatus = NULL;
  2525. LeaveCriticalSection (&g_CsJob);
  2526. continue;
  2527. }
  2528. //
  2529. // Do not update final job states
  2530. //
  2531. LegacyJobStatusToStatus(
  2532. FaxStatus->StatusId,
  2533. &dwJobStatus,
  2534. &dwExtendedStatus,
  2535. &bPrivateStatusCode);
  2536. if (FSPI_JS_ABORTED == dwJobStatus ||
  2537. FSPI_JS_COMPLETED == dwJobStatus ||
  2538. FSPI_JS_FAILED == dwJobStatus ||
  2539. FSPI_JS_FAILED_NO_RETRY == dwJobStatus ||
  2540. FSPI_JS_DELETED == dwJobStatus )
  2541. {
  2542. //
  2543. // This is a final status update. Final status is updated from FaxSendThread or FaxReceiveThread
  2544. //
  2545. DebugPrintEx(
  2546. DEBUG_WRN,
  2547. TEXT("JobId: %ld. Final status code. Not updating status %ld"),
  2548. JobEntry->lpJobQueueEntry->JobId,
  2549. dwJobStatus);
  2550. if (!HeapFree(pLineInfo->Provider->HeapHandle, 0, FaxStatus ))
  2551. {
  2552. DebugPrintEx(
  2553. DEBUG_ERR,
  2554. TEXT("Failed to free orphan device status (ec: %ld)"),
  2555. GetLastError());
  2556. //
  2557. // Nothing else we can do but report it in debug mode
  2558. //
  2559. }
  2560. FaxStatus = NULL;
  2561. LeaveCriticalSection (&g_CsJob);
  2562. continue;
  2563. }
  2564. //
  2565. // Go ahead with updating the status
  2566. //
  2567. FreeFSPIJobStatus(&JobEntry->FSPIJobStatus, FALSE);
  2568. memset(&JobEntry->FSPIJobStatus, 0, sizeof(FSPI_JOB_STATUS));
  2569. JobEntry->FSPIJobStatus.dwSizeOfStruct = sizeof(FSPI_JOB_STATUS);
  2570. //
  2571. // This is done for backward compatability with W2K Fax API.
  2572. // GetJobData() and FAX_GetDeviceStatus() will use this value to return
  2573. // the job status for legacy jobs.
  2574. //
  2575. JobEntry->LineInfo->State = FaxStatus->StatusId;
  2576. LegacyJobStatusToStatus(
  2577. FaxStatus->StatusId,
  2578. &JobEntry->FSPIJobStatus.dwJobStatus,
  2579. &JobEntry->FSPIJobStatus.dwExtendedStatus,
  2580. &bPrivateStatusCode);
  2581. if (bPrivateStatusCode)
  2582. {
  2583. JobEntry->FSPIJobStatus.fAvailableStatusInfo |= FSPI_JOB_STATUS_INFO_FSP_PRIVATE_STATUS_CODE;
  2584. }
  2585. JobEntry->FSPIJobStatus.dwExtendedStatusStringId = FaxStatus->StringId;
  2586. JobEntry->FSPIJobStatus.dwPageCount = FaxStatus->PageCount;
  2587. JobEntry->FSPIJobStatus.fAvailableStatusInfo |= FSPI_JOB_STATUS_INFO_PAGECOUNT;
  2588. if (FaxStatus->CSI)
  2589. {
  2590. JobEntry->FSPIJobStatus.lpwstrRemoteStationId = StringDup( FaxStatus->CSI );
  2591. if (!JobEntry->FSPIJobStatus.lpwstrRemoteStationId )
  2592. {
  2593. DebugPrintEx(
  2594. DEBUG_ERR,
  2595. TEXT("StringDup( FaxStatus->CSI ) failed (ec: %ld)"),
  2596. GetLastError());
  2597. }
  2598. }
  2599. if (FaxStatus->CallerId)
  2600. {
  2601. JobEntry->FSPIJobStatus.lpwstrCallerId = StringDup( FaxStatus->CallerId );
  2602. if (!JobEntry->FSPIJobStatus.lpwstrCallerId )
  2603. {
  2604. DebugPrintEx(
  2605. DEBUG_ERR,
  2606. TEXT("StringDup( FaxStatus.CallerId ) failed (ec: %ld)"),
  2607. GetLastError());
  2608. }
  2609. }
  2610. if (FaxStatus->RoutingInfo)
  2611. {
  2612. JobEntry->FSPIJobStatus.lpwstrRoutingInfo = StringDup( FaxStatus->RoutingInfo );
  2613. if (!JobEntry->FSPIJobStatus.lpwstrRoutingInfo )
  2614. {
  2615. DebugPrintEx(
  2616. DEBUG_ERR,
  2617. TEXT("StringDup( FaxStatus.RoutingInfo ) failed (ec: %ld)"),
  2618. GetLastError());
  2619. }
  2620. }
  2621. //
  2622. // Get extended status string
  2623. //
  2624. JobEntry->ExStatusString[0] = L'\0';
  2625. if (JobEntry->FSPIJobStatus.dwExtendedStatusStringId != 0)
  2626. {
  2627. DWORD dwSize;
  2628. HINSTANCE hLoadInstance;
  2629. Assert (JobEntry->FSPIJobStatus.dwExtendedStatus != 0);
  2630. if ( !_tcsicmp(JobEntry->LineInfo->Provider->szGUID,REGVAL_T30_PROVIDER_GUID_STRING) )
  2631. { // special case where the FSP is our FSP (fxst30.dll).
  2632. hLoadInstance = g_hResource;
  2633. }
  2634. else
  2635. {
  2636. hLoadInstance = JobEntry->LineInfo->Provider->hModule;
  2637. }
  2638. dwSize = LoadString (hLoadInstance,
  2639. JobEntry->FSPIJobStatus.dwExtendedStatusStringId,
  2640. JobEntry->ExStatusString,
  2641. ARR_SIZE(JobEntry->ExStatusString));
  2642. if (dwSize == 0)
  2643. {
  2644. DebugPrintEx(
  2645. DEBUG_ERR,
  2646. TEXT("Failed to load extended status string (ec: %ld) stringid : %ld, Provider: %s"),
  2647. GetLastError(),
  2648. JobEntry->FSPIJobStatus.dwExtendedStatusStringId,
  2649. JobEntry->LineInfo->Provider->ImageName);
  2650. JobEntry->FSPIJobStatus.fAvailableStatusInfo &= ~FSPI_JOB_STATUS_INFO_FSP_PRIVATE_STATUS_CODE;
  2651. JobEntry->FSPIJobStatus.dwExtendedStatusStringId = 0;
  2652. JobEntry->FSPIJobStatus.dwExtendedStatus = 0;
  2653. }
  2654. }
  2655. dwEventId = MapFSPIJobStatusToEventId(&JobEntry->FSPIJobStatus);
  2656. //
  2657. // Note: W2K Fax did issue notifications with EventId == 0 whenever an
  2658. // FSP reported proprietry status code. To keep backward compatability
  2659. // we keep up this behaviour although it might be regarded as a bug
  2660. //
  2661. if ( !CreateFaxEvent( JobEntry->LineInfo->PermanentLineID, dwEventId, JobEntry->lpJobQueueEntry->JobId ) )
  2662. {
  2663. DebugPrintEx(
  2664. DEBUG_ERR,
  2665. TEXT("CreateFaxEvent failed. (ec: %ld)"),
  2666. GetLastError());
  2667. }
  2668. EnterCriticalSection (&g_CsQueue);
  2669. DWORD dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS,
  2670. JobEntry->lpJobQueueEntry
  2671. );
  2672. if (ERROR_SUCCESS != dwRes)
  2673. {
  2674. DebugPrintEx(
  2675. DEBUG_ERR,
  2676. TEXT("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) failed for job id %ld (ec: %lc)"),
  2677. JobEntry->lpJobQueueEntry->UniqueId,
  2678. dwRes);
  2679. }
  2680. LeaveCriticalSection (&g_CsQueue);
  2681. HeapFree( JobEntry->LineInfo->Provider->HeapHandle, 0, FaxStatus );
  2682. }
  2683. LeaveCriticalSection( &g_CsJob );
  2684. }
  2685. if (!DecreaseServiceThreadsCount())
  2686. {
  2687. DebugPrintEx(
  2688. DEBUG_ERR,
  2689. TEXT("DecreaseServiceThreadsCount() failed (ec: %ld)"),
  2690. GetLastError());
  2691. }
  2692. return 0;
  2693. }
  2694. BOOL
  2695. InitializeJobManager(
  2696. PREG_FAX_SERVICE FaxReg
  2697. )
  2698. /*++
  2699. Routine Description:
  2700. This fuction initializes the thread pool and
  2701. FAX service queues.
  2702. Arguments:
  2703. ThreadHint - Number of threads to create in the initial pool.
  2704. Return Value:
  2705. Thread return value.
  2706. --*/
  2707. {
  2708. BOOL bRet;
  2709. DEBUG_FUNCTION_NAME(TEXT("InitializeJobManager"));
  2710. g_StatusCompletionPortHandle = CreateIoCompletionPort(
  2711. INVALID_HANDLE_VALUE,
  2712. NULL,
  2713. 0,
  2714. MAX_STATUS_THREADS
  2715. );
  2716. if (!g_StatusCompletionPortHandle)
  2717. {
  2718. DWORD ec = GetLastError();
  2719. FaxLog(
  2720. FAXLOG_CATEGORY_INIT,
  2721. FAXLOG_LEVEL_MIN,
  2722. 1,
  2723. MSG_SERVICE_INIT_FAILED_SYS_RESOURCE,
  2724. DWORD2DECIMAL(ec)
  2725. );
  2726. DebugPrintEx(DEBUG_ERR,TEXT("Failed to create StatusCompletionPort (ec: %ld)"), GetLastError() );
  2727. goto Error;
  2728. }
  2729. bRet = TRUE;
  2730. goto Exit;
  2731. Error:
  2732. bRet = FALSE;
  2733. Exit:
  2734. return bRet;
  2735. }
  2736. VOID
  2737. SetGlobalsFromRegistry(
  2738. PREG_FAX_SERVICE FaxReg
  2739. )
  2740. {
  2741. Assert(FaxReg);
  2742. DEBUG_FUNCTION_NAME(TEXT("SetGlobalsFromRegistry"));
  2743. EnterCriticalSection (&g_CsConfig);
  2744. g_dwFaxSendRetries = FaxReg->Retries;
  2745. g_dwFaxSendRetryDelay = (INT) FaxReg->RetryDelay;
  2746. g_dwFaxDirtyDays = FaxReg->DirtyDays;
  2747. g_dwNextJobId = FaxReg->NextJobNumber;
  2748. g_dwQueueState = FaxReg->dwQueueState;
  2749. g_fFaxUseDeviceTsid = FaxReg->UseDeviceTsid;
  2750. g_fFaxUseBranding = FaxReg->Branding;
  2751. g_fServerCp = FaxReg->ServerCp;
  2752. g_StartCheapTime = FaxReg->StartCheapTime;
  2753. g_StopCheapTime = FaxReg->StopCheapTime;
  2754. LeaveCriticalSection (&g_CsConfig);
  2755. return;
  2756. }
  2757. BOOL
  2758. FillMsTagInfo(
  2759. LPTSTR FaxFileName,
  2760. const JOB_QUEUE * lpcJobQueue
  2761. )
  2762. /*++
  2763. Routine Description:
  2764. Add Ms Tiff Tags to a sent fax. Wraps TiffAddMsTags...
  2765. Arguments:
  2766. FaxFileName - Name of the file to archive
  2767. SendTime - time the fax was sent
  2768. FaxStatus - job status
  2769. FaxSend - FAX_SEND structure for sent fax, includes CSID.
  2770. Return Value:
  2771. TRUE - The tags were added.
  2772. FALSE - The tags were not added.
  2773. --*/
  2774. {
  2775. BOOL success = FALSE;
  2776. MS_TAG_INFO MsTagInfo = {0};
  2777. WCHAR wcZero = L'\0';
  2778. PJOB_ENTRY lpJobEntry;
  2779. LPCFSPI_JOB_STATUS lpcFSPIJobStatus;
  2780. DEBUG_FUNCTION_NAME(TEXT("FillMsTagInfo"));
  2781. Assert (lpcJobQueue);
  2782. Assert (lpcJobQueue->lpParentJob);
  2783. lpJobEntry = lpcJobQueue->JobEntry;
  2784. Assert(lpJobEntry);
  2785. lpcFSPIJobStatus = &lpJobEntry->FSPIJobStatus;
  2786. if (lpcJobQueue->RecipientProfile.lptstrName && (lpcJobQueue->RecipientProfile.lptstrName[0] != wcZero) ) {
  2787. MsTagInfo.RecipName = lpcJobQueue->RecipientProfile.lptstrName;
  2788. }
  2789. if (lpcJobQueue->RecipientProfile.lptstrFaxNumber && (lpcJobQueue->RecipientProfile.lptstrFaxNumber[0] != wcZero) ) {
  2790. MsTagInfo.RecipNumber = lpcJobQueue->RecipientProfile.lptstrFaxNumber;
  2791. }
  2792. if (lpcJobQueue->SenderProfile.lptstrName && (lpcJobQueue->SenderProfile.lptstrName[0] != wcZero) ) {
  2793. MsTagInfo.SenderName = lpcJobQueue->SenderProfile.lptstrName;
  2794. }
  2795. if (lpcFSPIJobStatus->lpwstrRoutingInfo && (lpcFSPIJobStatus->lpwstrRoutingInfo[0] != wcZero) ) {
  2796. MsTagInfo.Routing = lpcFSPIJobStatus->lpwstrRoutingInfo;
  2797. }
  2798. if (lpcFSPIJobStatus->lpwstrRemoteStationId && (lpcFSPIJobStatus->lpwstrRemoteStationId[0] != wcZero) ) {
  2799. MsTagInfo.Csid = lpcFSPIJobStatus->lpwstrRemoteStationId;
  2800. }
  2801. if (lpJobEntry->lpwstrJobTsid && (lpJobEntry->lpwstrJobTsid[0] != wcZero) ) {
  2802. MsTagInfo.Tsid = lpJobEntry->lpwstrJobTsid;
  2803. }
  2804. if (!GetRealFaxTimeAsFileTime (lpJobEntry, FAX_TIME_TYPE_START, (FILETIME*)&MsTagInfo.StartTime))
  2805. {
  2806. MsTagInfo.StartTime = 0;
  2807. DebugPrintEx(DEBUG_ERR,TEXT("GetRealFaxTimeAsFileTime (Start time) Failed (ec: %ld)"), GetLastError() );
  2808. }
  2809. if (!GetRealFaxTimeAsFileTime (lpJobEntry, FAX_TIME_TYPE_END, (FILETIME*)&MsTagInfo.EndTime))
  2810. {
  2811. MsTagInfo.EndTime = 0;
  2812. DebugPrintEx(DEBUG_ERR,TEXT("GetRealFaxTimeAsFileTime (Eend time) Failed (ec: %ld)"), GetLastError() );
  2813. }
  2814. MsTagInfo.SubmissionTime = lpcJobQueue->lpParentJob->SubmissionTime;
  2815. MsTagInfo.OriginalScheduledTime = lpcJobQueue->lpParentJob->OriginalScheduleTime;
  2816. MsTagInfo.Type = JT_SEND;
  2817. if (lpJobEntry->LineInfo->DeviceName && (lpJobEntry->LineInfo->DeviceName[0] != wcZero) )
  2818. {
  2819. MsTagInfo.Port = lpJobEntry->LineInfo->DeviceName;
  2820. }
  2821. MsTagInfo.Pages = lpcJobQueue->PageCount;
  2822. MsTagInfo.Retries = lpcJobQueue->SendRetries;
  2823. if (lpcJobQueue->RecipientProfile.lptstrCompany && (lpcJobQueue->RecipientProfile.lptstrCompany[0] != wcZero) ) {
  2824. MsTagInfo.RecipCompany = lpcJobQueue->RecipientProfile.lptstrCompany;
  2825. }
  2826. if (lpcJobQueue->RecipientProfile.lptstrStreetAddress && (lpcJobQueue->RecipientProfile.lptstrStreetAddress[0] != wcZero) ) {
  2827. MsTagInfo.RecipStreet = lpcJobQueue->RecipientProfile.lptstrStreetAddress;
  2828. }
  2829. if (lpcJobQueue->RecipientProfile.lptstrCity && (lpcJobQueue->RecipientProfile.lptstrCity[0] != wcZero) ) {
  2830. MsTagInfo.RecipCity = lpcJobQueue->RecipientProfile.lptstrCity;
  2831. }
  2832. if (lpcJobQueue->RecipientProfile.lptstrState && (lpcJobQueue->RecipientProfile.lptstrState[0] != wcZero) ) {
  2833. MsTagInfo.RecipState = lpcJobQueue->RecipientProfile.lptstrState;
  2834. }
  2835. if (lpcJobQueue->RecipientProfile.lptstrZip && (lpcJobQueue->RecipientProfile.lptstrZip[0] != wcZero) ) {
  2836. MsTagInfo.RecipZip = lpcJobQueue->RecipientProfile.lptstrZip;
  2837. }
  2838. if (lpcJobQueue->RecipientProfile.lptstrCountry && (lpcJobQueue->RecipientProfile.lptstrCountry[0] != wcZero) ) {
  2839. MsTagInfo.RecipCountry = lpcJobQueue->RecipientProfile.lptstrCountry;
  2840. }
  2841. if (lpcJobQueue->RecipientProfile.lptstrTitle && (lpcJobQueue->RecipientProfile.lptstrTitle[0] != wcZero) ) {
  2842. MsTagInfo.RecipTitle = lpcJobQueue->RecipientProfile.lptstrTitle;
  2843. }
  2844. if (lpcJobQueue->RecipientProfile.lptstrDepartment && (lpcJobQueue->RecipientProfile.lptstrDepartment[0] != wcZero) ) {
  2845. MsTagInfo.RecipDepartment = lpcJobQueue->RecipientProfile.lptstrDepartment;
  2846. }
  2847. if (lpcJobQueue->RecipientProfile.lptstrOfficeLocation && (lpcJobQueue->RecipientProfile.lptstrOfficeLocation[0] != wcZero) ) {
  2848. MsTagInfo.RecipOfficeLocation = lpcJobQueue->RecipientProfile.lptstrOfficeLocation;
  2849. }
  2850. if (lpcJobQueue->RecipientProfile.lptstrHomePhone && (lpcJobQueue->RecipientProfile.lptstrHomePhone[0] != wcZero) ) {
  2851. MsTagInfo.RecipHomePhone = lpcJobQueue->RecipientProfile.lptstrHomePhone;
  2852. }
  2853. if (lpcJobQueue->RecipientProfile.lptstrOfficePhone && (lpcJobQueue->RecipientProfile.lptstrOfficePhone[0] != wcZero) ) {
  2854. MsTagInfo.RecipOfficePhone = lpcJobQueue->RecipientProfile.lptstrOfficePhone;
  2855. }
  2856. if (lpcJobQueue->RecipientProfile.lptstrEmail && (lpcJobQueue->RecipientProfile.lptstrEmail[0] != wcZero) ) {
  2857. MsTagInfo.RecipEMail = lpcJobQueue->RecipientProfile.lptstrEmail;
  2858. }
  2859. if (lpcJobQueue->SenderProfile.lptstrFaxNumber && (lpcJobQueue->SenderProfile.lptstrFaxNumber[0] != wcZero) ) {
  2860. MsTagInfo.SenderNumber = lpcJobQueue->SenderProfile.lptstrFaxNumber;
  2861. }
  2862. if (lpcJobQueue->SenderProfile.lptstrCompany && (lpcJobQueue->SenderProfile.lptstrCompany[0] != wcZero) ) {
  2863. MsTagInfo.SenderCompany = lpcJobQueue->SenderProfile.lptstrCompany;
  2864. }
  2865. if (lpcJobQueue->SenderProfile.lptstrStreetAddress && (lpcJobQueue->SenderProfile.lptstrStreetAddress[0] != wcZero) ) {
  2866. MsTagInfo.SenderStreet = lpcJobQueue->SenderProfile.lptstrStreetAddress;
  2867. }
  2868. if (lpcJobQueue->SenderProfile.lptstrCity && (lpcJobQueue->SenderProfile.lptstrCity[0] != wcZero) ) {
  2869. MsTagInfo.SenderCity = lpcJobQueue->SenderProfile.lptstrCity;
  2870. }
  2871. if (lpcJobQueue->SenderProfile.lptstrState && (lpcJobQueue->SenderProfile.lptstrState[0] != wcZero) ) {
  2872. MsTagInfo.SenderState = lpcJobQueue->SenderProfile.lptstrState;
  2873. }
  2874. if (lpcJobQueue->SenderProfile.lptstrZip && (lpcJobQueue->SenderProfile.lptstrZip[0] != wcZero) ) {
  2875. MsTagInfo.SenderZip = lpcJobQueue->SenderProfile.lptstrZip;
  2876. }
  2877. if (lpcJobQueue->SenderProfile.lptstrCountry && (lpcJobQueue->SenderProfile.lptstrCountry[0] != wcZero) ) {
  2878. MsTagInfo.SenderCountry = lpcJobQueue->SenderProfile.lptstrCountry;
  2879. }
  2880. if (lpcJobQueue->SenderProfile.lptstrTitle && (lpcJobQueue->SenderProfile.lptstrTitle[0] != wcZero) ) {
  2881. MsTagInfo.SenderTitle = lpcJobQueue->SenderProfile.lptstrTitle;
  2882. }
  2883. if (lpcJobQueue->SenderProfile.lptstrDepartment && (lpcJobQueue->SenderProfile.lptstrDepartment[0] != wcZero) ) {
  2884. MsTagInfo.SenderDepartment = lpcJobQueue->SenderProfile.lptstrDepartment;
  2885. }
  2886. if (lpcJobQueue->SenderProfile.lptstrOfficeLocation && (lpcJobQueue->SenderProfile.lptstrOfficeLocation[0] != wcZero) ) {
  2887. MsTagInfo.SenderOfficeLocation = lpcJobQueue->SenderProfile.lptstrOfficeLocation;
  2888. }
  2889. if (lpcJobQueue->SenderProfile.lptstrHomePhone && (lpcJobQueue->SenderProfile.lptstrHomePhone[0] != wcZero) ) {
  2890. MsTagInfo.SenderHomePhone = lpcJobQueue->SenderProfile.lptstrHomePhone;
  2891. }
  2892. if (lpcJobQueue->SenderProfile.lptstrOfficePhone && (lpcJobQueue->SenderProfile.lptstrOfficePhone[0] != wcZero) ) {
  2893. MsTagInfo.SenderOfficePhone = lpcJobQueue->SenderProfile.lptstrOfficePhone;
  2894. }
  2895. if (lpcJobQueue->SenderProfile.lptstrEmail && (lpcJobQueue->SenderProfile.lptstrEmail[0] != wcZero) ) {
  2896. MsTagInfo.SenderEMail = lpcJobQueue->SenderProfile.lptstrEmail;
  2897. }
  2898. if (lpcJobQueue->SenderProfile.lptstrBillingCode && (lpcJobQueue->SenderProfile.lptstrBillingCode[0] != wcZero) ) {
  2899. MsTagInfo.SenderBilling = lpcJobQueue->SenderProfile.lptstrBillingCode;
  2900. }
  2901. if (lpcJobQueue->JobParamsEx.lptstrDocumentName && (lpcJobQueue->JobParamsEx.lptstrDocumentName[0] != wcZero) ) {
  2902. MsTagInfo.Document = lpcJobQueue->JobParamsEx.lptstrDocumentName;
  2903. }
  2904. if (lpcJobQueue->lpParentJob->CoverPageEx.lptstrSubject && (lpcJobQueue->lpParentJob->CoverPageEx.lptstrSubject[0] != wcZero) ) {
  2905. MsTagInfo.Subject = lpcJobQueue->lpParentJob->CoverPageEx.lptstrSubject;
  2906. }
  2907. if (lpcJobQueue->lpParentJob->UserName && (lpcJobQueue->lpParentJob->UserName[0] != wcZero) ) {
  2908. MsTagInfo.SenderUserName = lpcJobQueue->lpParentJob->UserName;
  2909. }
  2910. if (lpcJobQueue->SenderProfile.lptstrTSID && (lpcJobQueue->SenderProfile.lptstrTSID[0] != wcZero) ) {
  2911. MsTagInfo.SenderTsid = lpcJobQueue->SenderProfile.lptstrTSID;
  2912. }
  2913. MsTagInfo.dwStatus = JS_COMPLETED; // We archive only succesfully sent faxes
  2914. MsTagInfo.dwExtendedStatus = lpcFSPIJobStatus->dwExtendedStatus;
  2915. if (lpJobEntry->ExStatusString[0] != wcZero) {
  2916. MsTagInfo.lptstrExtendedStatus = lpJobEntry->ExStatusString;
  2917. }
  2918. MsTagInfo.dwlBroadcastId = lpcJobQueue->lpParentJob->UniqueId;
  2919. MsTagInfo.Priority = lpcJobQueue->lpParentJob->JobParamsEx.Priority;
  2920. success = TiffAddMsTags( FaxFileName, &MsTagInfo, TRUE );
  2921. if (!success)
  2922. {
  2923. DebugPrintEx( DEBUG_ERR,
  2924. TEXT("TiffAddMsTags failed, ec = %ld"),
  2925. GetLastError ());
  2926. }
  2927. if(!AddNTFSStorageProperties( FaxFileName, &MsTagInfo , TRUE ))
  2928. {
  2929. if (ERROR_OPEN_FAILED != GetLastError ())
  2930. {
  2931. //
  2932. // If AddNTFSStorageProperties fails with ERROR_OPEN_FAIL then the archive
  2933. // folder is not on an NTFS 5 partition.
  2934. // This is ok - NTFS properties are a backup mechanism but not a must
  2935. //
  2936. DebugPrintEx( DEBUG_ERR,
  2937. TEXT("AddNTFSStorageProperties failed, ec = %ld"),
  2938. GetLastError ());
  2939. success = FALSE;
  2940. }
  2941. else
  2942. {
  2943. DebugPrintEx( DEBUG_WRN,
  2944. TEXT("AddNTFSStorageProperties failed with ERROR_OPEN_FAIL. Probably not an NTFS 5 partition"));
  2945. }
  2946. }
  2947. return success;
  2948. } // FillMsTagInfo
  2949. //*********************************************************************************
  2950. //* Name: ArchiveOutboundJob()
  2951. //* Author: Ronen Barenboim
  2952. //* Date: June 03, 1999
  2953. //*********************************************************************************
  2954. //* DESCRIPTION:
  2955. //* Archive a tiff file that has been sent by copying the file to an archive
  2956. //* directory. Also adds the MSTags to the new file generated at the
  2957. //* archive (not to the source file).
  2958. //*
  2959. //* PARAMETERS:
  2960. //* [IN ] const JOB_QUEUE * lpcJobQueue
  2961. //* Pointer to the recipient job which is to be archived.
  2962. //*
  2963. //* RETURN VALUE:
  2964. //* TRUE if the opeation succeeded.
  2965. //* FALSE if the operation failed.
  2966. //*********************************************************************************
  2967. BOOL
  2968. ArchiveOutboundJob(
  2969. const JOB_QUEUE * lpcJobQueue
  2970. )
  2971. {
  2972. BOOL rVal = FALSE;
  2973. WCHAR ArchiveFileName[MAX_PATH] = {0};
  2974. LPWSTR lpwszUserSid = NULL;
  2975. DWORD ec = ERROR_SUCCESS;
  2976. WCHAR wszArchiveFolder[MAX_PATH];
  2977. DEBUG_FUNCTION_NAME(TEXT("ArchiveOutboundJob"));
  2978. Assert(lpcJobQueue);
  2979. //
  2980. // be sure that the dir exists
  2981. //
  2982. EnterCriticalSection (&g_CsConfig);
  2983. lstrcpyn ( wszArchiveFolder,
  2984. g_ArchivesConfig[FAX_MESSAGE_FOLDER_SENTITEMS].lpcstrFolder,
  2985. MAX_PATH);
  2986. LeaveCriticalSection (&g_CsConfig);
  2987. ec=IsValidFaxFolder(wszArchiveFolder);
  2988. if (ERROR_SUCCESS != ec)
  2989. {
  2990. DebugPrintEx(DEBUG_ERR,
  2991. TEXT("IsValidFaxFolder failed for folder : %s (ec=%lu)."),
  2992. wszArchiveFolder,
  2993. ec);
  2994. FaxLog(
  2995. FAXLOG_CATEGORY_OUTBOUND,
  2996. FAXLOG_LEVEL_MIN,
  2997. 2,
  2998. MSG_FAX_ARCHIVE_OUTBOX_FOLDER_ERR,
  2999. wszArchiveFolder,
  3000. DWORD2DECIMAL(ec)
  3001. );
  3002. goto Error;
  3003. }
  3004. //
  3005. // get the user sid string
  3006. //
  3007. if (!ConvertSidToStringSid(lpcJobQueue->lpParentJob->UserSid, &lpwszUserSid))
  3008. {
  3009. ec = GetLastError();
  3010. DebugPrintEx(
  3011. DEBUG_ERR,
  3012. TEXT("ConvertSidToStringSid() failed (ec: %ld)"),
  3013. ec);
  3014. goto Error;
  3015. }
  3016. //
  3017. // get the file name
  3018. //
  3019. if (GenerateUniqueArchiveFileName( wszArchiveFolder,
  3020. ArchiveFileName,
  3021. ARR_SIZE(ArchiveFileName),
  3022. lpcJobQueue->UniqueId,
  3023. lpwszUserSid)) {
  3024. rVal = TRUE;
  3025. }
  3026. else
  3027. {
  3028. ec = GetLastError();
  3029. DebugPrintEx(
  3030. DEBUG_ERR,
  3031. TEXT("Failed to generate unique name for archive file at dir [%s] (ec: %ld)"),
  3032. wszArchiveFolder,
  3033. ec);
  3034. FaxLog(
  3035. FAXLOG_CATEGORY_OUTBOUND,
  3036. FAXLOG_LEVEL_MIN,
  3037. 1,
  3038. MSG_FAX_ARCHIVE_CREATE_FILE_FAILED,
  3039. DWORD2DECIMAL(ec)
  3040. );
  3041. goto Error;
  3042. }
  3043. if (rVal) {
  3044. Assert(lpcJobQueue->FileName);
  3045. rVal = CopyFile( lpcJobQueue->FileName, ArchiveFileName, FALSE );
  3046. if (!rVal)
  3047. {
  3048. ec = GetLastError();
  3049. DebugPrintEx(
  3050. DEBUG_ERR,
  3051. TEXT("CopyFile [%s] to [%s] failed. (ec: %ld)"),
  3052. lpcJobQueue->FileName,
  3053. ArchiveFileName,
  3054. ec);
  3055. FaxLog(
  3056. FAXLOG_CATEGORY_OUTBOUND,
  3057. FAXLOG_LEVEL_MIN,
  3058. 1,
  3059. MSG_FAX_ARCHIVE_CREATE_FILE_FAILED,
  3060. DWORD2DECIMAL(ec)
  3061. );
  3062. if (!DeleteFile(ArchiveFileName))
  3063. {
  3064. DebugPrintEx(
  3065. DEBUG_ERR,
  3066. TEXT("DeleteFile [%s] failed. (ec: %ld)"),
  3067. ArchiveFileName,
  3068. GetLastError());
  3069. }
  3070. goto Error;
  3071. }
  3072. }
  3073. if (rVal)
  3074. {
  3075. DWORD dwRes;
  3076. HANDLE hFind;
  3077. WIN32_FIND_DATA FindFileData;
  3078. if (!FillMsTagInfo( ArchiveFileName,
  3079. lpcJobQueue
  3080. ))
  3081. {
  3082. dwRes = GetLastError();
  3083. DebugPrintEx(
  3084. DEBUG_ERR,
  3085. TEXT("Failed to add MS TIFF tags to archived file %s. (ec: %ld)"),
  3086. ArchiveFileName,
  3087. dwRes);
  3088. FaxLog(
  3089. FAXLOG_CATEGORY_OUTBOUND,
  3090. FAXLOG_LEVEL_MIN,
  3091. 2,
  3092. MSG_FAX_ARCHIVE_NO_TAGS,
  3093. ArchiveFileName,
  3094. DWORD2HEX(dwRes)
  3095. );
  3096. }
  3097. dwRes = CreateArchiveEvent (lpcJobQueue->UniqueId,
  3098. FAX_EVENT_TYPE_OUT_ARCHIVE,
  3099. FAX_JOB_EVENT_TYPE_ADDED,
  3100. lpcJobQueue->lpParentJob->UserSid);
  3101. if (ERROR_SUCCESS != dwRes)
  3102. {
  3103. DebugPrintEx(
  3104. DEBUG_ERR,
  3105. TEXT("CreateConfigEvent(FAX_CONFIG_TYPE_*_ARCHIVE) failed (ec: %lc)"),
  3106. dwRes);
  3107. }
  3108. hFind = FindFirstFile( ArchiveFileName, &FindFileData);
  3109. if (INVALID_HANDLE_VALUE == hFind)
  3110. {
  3111. DebugPrintEx(
  3112. DEBUG_ERR,
  3113. TEXT("FindFirstFile failed (ec: %lc), File %s"),
  3114. GetLastError(),
  3115. ArchiveFileName);
  3116. }
  3117. else
  3118. {
  3119. // Update the archive size - for quota management
  3120. EnterCriticalSection (&g_CsConfig);
  3121. if (FAX_ARCHIVE_FOLDER_INVALID_SIZE != g_ArchivesConfig[FAX_MESSAGE_FOLDER_SENTITEMS].dwlArchiveSize)
  3122. {
  3123. g_ArchivesConfig[FAX_MESSAGE_FOLDER_SENTITEMS].dwlArchiveSize += (MAKELONGLONG(FindFileData.nFileSizeLow ,FindFileData.nFileSizeHigh));
  3124. }
  3125. LeaveCriticalSection (&g_CsConfig);
  3126. Assert (FindFileData.nFileSizeLow);
  3127. if (!FindClose(hFind))
  3128. {
  3129. DebugPrintEx(
  3130. DEBUG_ERR,
  3131. TEXT("FindClose failed (ec: %lc)"),
  3132. GetLastError());
  3133. }
  3134. }
  3135. FaxLog(
  3136. FAXLOG_CATEGORY_OUTBOUND,
  3137. FAXLOG_LEVEL_MAX,
  3138. 2,
  3139. MSG_FAX_SENT_ARCHIVE_SUCCESS,
  3140. lpcJobQueue->FileName,
  3141. ArchiveFileName
  3142. );
  3143. }
  3144. Assert( ERROR_SUCCESS == ec);
  3145. goto Exit;
  3146. Error:
  3147. Assert( ERROR_SUCCESS != ec);
  3148. FaxLog(
  3149. FAXLOG_CATEGORY_OUTBOUND,
  3150. FAXLOG_LEVEL_MIN,
  3151. 3,
  3152. MSG_FAX_ARCHIVE_FAILED,
  3153. lpcJobQueue->FileName,
  3154. wszArchiveFolder,
  3155. DWORD2HEX(GetLastError())
  3156. );
  3157. Exit:
  3158. if (ERROR_SUCCESS != ec)
  3159. {
  3160. SetLastError(ec);
  3161. }
  3162. if (lpwszUserSid != NULL)
  3163. {
  3164. LocalFree (lpwszUserSid);
  3165. }
  3166. return (ERROR_SUCCESS == ec);
  3167. }
  3168. BOOL UpdatePerfCounters(const JOB_QUEUE * lpcJobQueue)
  3169. {
  3170. SYSTEMTIME SystemTime ;
  3171. DWORD Seconds ;
  3172. HANDLE FileHandle ;
  3173. DWORD Bytes = 0 ; /// Compute #bytes in the file FaxSend.FileName and stick it here!
  3174. const JOB_ENTRY * lpcJobEntry;
  3175. DEBUG_FUNCTION_NAME(TEXT("UpdatePerfCounters"));
  3176. Assert(lpcJobQueue);
  3177. lpcJobEntry = lpcJobQueue->JobEntry;
  3178. Assert(lpcJobEntry);
  3179. FileHandle = SafeCreateFile(
  3180. lpcJobEntry->lpJobQueueEntry->FileName,
  3181. GENERIC_READ,
  3182. FILE_SHARE_READ,
  3183. NULL,
  3184. OPEN_EXISTING,
  3185. FILE_ATTRIBUTE_NORMAL,
  3186. NULL);
  3187. if(FileHandle != INVALID_HANDLE_VALUE)
  3188. {
  3189. Bytes = GetFileSize( FileHandle, NULL );
  3190. CloseHandle( FileHandle );
  3191. }
  3192. if (!FileTimeToSystemTime(
  3193. (FILETIME*)&lpcJobEntry->ElapsedTime,
  3194. &SystemTime
  3195. ))
  3196. {
  3197. DebugPrintEx(
  3198. DEBUG_ERR,
  3199. TEXT("FileTimeToSystemTime failed (ec: %ld)"),
  3200. GetLastError());
  3201. Assert(FALSE);
  3202. memset(&SystemTime,0,sizeof(SYSTEMTIME));
  3203. }
  3204. Seconds = (DWORD)( SystemTime.wSecond + 60 * ( SystemTime.wMinute + 60 * SystemTime.wHour ));
  3205. InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->OutboundFaxes );
  3206. InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->TotalFaxes );
  3207. InterlockedExchangeAdd( (PLONG)&g_pFaxPerfCounters->OutboundPages, (LONG)lpcJobEntry->FSPIJobStatus.dwPageCount );
  3208. InterlockedExchangeAdd( (PLONG)&g_pFaxPerfCounters->TotalPages, (LONG)lpcJobEntry->FSPIJobStatus.dwPageCount );
  3209. EnterCriticalSection( &g_CsPerfCounters );
  3210. g_dwOutboundSeconds += Seconds;
  3211. g_dwTotalSeconds += Seconds;
  3212. g_pFaxPerfCounters->OutboundMinutes = g_dwOutboundSeconds / 60 ;
  3213. g_pFaxPerfCounters->TotalMinutes = g_dwTotalSeconds / 60 ;
  3214. g_pFaxPerfCounters->OutboundBytes += Bytes;
  3215. g_pFaxPerfCounters->TotalBytes += Bytes;
  3216. LeaveCriticalSection( &g_CsPerfCounters );
  3217. return TRUE;
  3218. }
  3219. BOOL MarkJobAsExpired(PJOB_QUEUE lpJobQueue)
  3220. {
  3221. FILETIME CurrentFileTime;
  3222. LARGE_INTEGER NewTime;
  3223. DWORD dwMaxRetries;
  3224. BOOL rVal = TRUE;
  3225. DEBUG_FUNCTION_NAME(TEXT("MarkJobAsExpired"));
  3226. Assert(lpJobQueue);
  3227. Assert( JT_SEND == lpJobQueue->JobType ||
  3228. JT_ROUTING == lpJobQueue->JobType );
  3229. EnterCriticalSection(&g_CsQueue);
  3230. lpJobQueue->JobStatus = JS_RETRIES_EXCEEDED;
  3231. EnterCriticalSection (&g_CsConfig);
  3232. dwMaxRetries = g_dwFaxSendRetries;
  3233. LeaveCriticalSection (&g_CsConfig);
  3234. lpJobQueue->SendRetries = dwMaxRetries + 1;
  3235. //
  3236. // Set the job's ScheduleTime field to the time it totaly failed.
  3237. // (current time).
  3238. //
  3239. GetSystemTimeAsFileTime( &CurrentFileTime ); //Can not fail (Win32 SDK)
  3240. NewTime.LowPart = CurrentFileTime.dwLowDateTime;
  3241. NewTime.HighPart = CurrentFileTime.dwHighDateTime;
  3242. lpJobQueue->ScheduleTime = NewTime.QuadPart;
  3243. if (!CommitQueueEntry(lpJobQueue))
  3244. {
  3245. DebugPrintEx(
  3246. DEBUG_ERR,
  3247. TEXT("CommitQueueEntry() for recipien job %s has failed. (ec: %ld)"),
  3248. lpJobQueue->FileName,
  3249. GetLastError());
  3250. rVal = FALSE;
  3251. }
  3252. if (JT_SEND == lpJobQueue->JobType)
  3253. {
  3254. Assert (lpJobQueue->lpParentJob);
  3255. lpJobQueue->lpParentJob->dwFailedRecipientJobsCount+=1;
  3256. //
  3257. // The parent job keeps the schedule of the last recipient job that failed.
  3258. // The job retention policy for the parent will be based on that
  3259. // schedule.
  3260. lpJobQueue->lpParentJob->ScheduleTime = lpJobQueue->ScheduleTime;
  3261. if (!CommitQueueEntry(lpJobQueue->lpParentJob))
  3262. {
  3263. DebugPrintEx(
  3264. DEBUG_ERR,
  3265. TEXT("CommitQueueEntry() for parent job %s has failed. (ec: %ld)"),
  3266. lpJobQueue->lpParentJob->FileName,
  3267. GetLastError());
  3268. rVal = FALSE;
  3269. }
  3270. }
  3271. LeaveCriticalSection(&g_CsQueue);
  3272. return rVal;
  3273. }
  3274. //*********************************************************************************
  3275. //* Name: CreateJobEntry()
  3276. //* Author: Ronen Barenboim
  3277. //* Date: May 31, 1999
  3278. //*********************************************************************************
  3279. //* DESCRIPTION:
  3280. //* Creates and initializes a new JOB_ENTRY.
  3281. //* Opens the line the job is to be executed on (if it is a TAPI line)
  3282. //* and creates the attachement between the line and the job.
  3283. //* PARAMETERS:
  3284. //* [IN/OUT] PJOB_QUEUE lpJobQueue
  3285. //* For outgoing jobs this points to the JOB_QUEUE of the outgoing job.
  3286. //* for receive job this should be set to NULL.
  3287. //* [IN/OUT] LINE_INFO * lpLineInfo
  3288. //* A pointer to the LINE_INFO information of the line on which the job
  3289. //* is to be executed.
  3290. //* [IN ] BOOL bTranslateNumber
  3291. //* TRUE if the recipient number needs to be translated into dilable
  3292. //* string (needed for legacy FaxDevSend() where the number must be
  3293. //* dilable and not canonical).
  3294. //*
  3295. //* RETURN VALUE:
  3296. //*
  3297. //*********************************************************************************
  3298. PJOB_ENTRY CreateJobEntry(
  3299. PJOB_QUEUE lpJobQueue,
  3300. LINE_INFO * lpLineInfo,
  3301. BOOL bTranslateNumber
  3302. )
  3303. {
  3304. BOOL Failure = TRUE;
  3305. PJOB_ENTRY JobEntry = NULL;
  3306. DWORD rc = ERROR_SUCCESS;;
  3307. DEBUG_FUNCTION_NAME(TEXT("CreateJobEntry"));
  3308. Assert(!(lpJobQueue && lpJobQueue->JobType != JT_SEND));
  3309. Assert(!(bTranslateNumber && !lpJobQueue));
  3310. Assert (lpLineInfo);
  3311. JobEntry = (PJOB_ENTRY) MemAlloc( sizeof(JOB_ENTRY) );
  3312. if (!JobEntry)
  3313. {
  3314. rc=GetLastError();
  3315. DebugPrintEx(DEBUG_ERR,_T("Failed to allocated memory for JOB_ENTRY."));
  3316. goto exit;
  3317. }
  3318. memset(JobEntry, 0, sizeof(JOB_ENTRY));
  3319. if (lpJobQueue)
  3320. {
  3321. if (! _tcslen(lpJobQueue->tczDialableRecipientFaxNumber))
  3322. {
  3323. //
  3324. // The Fax Number was not compound, make translation as before
  3325. //
  3326. if (bTranslateNumber)
  3327. {
  3328. rc = TranslateCanonicalNumber(lpJobQueue->RecipientProfile.lptstrFaxNumber,
  3329. lpLineInfo->DeviceId,
  3330. JobEntry->DialablePhoneNumber,
  3331. ARR_SIZE(JobEntry->DialablePhoneNumber),
  3332. JobEntry->DisplayablePhoneNumber,
  3333. ARR_SIZE(JobEntry->DisplayablePhoneNumber));
  3334. if (ERROR_SUCCESS != rc)
  3335. {
  3336. DebugPrintEx(
  3337. DEBUG_ERR,
  3338. TEXT("TranslateCanonicalNumber() faield for number: %s (ec: %ld)"),
  3339. lpJobQueue->RecipientProfile.lptstrFaxNumber,
  3340. rc);
  3341. goto exit;
  3342. }
  3343. }
  3344. else
  3345. {
  3346. _tcsncpy(JobEntry->DialablePhoneNumber, lpJobQueue->RecipientProfile.lptstrFaxNumber, SIZEOF_PHONENO );
  3347. JobEntry->DialablePhoneNumber[SIZEOF_PHONENO - 1] = '\0';
  3348. _tcsncpy(JobEntry->DisplayablePhoneNumber, lpJobQueue->RecipientProfile.lptstrFaxNumber, SIZEOF_PHONENO );
  3349. JobEntry->DisplayablePhoneNumber[SIZEOF_PHONENO - 1] = '\0';
  3350. }
  3351. }
  3352. else
  3353. {
  3354. //
  3355. // The Fax Number was compound, no translation needed
  3356. // Take Dialable from JobQueue and Displayable from Recipient's PersonalProfile's FaxNumber
  3357. //
  3358. _tcsncpy(JobEntry->DialablePhoneNumber, lpJobQueue->tczDialableRecipientFaxNumber, SIZEOF_PHONENO );
  3359. _tcsncpy(JobEntry->DisplayablePhoneNumber, lpJobQueue->RecipientProfile.lptstrFaxNumber, (SIZEOF_PHONENO - 1));
  3360. JobEntry->DisplayablePhoneNumber[SIZEOF_PHONENO - 1] = '\0';
  3361. }
  3362. }
  3363. else
  3364. {
  3365. //
  3366. // lpJobQueue is NULL
  3367. //
  3368. JobEntry->DialablePhoneNumber[0] = L'\0';
  3369. JobEntry->DisplayablePhoneNumber[0] = L'\0';
  3370. }
  3371. JobEntry->CallHandle = 0;
  3372. JobEntry->InstanceData = 0;
  3373. JobEntry->LineInfo = lpLineInfo;
  3374. JobEntry->SendIdx = -1;
  3375. JobEntry->Released = FALSE;
  3376. JobEntry->lpJobQueueEntry = lpJobQueue;
  3377. JobEntry->bFSPJobInProgress = FALSE;
  3378. memset(&JobEntry->FSPIJobStatus,0,sizeof(FSPI_JOB_STATUS));
  3379. JobEntry->FSPIJobStatus.dwSizeOfStruct = sizeof(FSPI_JOB_STATUS);
  3380. JobEntry->FSPIJobStatus.dwJobStatus = FSPI_JS_UNKNOWN;
  3381. GetSystemTimeAsFileTime( (FILETIME*) &JobEntry->StartTime );
  3382. EnterCriticalSection (&g_CsLine);
  3383. if (!(lpLineInfo->Flags & FPF_VIRTUAL) && (!lpLineInfo->hLine))
  3384. {
  3385. if (!OpenTapiLine( lpLineInfo ))
  3386. {
  3387. rc = GetLastError();
  3388. DebugPrintEx(
  3389. DEBUG_ERR,
  3390. TEXT("OpenTapiLine failed. (ec: %ld)"),
  3391. rc);
  3392. LeaveCriticalSection (&g_CsLine);
  3393. goto exit;
  3394. }
  3395. }
  3396. //
  3397. // Attach the job to the line selected to service it.
  3398. //
  3399. lpLineInfo->JobEntry = JobEntry;
  3400. LeaveCriticalSection (&g_CsLine);
  3401. Failure = FALSE;
  3402. exit:
  3403. if (Failure)
  3404. {
  3405. // Failure is initialized to TRUE
  3406. if (JobEntry)
  3407. {
  3408. MemFree( JobEntry );
  3409. }
  3410. JobEntry = NULL;
  3411. }
  3412. if (ERROR_SUCCESS != rc)
  3413. {
  3414. SetLastError(rc);
  3415. }
  3416. return JobEntry;
  3417. } // CreateJobEntry
  3418. //*********************************************************************************
  3419. //* Name: TranslateCanonicalNumber()
  3420. //* Author: Ronen Barenboim
  3421. //* Date: May 31, 1999
  3422. //*********************************************************************************
  3423. //* DESCRIPTION:
  3424. //* Translates a canonical number to a dilable + displayable number.
  3425. //*
  3426. //* PARAMETERS:
  3427. //* [IN ] LPTSTR lptstrFaxNumber
  3428. //* The canonical number to translate.
  3429. //*
  3430. //* [IN ] DWORD dwDeviceID
  3431. //* The device ID.
  3432. //*
  3433. //* [OUT] LPTSTR lptstrDialableAddress
  3434. //* Buffer to receive the dialable translated address.
  3435. //*
  3436. //* [IN] DWORD dwDialableAddressCount
  3437. //* size in TCHARs of the buffer pointed by lptstrDialableAddress
  3438. //*
  3439. //* [OUT] LPTSTR lptstrDisplayableAddress
  3440. //* Buffer to receive the displayable translated address.
  3441. //*
  3442. //* [IN] DWORD dwDisplayableAddressCount
  3443. //* size in TCHARs of the buffer pointed by lptstrDialableAddress
  3444. //*
  3445. //* RETURN VALUE:
  3446. //* Win32 / HRESULT error code
  3447. //*********************************************************************************
  3448. static
  3449. DWORD
  3450. TranslateCanonicalNumber(
  3451. LPTSTR lptstrCanonicalFaxNumber,
  3452. DWORD dwDeviceID,
  3453. LPTSTR lptstrDialableAddress,
  3454. DWORD dwDialableAddressCount,
  3455. LPTSTR lptstrDisplayableAddress,
  3456. DWORD dwDisplayableAddressCount
  3457. )
  3458. {
  3459. DWORD ec = ERROR_SUCCESS;
  3460. LPLINETRANSLATEOUTPUT LineTranslateOutput = NULL;
  3461. DEBUG_FUNCTION_NAME(TEXT("TranslateCanonicalNumber"));
  3462. Assert(lptstrCanonicalFaxNumber && lptstrDialableAddress && lptstrDisplayableAddress);
  3463. ec = MyLineTranslateAddress( lptstrCanonicalFaxNumber, dwDeviceID, &LineTranslateOutput );
  3464. if (ERROR_SUCCESS == ec)
  3465. {
  3466. LPTSTR lptstrTranslateBuffer;
  3467. HRESULT hr;
  3468. //
  3469. // Copy displayable string
  3470. // TAPI returns credit card numbers in the displayable string.
  3471. // return the input canonical number as the displayable string.
  3472. //
  3473. hr = StringCchCopy(
  3474. lptstrDisplayableAddress,
  3475. dwDisplayableAddressCount,
  3476. lptstrCanonicalFaxNumber);
  3477. if (FAILED(hr))
  3478. {
  3479. DebugPrintEx(
  3480. DEBUG_ERR,
  3481. TEXT("StringCchCopy() failed (ec: %ld)"),
  3482. hr);
  3483. ec = HRESULT_CODE(hr);
  3484. goto Exit;
  3485. }
  3486. //
  3487. // Copy dialable string
  3488. //
  3489. Assert (LineTranslateOutput->dwDialableStringSize > 0);
  3490. lptstrTranslateBuffer=(LPTSTR)((LPBYTE)LineTranslateOutput + LineTranslateOutput->dwDialableStringOffset);
  3491. hr = StringCchCopy(
  3492. lptstrDialableAddress,
  3493. dwDialableAddressCount,
  3494. lptstrTranslateBuffer);
  3495. if (FAILED(hr))
  3496. {
  3497. DebugPrintEx(
  3498. DEBUG_ERR,
  3499. TEXT("StringCchCopy() failed (ec: %ld)"),
  3500. hr);
  3501. ec = HRESULT_CODE(hr);
  3502. goto Exit;
  3503. }
  3504. }
  3505. else
  3506. {
  3507. // ec is a Tapi ERROR
  3508. DebugPrintEx(
  3509. DEBUG_ERR,
  3510. TEXT("MyLineTranslateAddress() failed for fax number: [%s] (ec: %ld)"),
  3511. lptstrCanonicalFaxNumber,
  3512. ec);
  3513. goto Exit;
  3514. }
  3515. Assert (ERROR_SUCCESS == ec);
  3516. Exit:
  3517. MemFree( LineTranslateOutput );
  3518. if (ERROR_SUCCESS != ec)
  3519. {
  3520. SetLastError(ec);
  3521. }
  3522. return ec;
  3523. } // TranslateCanonicalNumber
  3524. //*********************************************************************************
  3525. //* Name: HandleCompletedSendJob()
  3526. //* Author: Ronen Barenboim
  3527. //* Date: June 01, 1999
  3528. //*********************************************************************************
  3529. //* DESCRIPTION:
  3530. //* Handles the completion of a recipient job. Called when a recipient job
  3531. //* has reaced a JS_COMPLETED state.
  3532. //*
  3533. //* IMPORTANT- This call can be blocking. Calling thread MUST NOT hold any critical section
  3534. //*
  3535. //* - Marks the job as completed (JS_COMPLETED).
  3536. //* - Archives the sent file if required.
  3537. //* - Sends a positive receipt
  3538. //* - Removes the parent job if required.
  3539. //*
  3540. //* PARAMETERS:
  3541. //* [IN ] PJOB_ENTRY lpJobEntry
  3542. //*
  3543. //* RETURN VALUE:
  3544. //* TRUE
  3545. //* If the operation completed successfully.
  3546. //* FALSE
  3547. //* If the operation failed. Call GetLastError() for extended errror
  3548. //* information.
  3549. //*********************************************************************************
  3550. BOOL HandleCompletedSendJob(PJOB_ENTRY lpJobEntry)
  3551. {
  3552. PJOB_QUEUE lpJobQueue = NULL;
  3553. DWORD ec = 0;
  3554. BOOL fCOMInitiliazed = FALSE;
  3555. HRESULT hr;
  3556. BOOL bArchiveSentItems;
  3557. DWORD dwRes;
  3558. DEBUG_FUNCTION_NAME(TEXT("HandleCompletedSendJob)"));
  3559. EnterCriticalSection ( &g_CsJob );
  3560. EnterCriticalSection (&g_CsConfig);
  3561. bArchiveSentItems = g_ArchivesConfig[FAX_MESSAGE_FOLDER_SENTITEMS].bUseArchive;
  3562. LeaveCriticalSection (&g_CsConfig);
  3563. Assert(lpJobEntry);
  3564. lpJobQueue = lpJobEntry->lpJobQueueEntry;
  3565. Assert(lpJobQueue);
  3566. Assert(JT_SEND == lpJobQueue->JobType);
  3567. Assert(FSPI_JS_COMPLETED == lpJobEntry->FSPIJobStatus.dwJobStatus);
  3568. //
  3569. // Update end time in JOB_ENTRY
  3570. //
  3571. GetSystemTimeAsFileTime( (FILETIME*) &lpJobEntry->EndTime );
  3572. //
  3573. // Update elapsed time in JOB_ENTRY
  3574. //
  3575. Assert (lpJobEntry->EndTime >= lpJobEntry->StartTime);
  3576. lpJobEntry->ElapsedTime = lpJobEntry->EndTime - lpJobEntry->StartTime;
  3577. //
  3578. // We generate a full tiff for each recipient
  3579. // so we will have something to put in the send archive.
  3580. //
  3581. if (!lpJobQueue->FileName)
  3582. {
  3583. DebugPrintEx(
  3584. DEBUG_MSG,
  3585. TEXT("[JobId: %ld] Generating body for recipient job."),
  3586. lpJobQueue->JobId
  3587. );
  3588. if (!CreateTiffFileForJob(lpJobQueue))
  3589. {
  3590. dwRes = GetLastError();
  3591. DebugPrintEx(
  3592. DEBUG_ERR,
  3593. TEXT("[JobId: %ld] CreateTiffFileForJob failed. (ec: %ld)"),
  3594. lpJobQueue->JobId,
  3595. dwRes);
  3596. FaxLog(
  3597. FAXLOG_CATEGORY_OUTBOUND,
  3598. FAXLOG_LEVEL_MIN,
  3599. 1,
  3600. MSG_FAX_TIFF_CREATE_FAILED_NO_ARCHIVE,
  3601. g_wszFaxQueueDir,
  3602. DWORD2DECIMAL(dwRes)
  3603. );
  3604. }
  3605. }
  3606. // Needed for Archiving
  3607. hr = CoInitialize (NULL);
  3608. if (FAILED (hr))
  3609. {
  3610. WCHAR wszArchiveFolder[MAX_PATH];
  3611. EnterCriticalSection (&g_CsConfig);
  3612. lstrcpyn ( wszArchiveFolder,
  3613. g_ArchivesConfig[FAX_MESSAGE_FOLDER_SENTITEMS].lpcstrFolder,
  3614. MAX_PATH);
  3615. LeaveCriticalSection (&g_CsConfig);
  3616. DebugPrintEx( DEBUG_ERR,
  3617. TEXT("CoInitilaize failed, err %ld"),
  3618. hr);
  3619. FaxLog(
  3620. FAXLOG_CATEGORY_OUTBOUND,
  3621. FAXLOG_LEVEL_MIN,
  3622. 3,
  3623. MSG_FAX_ARCHIVE_FAILED,
  3624. lpJobQueue->FileName,
  3625. wszArchiveFolder,
  3626. DWORD2DECIMAL(hr)
  3627. );
  3628. }
  3629. else
  3630. {
  3631. fCOMInitiliazed = TRUE;
  3632. }
  3633. if (lpJobQueue->FileName) //might be null if we failed to generate a TIFF
  3634. {
  3635. //
  3636. // Archive the file (also adds MS Tags to the tiff at the archive directory)
  3637. //
  3638. if (bArchiveSentItems && fCOMInitiliazed)
  3639. {
  3640. if (!ArchiveOutboundJob(lpJobQueue))
  3641. {
  3642. DebugPrintEx(
  3643. DEBUG_ERR,
  3644. TEXT("JobId: %ld] ArchiveOutboundJob() failed (ec: %ld)"),
  3645. lpJobQueue->JobId,
  3646. GetLastError());
  3647. //
  3648. // The event log entry is generated by the function itself
  3649. //
  3650. }
  3651. }
  3652. }
  3653. //
  3654. // Log the succesful send to the event log
  3655. //
  3656. EnterCriticalSection (&g_CsOutboundActivityLogging);
  3657. if (INVALID_HANDLE_VALUE == g_hOutboxActivityLogFile)
  3658. {
  3659. DebugPrintEx(DEBUG_ERR,
  3660. TEXT("Logging not initialized"));
  3661. }
  3662. else
  3663. {
  3664. if (!LogOutboundActivity(lpJobQueue))
  3665. {
  3666. DebugPrintEx(DEBUG_ERR, TEXT("Logging outbound activity failed"));
  3667. }
  3668. }
  3669. LeaveCriticalSection (&g_CsOutboundActivityLogging);
  3670. if (fCOMInitiliazed == TRUE)
  3671. {
  3672. CoUninitialize ();
  3673. }
  3674. FaxLogSend(lpJobQueue, FALSE);
  3675. //
  3676. // Increment counters for Performance Monitor
  3677. //
  3678. if (g_pFaxPerfCounters)
  3679. {
  3680. if (!UpdatePerfCounters(lpJobQueue))
  3681. {
  3682. DebugPrintEx(
  3683. DEBUG_ERR,
  3684. TEXT("[JobId: %ld] UpdatePerfCounters() failed. (ec: %ld)"),
  3685. lpJobQueue->JobId,
  3686. GetLastError());
  3687. Assert(FALSE);
  3688. }
  3689. }
  3690. EnterCriticalSection ( &g_CsQueue );
  3691. //
  3692. // Mark the job as completed (new client API)
  3693. //
  3694. lpJobQueue->JobStatus = JS_COMPLETED;
  3695. //
  3696. // Save the last extended status before ending this job
  3697. //
  3698. lpJobQueue->dwLastJobExtendedStatus = lpJobQueue->JobEntry->FSPIJobStatus.dwExtendedStatus;
  3699. hr = StringCchCopy(
  3700. lpJobQueue->ExStatusString,
  3701. ARR_SIZE(lpJobQueue->ExStatusString),
  3702. lpJobQueue->JobEntry->ExStatusString);
  3703. if (FAILED(hr))
  3704. {
  3705. //
  3706. // Can never happen, we use large enough buffer.
  3707. //
  3708. ASSERT_FALSE;
  3709. }
  3710. if (!UpdatePersistentJobStatus(lpJobQueue))
  3711. {
  3712. DebugPrintEx(
  3713. DEBUG_ERR,
  3714. TEXT("Failed to update persistent job status to 0x%08x"),
  3715. lpJobQueue->JobStatus);
  3716. Assert(FALSE);
  3717. }
  3718. lpJobQueue->lpParentJob->dwCompletedRecipientJobsCount+=1;
  3719. //
  3720. // Create Fax EventEx
  3721. //
  3722. dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS, lpJobQueue );
  3723. if (ERROR_SUCCESS != dwRes)
  3724. {
  3725. DebugPrintEx(
  3726. DEBUG_ERR,
  3727. TEXT("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) failed for job id %ld (ec: %lc)"),
  3728. lpJobQueue->UniqueId,
  3729. dwRes);
  3730. }
  3731. //
  3732. // We will send the receipt once we are out of all critical sections because this call can be blocking.
  3733. // just increase the preview refernce count so the job will not be deleted.
  3734. //
  3735. IncreaseJobRefCount (lpJobQueue, TRUE); // TRUE - preview
  3736. //
  3737. // Copy receipt information from JobEntry.
  3738. //
  3739. lpJobQueue->StartTime = lpJobQueue->JobEntry->StartTime;
  3740. lpJobQueue->EndTime = lpJobQueue->JobEntry->EndTime;
  3741. //
  3742. // EndJob() must be called BEFORE we remove the parent job (and recipients)
  3743. //
  3744. lpJobQueue->JobEntry->LineInfo->State = FPS_AVAILABLE;
  3745. //
  3746. // We just completed a send job on the device - update counter.
  3747. //
  3748. (VOID) UpdateDeviceJobsCounter (lpJobQueue->JobEntry->LineInfo, // Device to update
  3749. TRUE, // Sending
  3750. -1, // Number of new jobs (-1 = decrease by one)
  3751. TRUE); // Enable events
  3752. if (!EndJob( lpJobQueue->JobEntry ))
  3753. {
  3754. DebugPrintEx(
  3755. DEBUG_ERR,
  3756. TEXT("EndJob Failed. (ec: %ld)"),
  3757. GetLastError());
  3758. }
  3759. lpJobQueue->JobEntry = NULL;
  3760. DecreaseJobRefCount (lpJobQueue, TRUE); // This will mark it as JS_DELETING if needed
  3761. //
  3762. // Notify the queue that a device is now available.
  3763. //
  3764. if (!SetEvent( g_hJobQueueEvent ))
  3765. {
  3766. DebugPrintEx(
  3767. DEBUG_ERR,
  3768. TEXT("Failed to set g_hJobQueueEvent. (ec: %ld)"),
  3769. GetLastError());
  3770. g_ScanQueueAfterTimeout = TRUE;
  3771. }
  3772. LeaveCriticalSection ( &g_CsQueue );
  3773. LeaveCriticalSection ( &g_CsJob );
  3774. //
  3775. // Now send the receipt
  3776. //
  3777. if (!SendJobReceipt (TRUE, lpJobQueue, lpJobQueue->FileName))
  3778. {
  3779. ec = GetLastError ();
  3780. DebugPrintEx(
  3781. DEBUG_ERR,
  3782. TEXT("[JobId: %ld] SendJobReceipt failed. (ec: %ld)"),
  3783. lpJobQueue->JobId,
  3784. ec
  3785. );
  3786. }
  3787. EnterCriticalSection (&g_CsQueue);
  3788. DecreaseJobRefCount (lpJobQueue, TRUE, TRUE, TRUE); // last TRUE for Preview ref count.
  3789. LeaveCriticalSection (&g_CsQueue);
  3790. return TRUE;
  3791. } // HandleCompletedSendJob
  3792. //*********************************************************************************
  3793. //* Name: HandleFailedSendJob()
  3794. //* Author: Ronen Barenboim
  3795. //* Date: June 01, 1999
  3796. //*********************************************************************************
  3797. //* DESCRIPTION:
  3798. //* Handles the post failure operations of a send job.
  3799. //*
  3800. //* IMPORTANT- This call can be blocking. Calling thread MUST NOT hold any critical section
  3801. //*
  3802. //* PARAMETERS:
  3803. //* [IN ] PJOB_ENTRY lpJobEntry
  3804. //* The job that failed. It must be in FSPI_JS_ABORTED or FSPI_JS_FAILED
  3805. //* state.
  3806. //* RETURN VALUE:
  3807. //* TRUE
  3808. //* If the operation completed successfully.
  3809. //* FALSE
  3810. //* If the operation failed. Call GetLastError() for extended errror
  3811. //* information.
  3812. //*********************************************************************************
  3813. BOOL HandleFailedSendJob(PJOB_ENTRY lpJobEntry)
  3814. {
  3815. PJOB_QUEUE lpJobQueue;
  3816. BOOL bRetrying = FALSE;
  3817. DEBUG_FUNCTION_NAME(TEXT("HandleFailedSendJob"));
  3818. DWORD dwRes;
  3819. TCHAR tszJobTiffFile[MAX_PATH] = {0}; // Deleted after receipt is sent
  3820. BOOL fAddRetryDelay = TRUE;
  3821. EnterCriticalSection ( &g_CsJob );
  3822. EnterCriticalSection ( &g_CsQueue );
  3823. Assert(lpJobEntry);
  3824. lpJobQueue = lpJobEntry->lpJobQueueEntry;
  3825. Assert(lpJobQueue);
  3826. DebugPrintEx(
  3827. DEBUG_MSG,
  3828. TEXT("Failed Job: %ld"),
  3829. lpJobQueue->JobId);
  3830. Assert( FSPI_JS_ABORTED == lpJobEntry->FSPIJobStatus.dwJobStatus ||
  3831. FSPI_JS_FAILED == lpJobEntry->FSPIJobStatus.dwJobStatus ||
  3832. FSPI_JS_FAILED_NO_RETRY == lpJobEntry->FSPIJobStatus.dwJobStatus ||
  3833. FSPI_JS_DELETED == lpJobEntry->FSPIJobStatus.dwJobStatus);
  3834. //
  3835. // Do not cache rendered tiff files
  3836. //
  3837. if (lpJobQueue->FileName)
  3838. {
  3839. //
  3840. // We simply store the file name to delete and delete it later
  3841. // since we might need it for receipt attachment.
  3842. //
  3843. _tcsncpy (tszJobTiffFile,
  3844. lpJobQueue->FileName,
  3845. (sizeof (tszJobTiffFile) / sizeof (tszJobTiffFile[0]))-1);
  3846. MemFree (lpJobQueue->FileName);
  3847. lpJobQueue->FileName = NULL;
  3848. }
  3849. //
  3850. // Update end time in JOB_ENTRY
  3851. //
  3852. GetSystemTimeAsFileTime( (FILETIME*) &lpJobEntry->EndTime );
  3853. //
  3854. // Update elapsed time in JOB_ENTRY
  3855. //
  3856. Assert (lpJobEntry->EndTime >= lpJobEntry->StartTime);
  3857. lpJobEntry->ElapsedTime = lpJobEntry->EndTime - lpJobEntry->StartTime;
  3858. if ( FSPI_JS_ABORTED == lpJobEntry->FSPIJobStatus.dwJobStatus)
  3859. {
  3860. //
  3861. // The FSP reported the job was aborted.
  3862. //
  3863. DebugPrintEx(
  3864. DEBUG_MSG,
  3865. TEXT("[Job Id: %ld] EFSP reported that job was aborted."),
  3866. lpJobQueue->JobId);
  3867. //
  3868. // Check if the job was aborted by the service (shutting down) or by the user
  3869. //
  3870. if (FALSE == lpJobEntry->fSystemAbort)
  3871. {
  3872. //
  3873. // The event log about a canceled job will be reported at the end of this if..else block.
  3874. //
  3875. lpJobEntry->Aborting = 1;
  3876. bRetrying = FALSE; // Do not retry on cancel
  3877. }
  3878. else
  3879. {
  3880. //
  3881. // SystemAbort
  3882. // Don't increase the retry count since this is not really a failure.
  3883. //
  3884. bRetrying = TRUE;
  3885. fAddRetryDelay = FALSE;
  3886. }
  3887. }
  3888. else if ( FSPI_JS_FAILED == lpJobEntry->FSPIJobStatus.dwJobStatus)
  3889. {
  3890. switch (lpJobEntry->FSPIJobStatus.dwExtendedStatus)
  3891. {
  3892. case FSPI_ES_LINE_UNAVAILABLE:
  3893. //
  3894. // this is the glare condition. Someone snatched the line before the FSP
  3895. // had a chance to grab it.
  3896. // We will try again but will not increase the retry count.
  3897. //
  3898. EnterCriticalSection (&g_CsLine);
  3899. //
  3900. // Check if the line was busy or closed
  3901. //
  3902. if (!(lpJobEntry->LineInfo->Flags & FPF_VIRTUAL))
  3903. {
  3904. //
  3905. // Tapi line
  3906. //
  3907. if (NULL == lpJobEntry->LineInfo->hLine)
  3908. {
  3909. //
  3910. // Tapi worker thread got LINE_CLOSE
  3911. //
  3912. fAddRetryDelay = FALSE;
  3913. }
  3914. }
  3915. LeaveCriticalSection (&g_CsLine);
  3916. bRetrying = TRUE;
  3917. if (g_pFaxPerfCounters)
  3918. {
  3919. //
  3920. // Increase the 'Outbound failed connections' counter.
  3921. //
  3922. InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->OutboundFailedConnections );
  3923. }
  3924. //
  3925. // Don't increase the retry count since this is not really a failure.
  3926. //
  3927. break;
  3928. case FSPI_ES_NO_ANSWER:
  3929. case FSPI_ES_NO_DIAL_TONE:
  3930. case FSPI_ES_DISCONNECTED:
  3931. case FSPI_ES_BUSY:
  3932. case FSPI_ES_NOT_FAX_CALL:
  3933. case FSPI_ES_CALL_DELAYED:
  3934. //
  3935. // For these error codes we need to retry
  3936. //
  3937. bRetrying = CheckForJobRetry(lpJobQueue);
  3938. if (g_pFaxPerfCounters)
  3939. {
  3940. //
  3941. // Increase the 'Outbound failed connections' counter.
  3942. //
  3943. InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->OutboundFailedConnections );
  3944. }
  3945. break;
  3946. case FSPI_ES_FATAL_ERROR:
  3947. //
  3948. // For these error codes we need to retry
  3949. //
  3950. bRetrying = CheckForJobRetry(lpJobQueue);
  3951. if (g_pFaxPerfCounters)
  3952. {
  3953. //
  3954. // Increase the 'Outbound failed transmissions' counter.
  3955. //
  3956. InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->OutboundFailedXmit );
  3957. }
  3958. break;
  3959. case FSPI_ES_BAD_ADDRESS:
  3960. case FSPI_ES_CALL_BLACKLISTED:
  3961. //
  3962. // No retry for these error codes
  3963. //
  3964. bRetrying = FALSE;
  3965. if (g_pFaxPerfCounters)
  3966. {
  3967. //
  3968. // Increase the 'Outbound failed connections' counter.
  3969. //
  3970. InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->OutboundFailedConnections );
  3971. }
  3972. break;
  3973. default:
  3974. //
  3975. // Our default for extension codes
  3976. // is to retry.
  3977. //
  3978. bRetrying = CheckForJobRetry(lpJobQueue);
  3979. if (g_pFaxPerfCounters)
  3980. {
  3981. //
  3982. // Increase the 'Outbound failed transmissions' counter.
  3983. //
  3984. InterlockedIncrement( (PLONG)&g_pFaxPerfCounters->OutboundFailedXmit );
  3985. }
  3986. break;
  3987. }
  3988. }
  3989. else if ( FSPI_JS_FAILED_NO_RETRY == lpJobEntry->FSPIJobStatus.dwJobStatus )
  3990. {
  3991. //
  3992. // The FSP indicated that there is no point in retrying this job.
  3993. //
  3994. bRetrying = FALSE;
  3995. }
  3996. else if ( FSPI_JS_DELETED == lpJobEntry->FSPIJobStatus.dwJobStatus )
  3997. {
  3998. //
  3999. // This is the case where the job can not be reestablished
  4000. // we treat it as a failure with no retry.
  4001. bRetrying = FALSE;
  4002. }
  4003. if (lpJobEntry->Aborting )
  4004. {
  4005. //
  4006. // An abort operation is in progress for this job.
  4007. // No point in retrying.
  4008. // Just mark the job as canceled and see if we can remove the parent job yet.
  4009. //
  4010. DebugPrintEx(
  4011. DEBUG_MSG,
  4012. TEXT("[JobId: %ld] lpJobEntry->Aborting is ON."));
  4013. lpJobQueue->JobStatus = JS_CANCELED;
  4014. if (!UpdatePersistentJobStatus(lpJobQueue))
  4015. {
  4016. DebugPrintEx(
  4017. DEBUG_ERR,
  4018. TEXT("Failed to update persistent job status to 0x%08x"),
  4019. lpJobQueue->JobStatus);
  4020. Assert(FALSE);
  4021. }
  4022. lpJobQueue->lpParentJob->dwCanceledRecipientJobsCount+=1;
  4023. bRetrying = FALSE;
  4024. }
  4025. //
  4026. // Save the last extended status before ending this job
  4027. //
  4028. lpJobQueue->dwLastJobExtendedStatus = lpJobEntry->FSPIJobStatus.dwExtendedStatus;
  4029. HRESULT hr = StringCchCopy(
  4030. lpJobQueue->ExStatusString,
  4031. ARR_SIZE(lpJobQueue->ExStatusString),
  4032. lpJobQueue->JobEntry->ExStatusString);
  4033. if (FAILED(hr))
  4034. {
  4035. //
  4036. // Can never happen, we use large enough buffer.
  4037. //
  4038. ASSERT_FALSE;
  4039. }
  4040. if (!bRetrying && !lpJobEntry->Aborting)
  4041. {
  4042. //
  4043. // If we do not handle an abort request (in this case we do not want
  4044. // to count it as a failure since it will be counted as Canceled) and we decided
  4045. // not to retry then we need to mark the job as expired.
  4046. //
  4047. if (0 == lpJobQueue->dwLastJobExtendedStatus)
  4048. {
  4049. //
  4050. // Job was never really executed - this is a fatal error
  4051. //
  4052. lpJobQueue->dwLastJobExtendedStatus = FSPI_ES_FATAL_ERROR;
  4053. lpJobQueue->ExStatusString[0] = L'\0';
  4054. }
  4055. if (!MarkJobAsExpired(lpJobQueue))
  4056. {
  4057. DebugPrintEx(
  4058. DEBUG_ERR,
  4059. TEXT("[JobId: %ld] MarkJobAsExpired failed (ec: %ld)"),
  4060. lpJobQueue->JobId,
  4061. GetLastError());
  4062. }
  4063. }
  4064. if (!bRetrying)
  4065. {
  4066. //
  4067. // Job reached final failure state - send negative receipt
  4068. // We will send the receipt once we are out of all critical sections because this call can be blocking.
  4069. // just increase the preview refernce count so the job will not be deleted.
  4070. //
  4071. IncreaseJobRefCount (lpJobQueue, TRUE); // TRUE - preview
  4072. //
  4073. // Copy receipt information from JobEntry.
  4074. //
  4075. lpJobQueue->StartTime = lpJobQueue->JobEntry->StartTime;
  4076. lpJobQueue->EndTime = lpJobQueue->JobEntry->EndTime;
  4077. }
  4078. else
  4079. {
  4080. //
  4081. // Job marked for retry. Do not delete it. Reschedule it.
  4082. //
  4083. DebugPrintEx(
  4084. DEBUG_MSG,
  4085. TEXT("[JobId: %ld] Set for retry (JS_RETRYING). Retry Count = %ld)"),
  4086. lpJobQueue->JobId,
  4087. lpJobQueue->SendRetries);
  4088. lpJobQueue->JobStatus = JS_RETRYING;
  4089. //
  4090. // Job entry must be NULLified before leaving the CS.
  4091. // This is done below because we still need the Job entry for logging
  4092. //
  4093. if (TRUE == fAddRetryDelay)
  4094. {
  4095. //
  4096. // Send failure - Reschedule
  4097. //
  4098. RescheduleJobQueueEntry( lpJobQueue );
  4099. }
  4100. else
  4101. {
  4102. //
  4103. // FaxDevShutDown() was called, or We lost the line, Do not add retry delay
  4104. //
  4105. if (!CommitQueueEntry(lpJobQueue))
  4106. {
  4107. DebugPrintEx(
  4108. DEBUG_ERR,
  4109. TEXT("CommitQueueEntry() for recipien job %s has failed. (ec: %ld)"),
  4110. lpJobQueue->FileName,
  4111. GetLastError());
  4112. }
  4113. }
  4114. }
  4115. FaxLogSend(
  4116. lpJobQueue,
  4117. bRetrying);
  4118. if (!bRetrying)
  4119. {
  4120. EnterCriticalSection (&g_CsOutboundActivityLogging);
  4121. if (INVALID_HANDLE_VALUE == g_hOutboxActivityLogFile)
  4122. {
  4123. DebugPrintEx(DEBUG_ERR,
  4124. TEXT("Logging not initialized"));
  4125. }
  4126. else
  4127. {
  4128. if (!LogOutboundActivity(lpJobQueue))
  4129. {
  4130. DebugPrintEx(DEBUG_ERR, TEXT("Logging outbound activity failed"));
  4131. }
  4132. }
  4133. LeaveCriticalSection (&g_CsOutboundActivityLogging);
  4134. }
  4135. //
  4136. // Notify clients on status change
  4137. //
  4138. dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS, lpJobQueue);
  4139. if (ERROR_SUCCESS != dwRes)
  4140. {
  4141. DebugPrintEx(
  4142. DEBUG_ERR,
  4143. TEXT("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) failed for job id %ld (ec: %lc)"),
  4144. lpJobQueue->UniqueId,
  4145. dwRes);
  4146. }
  4147. //
  4148. // EndJob() must be called BEFORE we remove the parent job (and recipients)
  4149. //
  4150. lpJobEntry->LineInfo->State = FPS_AVAILABLE;
  4151. //
  4152. // We just completed a send job on the device - update counter.
  4153. //
  4154. (VOID) UpdateDeviceJobsCounter ( lpJobEntry->LineInfo, // Device to update
  4155. TRUE, // Sending
  4156. -1, // Number of new jobs (-1 = decrease by one)
  4157. TRUE); // Enable events
  4158. if (!EndJob( lpJobEntry ))
  4159. {
  4160. DebugPrintEx(
  4161. DEBUG_ERR,
  4162. TEXT("EndJob Failed. (ec: %ld)"),
  4163. GetLastError());
  4164. }
  4165. lpJobQueue->JobEntry = NULL;
  4166. if (JS_CANCELED == lpJobQueue->JobStatus)
  4167. {
  4168. DWORD dwJobId;
  4169. dwJobId = lpJobQueue->JobId;
  4170. // Job was canceled - decrease reference count
  4171. DecreaseJobRefCount (lpJobQueue, TRUE); // This will mark it as JS_DELETING if needed
  4172. //
  4173. // We need to send the legacy W2K FEI_DELETING notification.
  4174. //
  4175. if (!CreateFaxEvent(0, FEI_DELETED, dwJobId))
  4176. {
  4177. DebugPrintEx(
  4178. DEBUG_ERR,
  4179. TEXT("CreateFaxEvent() failed. Event: 0x%08X JobId: %ld DeviceId: (ec: %ld)"),
  4180. FEI_DELETED,
  4181. lpJobQueue->JobId,
  4182. 0,
  4183. GetLastError());
  4184. }
  4185. }
  4186. //
  4187. // Notify the queue that a device is now available.
  4188. //
  4189. if (!SetEvent( g_hJobQueueEvent ))
  4190. {
  4191. DebugPrintEx(
  4192. DEBUG_ERR,
  4193. TEXT("Failed to set g_hJobQueueEvent. (ec: %ld)"),
  4194. GetLastError());
  4195. g_ScanQueueAfterTimeout = TRUE;
  4196. }
  4197. LeaveCriticalSection ( &g_CsQueue );
  4198. LeaveCriticalSection ( &g_CsJob );
  4199. //
  4200. // Now, send the receipt
  4201. //
  4202. if (!bRetrying)
  4203. {
  4204. //
  4205. // Job reached final failure state - send negative receipt
  4206. //
  4207. if (!SendJobReceipt (FALSE, lpJobQueue, tszJobTiffFile))
  4208. {
  4209. DebugPrintEx(
  4210. DEBUG_ERR,
  4211. TEXT("[JobId: %ld] SendJobReceipt failed. (ec: %ld)"),
  4212. lpJobQueue->JobId,
  4213. GetLastError ());
  4214. }
  4215. EnterCriticalSection (&g_CsQueue);
  4216. DecreaseJobRefCount (lpJobQueue, TRUE, TRUE, TRUE); // last TRUE for Preview ref count.
  4217. LeaveCriticalSection (&g_CsQueue);
  4218. }
  4219. if (lstrlen (tszJobTiffFile))
  4220. {
  4221. //
  4222. // Now we can safely delete the job's TIFF file
  4223. //
  4224. DebugPrintEx(DEBUG_MSG,
  4225. TEXT("Deleting per recipient body file %s"),
  4226. tszJobTiffFile);
  4227. if (!DeleteFile( tszJobTiffFile ))
  4228. {
  4229. DebugPrintEx(DEBUG_MSG,
  4230. TEXT("Failed to delete per recipient body file %s (ec: %ld)"),
  4231. tszJobTiffFile,
  4232. GetLastError());
  4233. }
  4234. }
  4235. return TRUE;
  4236. } // HandleFailedSendJob
  4237. //*********************************************************************************
  4238. //* Name: StartReceiveJob()
  4239. //* Author: Ronen Barenboim
  4240. //* Date: June 02, 1999
  4241. //*********************************************************************************
  4242. //* DESCRIPTION:
  4243. //* Starts a receive job on the specified device.
  4244. //* PARAMETERS:
  4245. //* [IN ] DWORD DeviceId
  4246. //* The permanent line id (not TAPI) of the device on which the fax is
  4247. //* to be received.
  4248. //*
  4249. //* RETURN VALUE:
  4250. //*
  4251. //*********************************************************************************
  4252. PJOB_ENTRY
  4253. StartReceiveJob(
  4254. DWORD DeviceId
  4255. )
  4256. {
  4257. BOOL Failure = TRUE;
  4258. PJOB_ENTRY JobEntry = NULL;
  4259. PLINE_INFO LineInfo;
  4260. BOOL bRes = FALSE;
  4261. DWORD rc = ERROR_SUCCESS;
  4262. DEBUG_FUNCTION_NAME(TEXT("StartRecieveJob"));
  4263. LineInfo = GetTapiLineForFaxOperation(
  4264. DeviceId,
  4265. JT_RECEIVE,
  4266. NULL
  4267. );
  4268. if (!LineInfo)
  4269. {
  4270. //
  4271. // Could not find a line to send the fax on.
  4272. //
  4273. rc = GetLastError();
  4274. DebugPrintEx(
  4275. DEBUG_WRN,
  4276. TEXT("Failed to find a line to send the fax on. (ec: %ld)"),
  4277. rc);
  4278. goto exit;
  4279. }
  4280. JobEntry = CreateJobEntry(NULL, LineInfo, FALSE);
  4281. if (!JobEntry)
  4282. {
  4283. rc = GetLastError();
  4284. DebugPrintEx(
  4285. DEBUG_ERR,
  4286. TEXT("Failed to create JobEntry. (ec: %ld)"),
  4287. rc);
  4288. goto exit;
  4289. }
  4290. __try
  4291. {
  4292. //
  4293. // Call the FSP associated with the line to start a fax job. Note that at this
  4294. // point it is not known if the job is send or receive.
  4295. //
  4296. bRes = LineInfo->Provider->FaxDevStartJob(
  4297. LineInfo->hLine,
  4298. LineInfo->DeviceId,
  4299. (PHANDLE) &JobEntry->InstanceData, // JOB_ENTRY.InstanceData is where the FSP will place its
  4300. // job handle (fax handle).
  4301. g_StatusCompletionPortHandle,
  4302. (ULONG_PTR) LineInfo ); // Note that the completion key provided to the FSP is the LineInfo
  4303. // pointer. When the FSP reports status it uses this key thus allowing
  4304. // us to know to which line the status belongs.
  4305. }
  4306. __except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, LineInfo->Provider->FriendlyName, GetExceptionCode()))
  4307. {
  4308. ASSERT_FALSE;
  4309. }
  4310. if (!bRes)
  4311. {
  4312. rc = GetLastError();
  4313. DebugPrintEx(DEBUG_ERR,TEXT("FaxDevStartJob failed (ec: %ld)"),GetLastError());
  4314. goto exit;
  4315. }
  4316. //
  4317. // Add the new JOB_ENTRY to the job list.
  4318. //
  4319. EnterCriticalSection( &g_CsJob );
  4320. JobEntry->bFSPJobInProgress = TRUE;
  4321. InsertTailList( &g_JobListHead, &JobEntry->ListEntry );
  4322. LeaveCriticalSection( &g_CsJob );
  4323. Failure = FALSE;
  4324. //
  4325. // Attach the job to the line selected to service it.
  4326. //
  4327. LineInfo->JobEntry = JobEntry;
  4328. exit:
  4329. if (Failure)
  4330. { // Failure is initialized to TRUE
  4331. if (LineInfo)
  4332. {
  4333. ReleaseTapiLine( LineInfo, 0 );
  4334. }
  4335. if (JobEntry)
  4336. {
  4337. EndJob(JobEntry);
  4338. }
  4339. JobEntry = NULL;
  4340. }
  4341. if (ERROR_SUCCESS != rc)
  4342. {
  4343. SetLastError(rc);
  4344. FaxLog(FAXLOG_CATEGORY_INBOUND,
  4345. FAXLOG_LEVEL_MIN,
  4346. 0,
  4347. MSG_FAX_RECEIVE_FAILED);
  4348. }
  4349. return JobEntry;
  4350. }
  4351. //*********************************************************************************
  4352. //* Name: StartRoutingJob()
  4353. //* Author: Mooly Beery (MoolyB)
  4354. //* Date: July 20, 2000
  4355. //*********************************************************************************
  4356. //* DESCRIPTION:
  4357. //* Starts a routing operation. Must lock g_CsJob and g_CsQueue.
  4358. //* PARAMETERS:
  4359. //* [IN/OUT ] PJOB_QUEUE lpJobQueueEntry
  4360. //* A pointer to the job for which the routing operation is to be
  4361. //* performed.
  4362. //*
  4363. //* RETURN VALUE:
  4364. //* TRUE
  4365. //* If the operation succeeded.
  4366. //* FALSE
  4367. //* If the operation failed. Call GetLastError() to get extended error
  4368. //* information.
  4369. //*
  4370. //*********************************************************************************
  4371. BOOL
  4372. StartRoutingJob(
  4373. PJOB_QUEUE lpJobQueueEntry
  4374. )
  4375. {
  4376. DWORD ec = ERROR_SUCCESS;
  4377. HANDLE hThread = NULL;
  4378. DWORD ThreadId;
  4379. DEBUG_FUNCTION_NAME(TEXT("StartRoutingJob"));
  4380. //
  4381. // We mark the job as IN_PROGRESS so it can not be deleted or routed simultaneously
  4382. //
  4383. lpJobQueueEntry->JobStatus = JS_INPROGRESS;
  4384. hThread = CreateThreadAndRefCount(
  4385. NULL,
  4386. 0,
  4387. (LPTHREAD_START_ROUTINE) FaxRouteThread,
  4388. (LPVOID) lpJobQueueEntry,
  4389. 0,
  4390. &ThreadId
  4391. );
  4392. if (hThread == NULL)
  4393. {
  4394. ec = GetLastError();
  4395. DebugPrintEx( DEBUG_ERR,
  4396. _T("CreateThreadAndRefCount for FaxRouteThread failed (ec: 0x%0X)"),
  4397. ec);
  4398. if (!MarkJobAsExpired(lpJobQueueEntry))
  4399. {
  4400. DEBUG_ERR,
  4401. TEXT("[JobId: %ld] MarkJobAsExpired failed (ec: %ld)"),
  4402. lpJobQueueEntry->JobId,
  4403. GetLastError();
  4404. }
  4405. SetLastError(ec);
  4406. return FALSE;
  4407. }
  4408. DebugPrintEx( DEBUG_MSG,
  4409. _T("FaxRouteThread thread created for job id %d ")
  4410. _T("(thread id: 0x%0x)"),
  4411. lpJobQueueEntry->JobId,
  4412. ThreadId);
  4413. CloseHandle( hThread );
  4414. //
  4415. // Create Fax EventEx
  4416. //
  4417. DWORD dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS,
  4418. lpJobQueueEntry);
  4419. if (ERROR_SUCCESS != dwRes)
  4420. {
  4421. DebugPrintEx( DEBUG_ERR,
  4422. _T("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) ")
  4423. _T("failed for job id %ld (ec: %ld)"),
  4424. lpJobQueueEntry->JobId,
  4425. dwRes);
  4426. }
  4427. return TRUE;
  4428. }
  4429. //*********************************************************************************
  4430. //* Name: StartSendJob()
  4431. //* Author: Ronen Barenboim
  4432. //* Date: June 02, 1999
  4433. //*********************************************************************************
  4434. //* DESCRIPTION:
  4435. //* Starts a send operation on a legacy of Extened FSP device.
  4436. //* PARAMETERS:
  4437. //* [IN/OUT ] PJOB_QUEUE lpJobQueueEntry
  4438. //* A pointer to the recipient job for which the send operation is to be
  4439. //* performed. For extended sends this is the Anchor recipient.
  4440. //*
  4441. //* [IN/OUT] PLINE_INFO lpLineInfo
  4442. //* A pointer to the line on which the send operatin is to be performed.
  4443. //*
  4444. //* RETURN VALUE:
  4445. //* TRUE
  4446. //* If the operation succeeded.
  4447. //* FALSE
  4448. //* If the operation failed. Call GetLastError() to get extended error
  4449. //* information.
  4450. //*
  4451. //*********************************************************************************
  4452. BOOL
  4453. StartSendJob(
  4454. PJOB_QUEUE lpJobQueueEntry,
  4455. PLINE_INFO lpLineInfo
  4456. )
  4457. {
  4458. DWORD rc = ERROR_SUCCESS;
  4459. DEBUG_FUNCTION_NAME(TEXT("StartSendJob"));
  4460. Assert(lpJobQueueEntry);
  4461. Assert(JT_SEND == lpJobQueueEntry->JobType);
  4462. Assert(lpLineInfo);
  4463. if (FSPI_API_VERSION_1 == lpLineInfo->Provider->dwAPIVersion)
  4464. {
  4465. if (!StartLegacySendJob(lpJobQueueEntry,lpLineInfo))
  4466. {
  4467. rc = GetLastError();
  4468. DebugPrintEx(
  4469. DEBUG_ERR,
  4470. TEXT("StartLegacySendJob() failed for JobId: %ld (ec: %ld)"),
  4471. lpJobQueueEntry->JobId,
  4472. GetLastError());
  4473. goto exit;
  4474. }
  4475. }
  4476. else
  4477. {
  4478. DebugPrintEx(
  4479. DEBUG_ERR,
  4480. TEXT("Unsupported FSPI version (0x%08X) for line : %s "),
  4481. lpLineInfo->Provider->dwAPIVersion,
  4482. lpLineInfo->DeviceName);
  4483. Assert(FALSE);
  4484. goto exit;
  4485. }
  4486. exit:
  4487. if (ERROR_SUCCESS != rc) {
  4488. SetLastError(rc);
  4489. TCHAR strJobID[20]={0};
  4490. //
  4491. // Convert Job ID into a string. (the string is 18 TCHARs long !!!)
  4492. //
  4493. HRESULT hr = StringCchPrintf(
  4494. strJobID,
  4495. ARR_SIZE(strJobID),
  4496. TEXT("0x%016I64x"),
  4497. lpJobQueueEntry->UniqueId);
  4498. if (FAILED(hr))
  4499. {
  4500. //
  4501. // Should never happen, we use large enough buffer.
  4502. //
  4503. ASSERT_FALSE;
  4504. }
  4505. FaxLog(
  4506. FAXLOG_CATEGORY_OUTBOUND,
  4507. FAXLOG_LEVEL_MIN,
  4508. 7,
  4509. MSG_FAX_SEND_FAILED,
  4510. lpJobQueueEntry->SenderProfile.lptstrName,
  4511. lpJobQueueEntry->SenderProfile.lptstrBillingCode,
  4512. lpJobQueueEntry->SenderProfile.lptstrCompany,
  4513. lpJobQueueEntry->SenderProfile.lptstrDepartment,
  4514. lpLineInfo->DeviceName,
  4515. strJobID,
  4516. lpJobQueueEntry->lpParentJob->UserName
  4517. );
  4518. }
  4519. return (0 == rc);
  4520. }
  4521. //*********************************************************************************
  4522. //* Name: StartLegacySendJob()
  4523. //* Author: Ronen Barenboim
  4524. //* Date: June 02, 1999
  4525. //*********************************************************************************
  4526. //* DESCRIPTION:
  4527. //* Starts the operation of sending a fax on a legacy FSP device.
  4528. //* - creates the JOB_ENTRY
  4529. //* - calls FaxDevStartJob()
  4530. //* - calls SendDocument() to actually send the document
  4531. //* - calls EndJob() if anything goes wrong.
  4532. //*
  4533. //* PARAMETERS:
  4534. //* [XXX] PJOB_QUEUE lpJobQueue
  4535. //* A pointer to the recipient job for the send operation is to be started.
  4536. //* [XXX] PLINE_INFO lpLineInfo
  4537. //* A pointer to the LINE_INFO of the line on which the fax is to be sent.
  4538. //*
  4539. //*
  4540. //* RETURN VALUE:
  4541. //* TRUE if the operation succeeded.
  4542. //* FALSE if it failed. Call GetLastError() to get extended error information.
  4543. //*
  4544. //*********************************************************************************
  4545. PJOB_ENTRY StartLegacySendJob(
  4546. PJOB_QUEUE lpJobQueue,
  4547. PLINE_INFO lpLineInfo
  4548. )
  4549. {
  4550. PJOB_ENTRY lpJobEntry = NULL;
  4551. DWORD rc = 0;
  4552. DWORD dwRes;
  4553. DEBUG_FUNCTION_NAME(TEXT("StartLegacySendJob"));
  4554. Assert(JT_SEND == lpJobQueue->JobType);
  4555. Assert(FSPI_API_VERSION_1 == lpLineInfo->Provider->dwAPIVersion);
  4556. lpJobEntry = CreateJobEntry(lpJobQueue, lpLineInfo, TRUE);
  4557. if (!lpJobEntry)
  4558. {
  4559. rc = GetLastError();
  4560. DebugPrintEx(
  4561. DEBUG_ERR,
  4562. TEXT("Failed to create JobEntry for JobId: %ld. (ec: %ld)"),
  4563. lpJobQueue->JobId,
  4564. rc);
  4565. goto Error;
  4566. }
  4567. lpJobQueue->JobStatus = JS_INPROGRESS;
  4568. //
  4569. // Add the new JOB_ENTRY to the job list.
  4570. //
  4571. EnterCriticalSection( &g_CsJob );
  4572. InsertTailList( &g_JobListHead, &lpJobEntry->ListEntry );
  4573. LeaveCriticalSection( &g_CsJob );
  4574. //
  4575. // Attach the job to the line selected to service it.
  4576. //
  4577. lpLineInfo->JobEntry = lpJobEntry;
  4578. lpJobQueue->JobEntry = lpJobEntry;
  4579. __try
  4580. {
  4581. //
  4582. // Call the FSP associated with the line to start a fax job. Note that at this
  4583. // point it is not known if the job is send or receive.
  4584. //
  4585. if (lpLineInfo->Provider->FaxDevStartJob(
  4586. lpLineInfo->hLine,
  4587. lpLineInfo->DeviceId,
  4588. (PHANDLE) &lpJobEntry->InstanceData, // JOB_ENTRY.InstanceData is where the FSP will place its
  4589. // job handle (fax handle).
  4590. g_StatusCompletionPortHandle,
  4591. (ULONG_PTR) lpLineInfo )) // Note that the completion key provided to the FSP is the LineInfo
  4592. // pointer. When the FSP reports status it uses this key thus allowing
  4593. // us to know to which line the status belongs.
  4594. {
  4595. DebugPrintEx(
  4596. DEBUG_MSG,
  4597. TEXT("FaxDevStartJob() Successfuly called for JobId: %ld)"),
  4598. lpJobQueue->JobId);
  4599. lpJobEntry->bFSPJobInProgress = TRUE;
  4600. }
  4601. else
  4602. {
  4603. rc = GetLastError();
  4604. DebugPrintEx(DEBUG_ERR,TEXT("FaxDevStartJob() failed (ec: %ld)"),rc);
  4605. if (0 == rc)
  4606. {
  4607. //
  4608. // FSP failed to report last error so we set our own.
  4609. //
  4610. DebugPrintEx(DEBUG_ERR,TEXT("FaxDevStartJob() failed but reported 0 for last error"));
  4611. rc = ERROR_GEN_FAILURE;
  4612. }
  4613. goto Error;
  4614. }
  4615. }
  4616. __except (HandleFaxExtensionFault(EXCEPTION_SOURCE_FSP, lpLineInfo->Provider->FriendlyName, GetExceptionCode()))
  4617. {
  4618. ASSERT_FALSE;
  4619. }
  4620. //
  4621. // start the send job
  4622. //
  4623. rc = SendDocument(
  4624. lpJobEntry,
  4625. lpJobQueue->FileName
  4626. );
  4627. if (rc)
  4628. {
  4629. DebugPrintEx(
  4630. DEBUG_ERR,
  4631. TEXT("SendDocument failed for JobId: %ld (ec: %ld)"),
  4632. lpJobQueue->JobId,
  4633. rc);
  4634. goto Error;
  4635. }
  4636. Assert (0 == rc);
  4637. goto Exit;
  4638. Error:
  4639. Assert( 0 != rc);
  4640. if (lpJobEntry)
  4641. {
  4642. if (!EndJob(lpJobEntry))
  4643. {
  4644. DebugPrintEx(
  4645. DEBUG_ERR,
  4646. TEXT("EndJob() failed for JobId: %ld (ec: %ld)"),
  4647. lpJobQueue->JobId,
  4648. GetLastError());
  4649. }
  4650. lpJobEntry = NULL;
  4651. lpJobQueue->JobEntry = NULL;
  4652. }
  4653. else
  4654. {
  4655. //
  4656. // Release the line
  4657. //
  4658. if (!ReleaseTapiLine(lpLineInfo, NULL))
  4659. {
  4660. DebugPrintEx(
  4661. DEBUG_ERR,
  4662. TEXT("ReleaseTapiLine() failed (ec: %ld)"),
  4663. GetLastError());
  4664. }
  4665. }
  4666. //
  4667. // set the job into the retries exceeded state
  4668. //
  4669. if (0 == lpJobQueue->dwLastJobExtendedStatus)
  4670. {
  4671. //
  4672. // Job was never really executed - this is a fatal error
  4673. //
  4674. lpJobQueue->dwLastJobExtendedStatus = FSPI_ES_FATAL_ERROR;
  4675. lpJobQueue->ExStatusString[0] = L'\0';
  4676. }
  4677. if (!MarkJobAsExpired(lpJobQueue))
  4678. {
  4679. DebugPrintEx(
  4680. DEBUG_ERR,
  4681. TEXT("[JobId: %ld] MarkJobAsExpired failed (ec: %ld)"),
  4682. lpJobQueue->JobId,
  4683. GetLastError());
  4684. }
  4685. //
  4686. // Notify clients on status change
  4687. //
  4688. dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS, lpJobQueue);
  4689. if (ERROR_SUCCESS != dwRes)
  4690. {
  4691. DebugPrintEx(
  4692. DEBUG_ERR,
  4693. _T("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) failed for job id %ld (ec: %lc)"),
  4694. lpJobQueue->UniqueId,
  4695. dwRes);
  4696. }
  4697. Exit:
  4698. if (rc)
  4699. {
  4700. SetLastError(rc);
  4701. }
  4702. return lpJobEntry;
  4703. }
  4704. //*********************************************************************************
  4705. //* Name: UpdateJobStatus()
  4706. //* Author: Ronen Barenboim
  4707. //* Date: June 01, 1999
  4708. //*********************************************************************************
  4709. //* DESCRIPTION:
  4710. //* Updated the FSPI job status kept in the job entry.
  4711. //* Generates legacy API event and new events as required.
  4712. //* PARAMETERS:
  4713. //* [OUT] PJOB_ENTRY lpJobEntry
  4714. //* The job entry whose FSPI status is to be udpated.
  4715. //*
  4716. //* [IN] LPCFSPI_JOB_STATUS lpcFSPJobStatus
  4717. //* The new FSPI job status.
  4718. //*
  4719. //* RETURN VALUE:
  4720. //* TRUE if the operation succeeded.
  4721. //* FALSE if the operation failed. Call GetLastError() to get extended error
  4722. //* information.
  4723. //* Remarks:
  4724. //* The function fress the last FSPI job status held in the job entry
  4725. //* (if any).
  4726. //*********************************************************************************
  4727. BOOL UpdateJobStatus(
  4728. PJOB_ENTRY lpJobEntry,
  4729. LPCFSPI_JOB_STATUS lpcFSPJobStatus
  4730. )
  4731. {
  4732. DWORD ec = 0;
  4733. DWORD dwEventId;
  4734. DWORD Size = 0;
  4735. HINSTANCE hLoadInstance = NULL;
  4736. DEBUG_FUNCTION_NAME(TEXT("UpdateJobStatus"));
  4737. Assert(lpJobEntry);
  4738. Assert(lpcFSPJobStatus);
  4739. Assert (lpJobEntry->lpJobQueueEntry);
  4740. EnterCriticalSection( &g_CsJob );
  4741. DebugPrintEx(
  4742. DEBUG_MSG,
  4743. TEXT("dwJobStatus: 0x%08X dwExtendedStatus: 0x%08X"),
  4744. lpcFSPJobStatus->dwJobStatus,
  4745. lpcFSPJobStatus->dwExtendedStatus
  4746. );
  4747. if (TRUE == lpJobEntry->fStopUpdateStatus)
  4748. {
  4749. DebugPrintEx(
  4750. DEBUG_WRN,
  4751. TEXT("JobId: %ld. fStopUpdateStatus was set. Not updating status"),
  4752. lpJobEntry->lpJobQueueEntry->JobId,
  4753. lpJobEntry->lpJobQueueEntry->JobStatus);
  4754. LeaveCriticalSection (&g_CsJob);
  4755. return TRUE;
  4756. }
  4757. //
  4758. // Map the FSPI job status to an FEI_* event (0 if not event matches the status)
  4759. //
  4760. dwEventId = MapFSPIJobStatusToEventId(lpcFSPJobStatus);
  4761. //
  4762. // Note: W2K Fax did issue notifications with EventId == 0 whenever an
  4763. // FSP reported proprietry status code. To keep backward compatability
  4764. // we keep up this behaviour although it might be regarded as a bug
  4765. //
  4766. if (!CreateFaxEvent( lpJobEntry->LineInfo->PermanentLineID, dwEventId, lpJobEntry->lpJobQueueEntry->JobId ))
  4767. {
  4768. if ( TRUE == g_bServiceIsDown)
  4769. {
  4770. DebugPrintEx(
  4771. DEBUG_WRN,
  4772. TEXT("CreateFaxEvent() failed. Event: 0x%08X JobId: %ld DeviceId: (Service is going down)"),
  4773. dwEventId,
  4774. lpJobEntry->lpJobQueueEntry->JobId,
  4775. lpJobEntry->LineInfo->PermanentLineID
  4776. );
  4777. }
  4778. else
  4779. {
  4780. DebugPrintEx(
  4781. DEBUG_ERR,
  4782. TEXT("CreateFaxEvent() failed. Event: 0x%08X JobId: %ld DeviceId: (ec: %ld)"),
  4783. dwEventId,
  4784. lpJobEntry->lpJobQueueEntry->JobId,
  4785. lpJobEntry->LineInfo->PermanentLineID,
  4786. GetLastError());
  4787. Assert(FALSE);
  4788. }
  4789. }
  4790. lpJobEntry->FSPIJobStatus.dwJobStatus = lpcFSPJobStatus->dwJobStatus;
  4791. lpJobEntry->FSPIJobStatus.dwExtendedStatus = lpcFSPJobStatus->dwExtendedStatus;
  4792. lpJobEntry->FSPIJobStatus.dwExtendedStatusStringId = lpcFSPJobStatus->dwExtendedStatusStringId;
  4793. if (lpcFSPJobStatus->fAvailableStatusInfo & FSPI_JOB_STATUS_INFO_PAGECOUNT)
  4794. {
  4795. lpJobEntry->FSPIJobStatus.dwPageCount = lpcFSPJobStatus->dwPageCount;
  4796. lpJobEntry->FSPIJobStatus.fAvailableStatusInfo |= FSPI_JOB_STATUS_INFO_PAGECOUNT;
  4797. }
  4798. if (lpcFSPJobStatus->fAvailableStatusInfo & FSPI_JOB_STATUS_INFO_TRANSMISSION_START)
  4799. {
  4800. lpJobEntry->FSPIJobStatus.tmTransmissionStart = lpcFSPJobStatus->tmTransmissionStart;
  4801. lpJobEntry->FSPIJobStatus.fAvailableStatusInfo |= FSPI_JOB_STATUS_INFO_TRANSMISSION_START;
  4802. }
  4803. if (lpcFSPJobStatus->fAvailableStatusInfo & FSPI_JOB_STATUS_INFO_TRANSMISSION_END)
  4804. {
  4805. lpJobEntry->FSPIJobStatus.tmTransmissionEnd = lpcFSPJobStatus->tmTransmissionEnd;
  4806. lpJobEntry->FSPIJobStatus.fAvailableStatusInfo |= FSPI_JOB_STATUS_INFO_TRANSMISSION_END;
  4807. }
  4808. if (NULL != lpcFSPJobStatus->lpwstrRemoteStationId)
  4809. {
  4810. if (!ReplaceStringWithCopy(&lpJobEntry->FSPIJobStatus.lpwstrRemoteStationId,
  4811. lpcFSPJobStatus->lpwstrRemoteStationId))
  4812. {
  4813. DebugPrintEx(
  4814. DEBUG_ERR,
  4815. TEXT("ReplaceStringWithCopy() failed. (ec: %ld)"),
  4816. GetLastError());
  4817. }
  4818. }
  4819. if (NULL != lpcFSPJobStatus->lpwstrCallerId)
  4820. {
  4821. if (!ReplaceStringWithCopy(&lpJobEntry->FSPIJobStatus.lpwstrCallerId,
  4822. lpcFSPJobStatus->lpwstrCallerId))
  4823. {
  4824. DebugPrintEx(
  4825. DEBUG_ERR,
  4826. TEXT("ReplaceStringWithCopy() failed. (ec: %ld)"),
  4827. GetLastError());
  4828. }
  4829. }
  4830. if (NULL != lpcFSPJobStatus->lpwstrRoutingInfo)
  4831. {
  4832. if (!ReplaceStringWithCopy(&lpJobEntry->FSPIJobStatus.lpwstrRoutingInfo,
  4833. lpcFSPJobStatus->lpwstrRoutingInfo))
  4834. {
  4835. DebugPrintEx(
  4836. DEBUG_ERR,
  4837. TEXT("ReplaceStringWithCopy() failed. (ec: %ld)"),
  4838. GetLastError());
  4839. }
  4840. }
  4841. lpJobEntry->ExStatusString[0] = L'\0';
  4842. //
  4843. // Get extended status string
  4844. //
  4845. Assert (lpJobEntry->LineInfo != NULL)
  4846. if (lpJobEntry->FSPIJobStatus.dwExtendedStatusStringId != 0)
  4847. {
  4848. Assert (lpJobEntry->FSPIJobStatus.dwExtendedStatus != 0);
  4849. if ( !_tcsicmp(lpJobEntry->LineInfo->Provider->szGUID,REGVAL_T30_PROVIDER_GUID_STRING) )
  4850. { // special case where the FSP is our FSP (fxst30.dll).
  4851. hLoadInstance = g_hResource;
  4852. }
  4853. else
  4854. {
  4855. hLoadInstance = lpJobEntry->LineInfo->Provider->hModule;
  4856. }
  4857. Size = LoadString (hLoadInstance,
  4858. lpJobEntry->FSPIJobStatus.dwExtendedStatusStringId,
  4859. lpJobEntry->ExStatusString,
  4860. sizeof(lpJobEntry->ExStatusString)/sizeof(WCHAR));
  4861. if (Size == 0)
  4862. {
  4863. ec = GetLastError();
  4864. DebugPrintEx(
  4865. DEBUG_ERR,
  4866. TEXT("Failed to load extended status string (ec: %ld) stringid : %ld, Provider: %s"),
  4867. ec,
  4868. lpJobEntry->FSPIJobStatus.dwExtendedStatusStringId,
  4869. lpJobEntry->LineInfo->Provider->ImageName);
  4870. lpJobEntry->FSPIJobStatus.fAvailableStatusInfo &= ~FSPI_JOB_STATUS_INFO_FSP_PRIVATE_STATUS_CODE;
  4871. lpJobEntry->FSPIJobStatus.dwExtendedStatusStringId = 0;
  4872. lpJobEntry->FSPIJobStatus.dwExtendedStatus = 0;
  4873. goto Error;
  4874. }
  4875. }
  4876. EnterCriticalSection (&g_CsQueue);
  4877. DWORD dwRes = CreateQueueEvent ( FAX_JOB_EVENT_TYPE_STATUS,
  4878. lpJobEntry->lpJobQueueEntry);
  4879. if (ERROR_SUCCESS != dwRes)
  4880. {
  4881. DebugPrintEx(
  4882. DEBUG_ERR,
  4883. TEXT("CreateQueueEvent(FAX_JOB_EVENT_TYPE_STATUS) failed for job id %ld (ec: %lc)"),
  4884. lpJobEntry->lpJobQueueEntry->UniqueId,
  4885. dwRes);
  4886. }
  4887. LeaveCriticalSection (&g_CsQueue);
  4888. Assert (0 == ec);
  4889. goto Exit;
  4890. Error:
  4891. Assert( ec !=0 );
  4892. Exit:
  4893. LeaveCriticalSection( &g_CsJob );
  4894. if (ec)
  4895. {
  4896. SetLastError(ec);
  4897. }
  4898. return (0 == ec);
  4899. }
  4900. //*********************************************************************************
  4901. //* Name: CheckForJobRetry
  4902. //* Author: Ronen Barenboim
  4903. //* Date: June 01, 1999
  4904. //*********************************************************************************
  4905. //* DESCRIPTION:
  4906. //* Checks if a recipient job should be retried.
  4907. //* Increments the retry count and marks the job as expired if it passed
  4908. //* the retry limit.
  4909. //* PARAMETERS:
  4910. //* [IN/OUT] PJOB_QUEUE lpJobQueue
  4911. //* A pointer to the JOB_QUEUE structure of the recipient job.
  4912. //* RETURN VALUE:
  4913. //* TRUE if the job is to be retried.
  4914. //* FALSE if it is not to be retried.
  4915. //*********************************************************************************
  4916. BOOL CheckForJobRetry (PJOB_QUEUE lpJobQueue)
  4917. {
  4918. PJOB_ENTRY lpJobEntry;
  4919. DWORD dwMaxRetries;
  4920. DEBUG_FUNCTION_NAME(TEXT("CheckForJobRetry"));
  4921. Assert(lpJobQueue);
  4922. lpJobEntry = lpJobQueue->JobEntry;
  4923. Assert(lpJobEntry);
  4924. //
  4925. // Increase the retry count and check if we exceeded maximum retries.
  4926. //
  4927. EnterCriticalSection (&g_CsConfig);
  4928. dwMaxRetries = g_dwFaxSendRetries;
  4929. LeaveCriticalSection (&g_CsConfig);
  4930. lpJobQueue->SendRetries++;
  4931. if (lpJobQueue->SendRetries <= dwMaxRetries)
  4932. {
  4933. return TRUE;
  4934. }
  4935. else
  4936. {
  4937. //
  4938. // retries exceeded report that the job is not to be retried
  4939. return FALSE;
  4940. }
  4941. }
  4942. //*********************************************************************************
  4943. //* Name: FindJobEntryByRecipientNumber()
  4944. //* Author: Ronen Barenboim
  4945. //* Date: June 01, 1999
  4946. //*********************************************************************************
  4947. //* DESCRIPTION:
  4948. //* Finds the first running job that is destined to a certain number.
  4949. //*
  4950. //* PARAMETERS:
  4951. //* [IN ] LPTSTR lptstrNumber
  4952. //* The number to match. This must be in canonical form.
  4953. //*
  4954. //* RETURN VALUE:
  4955. //* A pointer to the JOB_ENTRY in the g_JobListHead list that is destined to
  4956. //* the specified number.
  4957. //* If no such job is found the return value is NULL.
  4958. //*********************************************************************************
  4959. PJOB_ENTRY FindJobEntryByRecipientNumber(LPCWSTR lpcwstrNumber)
  4960. {
  4961. PLIST_ENTRY lpNext;
  4962. PJOB_ENTRY lpJobEntry;
  4963. DEBUG_FUNCTION_NAME(TEXT("FindJobEntryByRecipientNumber"));
  4964. Assert(lpcwstrNumber);
  4965. lpNext = g_JobListHead.Flink;
  4966. Assert(lpNext);
  4967. while ((ULONG_PTR)lpNext != (ULONG_PTR)&g_JobListHead) {
  4968. lpJobEntry = CONTAINING_RECORD( lpNext, JOB_ENTRY, ListEntry );
  4969. lpNext = lpJobEntry->ListEntry.Flink;
  4970. if (JT_SEND == lpJobEntry->lpJobQueueEntry->JobType)
  4971. {
  4972. if (!_wcsicmp(lpJobEntry->lpJobQueueEntry->RecipientProfile.lptstrFaxNumber, lpcwstrNumber))
  4973. {
  4974. return lpJobEntry;
  4975. }
  4976. }
  4977. }
  4978. return NULL;
  4979. }
  4980. BOOL CreateJobQueueThread(void)
  4981. {
  4982. DWORD ThreadId;
  4983. DWORD ec = ERROR_SUCCESS;
  4984. DEBUG_FUNCTION_NAME(TEXT("CreateJobQueueThread"));
  4985. g_hJobQueueThread = CreateThreadAndRefCount(
  4986. NULL,
  4987. 0,
  4988. (LPTHREAD_START_ROUTINE) JobQueueThread,
  4989. NULL,
  4990. 0,
  4991. &ThreadId
  4992. );
  4993. if (NULL == g_hJobQueueThread)
  4994. {
  4995. DebugPrintEx(
  4996. DEBUG_ERR,
  4997. TEXT("Failed to create JobQueueThread (ec: %ld)."),
  4998. GetLastError());
  4999. goto Error;
  5000. }
  5001. Assert( ERROR_SUCCESS == ec);
  5002. goto Exit;
  5003. Error:
  5004. Assert (ERROR_SUCCESS != ec);
  5005. //
  5006. // We don't close the already created threads. (They are terminated on process exit).
  5007. //
  5008. Exit:
  5009. if (ERROR_SUCCESS != ec)
  5010. {
  5011. SetLastError(ec);
  5012. }
  5013. return (ERROR_SUCCESS == ec);
  5014. }
  5015. BOOL CreateStatusThreads(void)
  5016. {
  5017. int i;
  5018. DWORD ThreadId;
  5019. DWORD ec = ERROR_SUCCESS;
  5020. HANDLE hStatusThreads[MAX_STATUS_THREADS];
  5021. DEBUG_FUNCTION_NAME(TEXT("CreateStatusThreads"));
  5022. memset(hStatusThreads, 0, sizeof(HANDLE)*MAX_STATUS_THREADS);
  5023. for (i=0; i<MAX_STATUS_THREADS; i++) {
  5024. hStatusThreads[i] = CreateThreadAndRefCount(
  5025. NULL,
  5026. 0,
  5027. (LPTHREAD_START_ROUTINE) FaxStatusThread,
  5028. NULL,
  5029. 0,
  5030. &ThreadId
  5031. );
  5032. if (!hStatusThreads[i]) {
  5033. ec = GetLastError();
  5034. DebugPrintEx(
  5035. DEBUG_ERR,
  5036. TEXT("Failed to create status thread %d (CreateThreadAndRefCount)(ec=0x%08x)."),
  5037. i,
  5038. ec);
  5039. goto Error;
  5040. }
  5041. }
  5042. Assert (ERROR_SUCCESS == ec);
  5043. goto Exit;
  5044. Error:
  5045. Assert (ERROR_SUCCESS != ec);
  5046. Exit:
  5047. //
  5048. // Close the thread handles we no longer need them
  5049. //
  5050. for (i=0; i<MAX_STATUS_THREADS; i++)
  5051. {
  5052. if(NULL == hStatusThreads[i])
  5053. {
  5054. continue;
  5055. }
  5056. if (!CloseHandle(hStatusThreads[i]))
  5057. {
  5058. DebugPrintEx(
  5059. DEBUG_ERR,
  5060. TEXT("Failed to close thread handle at index %ld [handle = 0x%08X] (ec=0x%08x)."),
  5061. i,
  5062. hStatusThreads[i],
  5063. GetLastError());
  5064. }
  5065. }
  5066. if (ec)
  5067. {
  5068. SetLastError(ec);
  5069. }
  5070. return (ERROR_SUCCESS == ec);
  5071. }
  5072. static
  5073. BOOL
  5074. SendJobReceipt (
  5075. BOOL bPositive,
  5076. JOB_QUEUE * lpJobQueue,
  5077. LPCTSTR lpctstrAttachment
  5078. )
  5079. /*++
  5080. Routine name : SendJobReceipt
  5081. Routine description:
  5082. Determines if a receipts should be send and calls SendReceipt accordingly
  5083. Author:
  5084. Eran Yariv (EranY), Feb, 2000
  5085. Arguments:
  5086. bPositive [in] - Did current job ended successfully?
  5087. lpJobQueue [in] - Pointer to recipient job that just ended
  5088. lpctstrAttachment [in] - Job TIFF file to attach (in case of single recipient job only)
  5089. Return Value:
  5090. TRUE if successful, FALSE otherwise.
  5091. In case of failure, call GetLastError() to obtain error code.
  5092. --*/
  5093. {
  5094. BOOL bSingleJobReceipt = FALSE;
  5095. DEBUG_FUNCTION_NAME(TEXT("SendJobReceipt)"));
  5096. if (lpJobQueue->lpParentJob->dwRecipientJobsCount > 1)
  5097. {
  5098. //
  5099. // Broadcast case
  5100. //
  5101. if (lpJobQueue->JobParamsEx.dwReceiptDeliveryType & DRT_GRP_PARENT)
  5102. {
  5103. //
  5104. // Broadcast receipt grouping is requested
  5105. //
  5106. if (IsSendJobReadyForDeleting (lpJobQueue))
  5107. {
  5108. //
  5109. // This is the last job in the broadcast, it's time to send a broadcast receipt
  5110. //
  5111. //
  5112. // As receipt sending is async, there still might be a chance that more than one recipient jobs will reach this point
  5113. // We must verify that only one receipt is sent per broadcast job
  5114. //
  5115. EnterCriticalSection (&g_CsQueue);
  5116. if (FALSE == lpJobQueue->lpParentJob->fReceiptSent)
  5117. {
  5118. PJOB_QUEUE pParentJob = lpJobQueue->lpParentJob;
  5119. BOOL bPositiveBroadcast =
  5120. (pParentJob->dwCompletedRecipientJobsCount == pParentJob->dwRecipientJobsCount) ?
  5121. TRUE : FALSE;
  5122. //
  5123. // set the flag so we will not send duplicate receipts for broadcast
  5124. //
  5125. lpJobQueue->lpParentJob->fReceiptSent = TRUE;
  5126. //
  5127. // Leave g_CsQueue so we will not block the service
  5128. //
  5129. LeaveCriticalSection (&g_CsQueue);
  5130. if (!SendReceipt(bPositiveBroadcast,
  5131. TRUE,
  5132. pParentJob,
  5133. pParentJob->FileName))
  5134. {
  5135. DebugPrintEx(
  5136. DEBUG_ERR,
  5137. TEXT("[Job Id: %ld] Failed to send broadcast receipt. (ec: %ld)"),
  5138. lpJobQueue->JobId,
  5139. GetLastError());
  5140. return FALSE;
  5141. }
  5142. }
  5143. else
  5144. {
  5145. //
  5146. // More than one job reached this point when the broadcast jo was ready for deleting.
  5147. // Only on receipt is sent
  5148. //
  5149. LeaveCriticalSection (&g_CsQueue);
  5150. }
  5151. }
  5152. else
  5153. {
  5154. //
  5155. // More jobs are still not finished, do not send receipt
  5156. //
  5157. }
  5158. }
  5159. else
  5160. {
  5161. //
  5162. // This is a recipient part of a broadcast but the user was
  5163. // asking for a receipt for every recipient.
  5164. //
  5165. bSingleJobReceipt = TRUE;
  5166. }
  5167. }
  5168. else
  5169. {
  5170. //
  5171. // This is not a broadcast case
  5172. //
  5173. bSingleJobReceipt = TRUE;
  5174. }
  5175. if (bSingleJobReceipt)
  5176. {
  5177. //
  5178. // Send receipt for this job only
  5179. //
  5180. if (!SendReceipt(bPositive, FALSE, lpJobQueue, lpctstrAttachment))
  5181. {
  5182. DebugPrintEx(
  5183. DEBUG_ERR,
  5184. TEXT("[Job Id: %ld] Failed to send POSITIVE receipt. (ec: %ld)"),
  5185. lpJobQueue->JobId,
  5186. GetLastError());
  5187. return FALSE;
  5188. }
  5189. }
  5190. return TRUE;
  5191. } // SendJobReceipt
  5192. VOID
  5193. UpdateDeviceJobsCounter (
  5194. PLINE_INFO pLine,
  5195. BOOL bSend,
  5196. int iInc,
  5197. BOOL bNotify
  5198. )
  5199. /*++
  5200. Routine name : UpdateDeviceJobsCounter
  5201. Routine description:
  5202. Updates the send or receive jobs counter of a device
  5203. Author:
  5204. Eran Yariv (EranY), Jul, 2000
  5205. Arguments:
  5206. pLine [in] - Device pointer
  5207. bSend [in] - Send counter (FALSE = Receive counter)
  5208. iInc [in] - Increase jobs count (negative means decrease)
  5209. decrease [in] - Allow events (FAX_EVENT_TYPE_DEVICE_STATUS)
  5210. Return Value:
  5211. None.
  5212. --*/
  5213. {
  5214. DWORD dwOldCount;
  5215. DWORD dwNewCount;
  5216. DEBUG_FUNCTION_NAME(TEXT("UpdateDeviceJobsCounter)"));
  5217. Assert (pLine);
  5218. if (!iInc)
  5219. {
  5220. //
  5221. // No change
  5222. //
  5223. ASSERT_FALSE;
  5224. return;
  5225. }
  5226. EnterCriticalSection (&g_CsLine);
  5227. dwOldCount = bSend ? pLine->dwSendingJobsCount : pLine->dwReceivingJobsCount;
  5228. if (0 > iInc)
  5229. {
  5230. //
  5231. // Decrease case
  5232. //
  5233. if ((int)dwOldCount + iInc < 0)
  5234. {
  5235. //
  5236. // Weird - should never happen
  5237. //
  5238. ASSERT_FALSE;
  5239. iInc = -(int)dwOldCount;
  5240. }
  5241. }
  5242. dwNewCount = (DWORD)((int)dwOldCount + iInc);
  5243. if (bSend)
  5244. {
  5245. pLine->dwSendingJobsCount = dwNewCount;
  5246. }
  5247. else
  5248. {
  5249. pLine->dwReceivingJobsCount = dwNewCount;
  5250. }
  5251. LeaveCriticalSection (&g_CsLine);
  5252. if (bNotify && ((0 == dwNewCount) || (0 == dwOldCount)))
  5253. {
  5254. //
  5255. // State change
  5256. //
  5257. DWORD ec = CreateDeviceEvent (pLine, FALSE);
  5258. if (ERROR_SUCCESS != ec)
  5259. {
  5260. DebugPrintEx(
  5261. DEBUG_ERR,
  5262. TEXT("CreateDeviceEvent() (ec: %lc)"),
  5263. ec);
  5264. }
  5265. }
  5266. } // UpdateDeviceJobsCounter