Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1896 lines
51 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 managegement.
  9. Author:
  10. Wesley Witt (wesw) 24-Jan-1996
  11. Revision History:
  12. --*/
  13. #include "faxsvc.h"
  14. #pragma hdrstop
  15. LIST_ENTRY JobListHead;
  16. CRITICAL_SECTION CsJob;
  17. HANDLE StatusCompletionPortHandle;
  18. DWORD FaxSendRetries;
  19. DWORD FaxSendRetryDelay;
  20. DWORD FaxDirtyDays;
  21. BOOL FaxUseDeviceTsid;
  22. BOOL FaxUseBranding;
  23. BOOL ServerCp;
  24. FAX_TIME StartCheapTime;
  25. FAX_TIME StopCheapTime;
  26. BOOL ArchiveOutgoingFaxes;
  27. LPTSTR ArchiveDirectory;
  28. DWORD NextJobId;
  29. BOOL ForceReceive;
  30. DWORD TerminationDelay;
  31. extern HANDLE hServiceEndEvent; // signal this after letting clients know fax service is ending
  32. PJOB_ENTRY
  33. FindJob(
  34. IN HANDLE FaxHandle
  35. )
  36. /*++
  37. Routine Description:
  38. This fuction locates a FAX job by matching
  39. the FAX handle value.
  40. Arguments:
  41. FaxHandle - FAX handle returned from startjob
  42. Return Value:
  43. NULL for failure.
  44. Valid pointer to a JOB_ENTRY on success.
  45. --*/
  46. {
  47. PLIST_ENTRY Next;
  48. PJOB_ENTRY JobEntry;
  49. EnterCriticalSection( &CsJob );
  50. Next = JobListHead.Flink;
  51. if (Next == NULL) {
  52. LeaveCriticalSection( &CsJob );
  53. return NULL;
  54. }
  55. while ((ULONG_PTR)Next != (ULONG_PTR)&JobListHead) {
  56. JobEntry = CONTAINING_RECORD( Next, JOB_ENTRY, ListEntry );
  57. if (JobEntry->InstanceData == (ULONG_PTR) FaxHandle) {
  58. LeaveCriticalSection( &CsJob );
  59. return JobEntry;
  60. }
  61. Next = JobEntry->ListEntry.Flink;
  62. }
  63. LeaveCriticalSection( &CsJob );
  64. return NULL;
  65. }
  66. BOOL
  67. FindJobByJob(
  68. IN PJOB_ENTRY JobEntryToFind
  69. )
  70. /*++
  71. Routine Description:
  72. This fuction locates a FAX job by matching
  73. the FAX handle value.
  74. Arguments:
  75. FaxHandle - FAX handle returned from startjob
  76. Return Value:
  77. NULL for failure.
  78. Valid pointer to a JOB_ENTRY on success.
  79. --*/
  80. {
  81. PLIST_ENTRY Next;
  82. PJOB_ENTRY JobEntry;
  83. EnterCriticalSection( &CsJob );
  84. Next = JobListHead.Flink;
  85. if (Next == NULL) {
  86. LeaveCriticalSection( &CsJob );
  87. return FALSE;
  88. }
  89. while ((ULONG_PTR)Next != (ULONG_PTR)&JobListHead) {
  90. JobEntry = CONTAINING_RECORD( Next, JOB_ENTRY, ListEntry );
  91. if (JobEntry == JobEntryToFind) {
  92. LeaveCriticalSection( &CsJob );
  93. return TRUE;
  94. }
  95. Next = JobEntry->ListEntry.Flink;
  96. }
  97. LeaveCriticalSection( &CsJob );
  98. return FALSE;
  99. }
  100. BOOL
  101. FaxSendCallback(
  102. IN HANDLE FaxHandle,
  103. IN HCALL CallHandle,
  104. IN DWORD Reserved1,
  105. IN DWORD Reserved2
  106. )
  107. /*++
  108. Routine Description:
  109. This fuction is called asychronously by a FAX device
  110. provider after a call is established. The sole purpose
  111. of the callback is to communicate the call handle from the
  112. device provider to the FAX service.
  113. Arguments:
  114. FaxHandle - FAX handle returned from startjob
  115. CallHandle - Call handle for newly initiated call
  116. Reserved1 - Always zero.
  117. Reserved2 - Always zero.
  118. Return Value:
  119. TRUE for success, FAX operation continues.
  120. FALSE for failure, FAX operation is terminated.
  121. --*/
  122. {
  123. PJOB_ENTRY JobEntry;
  124. JobEntry = FindJob( FaxHandle );
  125. if (!JobEntry) {
  126. return FALSE;
  127. }
  128. JobEntry->CallHandle = CallHandle;
  129. return TRUE;
  130. }
  131. DWORD
  132. FaxSendThread(
  133. PFAX_SEND_ITEM FaxSendItem
  134. )
  135. /*++
  136. Routine Description:
  137. This fuction runs asychronously as a separate thread to
  138. send a FAX document. There is one send thread per outstanding
  139. FAX send operation. The thread ends when the document is
  140. either successfuly sent or the operation is aborted.
  141. Arguments:
  142. FaxSendItem - pointer to a FAX send item packet that
  143. describes the requested FAX send operation.
  144. Return Value:
  145. Always zero.
  146. --*/
  147. {
  148. FAX_SEND FaxSend;
  149. PFAX_DEV_STATUS FaxStatus = NULL;
  150. DWORD StatusSize;
  151. BOOL Rslt;
  152. DWORD BytesNeeded;
  153. BOOL Retrying = FALSE;
  154. BOOL Archived;
  155. TCHAR PageCountStr[64];
  156. TCHAR TimeStr[128];
  157. LPDWORD MsgPtr[6];
  158. TCHAR MsgStr[2048];
  159. DWORD MsgCount;
  160. FILETIME LocalTime;
  161. TCHAR lpDate[50];
  162. int lenDate;
  163. TCHAR lpTime[50];
  164. int lenTime;
  165. TCHAR lpDateTime[104];
  166. TCHAR lpCallerNumberPlusCompanyName[200];
  167. DWORD lenCallerNumberPlusCompanyName;
  168. DWORD delta;
  169. BOOL HandoffJob;
  170. TCHAR lpBranding[400];
  171. DWORD lenBranding;
  172. TCHAR lpBrandingEnd[50];
  173. DWORD lenBrandingEnd;
  174. DWORD BrandingMaxLen = 115;
  175. INT BrandingHeight = 22; // in scan lines.
  176. DWORD PageCount = 0;
  177. //
  178. // allocate memory for the status packet
  179. // this is a variable size packet based
  180. // on the size of the strings contained
  181. // withing the packet.
  182. //
  183. StatusSize = sizeof(FAX_DEV_STATUS) + FAXDEVREPORTSTATUS_SIZE;
  184. FaxStatus = (PFAX_DEV_STATUS) MemAlloc( StatusSize );
  185. if (!FaxStatus) {
  186. DebugPrint(( TEXT("FaxSendThread exiting because it could not allocate memory") ));
  187. return ERROR_NOT_ENOUGH_MEMORY;
  188. }
  189. SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
  190. FaxSend.SizeOfStruct = sizeof(FAX_SEND);
  191. FaxSend.FileName = FaxSendItem->FileName;
  192. FaxSend.CallerName = FaxSendItem->SenderName;
  193. FaxSend.CallerNumber = FaxSendItem->Tsid;
  194. FaxSend.ReceiverName = FaxSendItem->RecipientName;
  195. FaxSend.ReceiverNumber = FaxSendItem->PhoneNumber;
  196. FaxSend.CallHandle = 0; // filled in later via TapiStatusThread, if appropriate
  197. FaxSend.Reserved[0] = 0;
  198. FaxSend.Reserved[1] = 0;
  199. FaxSend.Reserved[2] = 0;
  200. FaxSendItem->JobQueue->JobStatus = JS_INPROGRESS;
  201. FaxSendItem->JobEntry->DocumentName = StringDup( FaxSendItem->DocumentName );
  202. HandoffJob = FaxSendItem->JobEntry->HandoffJob;
  203. //
  204. // Replace the original MMR file by one with the Branding on every page.
  205. //
  206. if (FaxUseBranding && FaxSendItem->JobQueue->SendRetries == 0) {
  207. if (FaxSend.CallerNumber == NULL) {
  208. DebugPrint(( TEXT("FaxSendThread() CallerNumber==0 NO BRANDING job\n") ));
  209. goto lPostBranding;
  210. }
  211. if (FaxSend.ReceiverNumber == NULL) {
  212. DebugPrint(( TEXT("FaxSendThread() ReceiverNumber==0 NO BRANDING job\n") ));
  213. goto lPostBranding;
  214. }
  215. if ( ! (lenDate = GetDateFormat( LOCALE_SYSTEM_DEFAULT,
  216. DATE_SHORTDATE,
  217. NULL, // use system date
  218. NULL, // use locale format
  219. lpDate,
  220. sizeof(lpDate)) ) ) {
  221. DebugPrint(( TEXT("FaxSendThread() GetDateFormat failed NO BRANDING job\n") ));
  222. goto lPostBranding;
  223. }
  224. if ( ! (lenTime = GetTimeFormat( LOCALE_SYSTEM_DEFAULT,
  225. TIME_NOSECONDS,
  226. NULL, // use system time
  227. NULL, // use locale format
  228. lpTime,
  229. sizeof(lpTime)) ) ) {
  230. DebugPrint(( TEXT("FaxSendThread() GetTimeFormat failed NO BRANDING job\n") ));
  231. goto lPostBranding;
  232. }
  233. _stprintf( lpDateTime, TEXT("%s %s"), lpDate, lpTime);
  234. //
  235. // Create lpCallerNumberPlusCompanyName
  236. //
  237. if (FaxSendItem->SenderCompany) {
  238. _stprintf( lpCallerNumberPlusCompanyName, TEXT("%s %s"), FaxSend.CallerNumber, FaxSendItem->SenderCompany);
  239. }
  240. else {
  241. _stprintf( lpCallerNumberPlusCompanyName, TEXT("%s"), FaxSend.CallerNumber );
  242. }
  243. MsgPtr[0] = (LPDWORD) lpDateTime;
  244. MsgPtr[1] = (LPDWORD) lpCallerNumberPlusCompanyName;
  245. MsgPtr[2] = (LPDWORD) FaxSend.ReceiverNumber;
  246. MsgPtr[3] = NULL;
  247. if ( ! ( lenBranding = FormatMessage(
  248. FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  249. NULL,
  250. MSG_BRANDING_FULL,
  251. 0,
  252. lpBranding,
  253. sizeof(lpBranding),
  254. (va_list *) MsgPtr
  255. ) ) ) {
  256. DebugPrint(( TEXT("FaxSendThread() MSG_BRANDING_OF failed NO BRANDING job\n") ));
  257. goto lPostBranding;
  258. }
  259. if ( ! ( lenBrandingEnd = FormatMessage(
  260. FORMAT_MESSAGE_FROM_HMODULE,
  261. NULL,
  262. MSG_BRANDING_END,
  263. 0,
  264. lpBrandingEnd,
  265. sizeof(lpBrandingEnd),
  266. NULL
  267. ) ) ) {
  268. DebugPrint(( TEXT("FaxSendThread() MSG_BRANDING_OF failed NO BRANDING job\n") ));
  269. goto lPostBranding;
  270. }
  271. //
  272. // Make sure we can fit everything.
  273. //
  274. if (lenBranding + lenBrandingEnd + 8 <= BrandingMaxLen) {
  275. goto lDoBranding;
  276. }
  277. //
  278. // Lets try to skip ReceiverNumber. The important part - is the CallerNumberPlusCompanyName.
  279. //
  280. MsgPtr[0] = (LPDWORD) lpDateTime;
  281. MsgPtr[1] = (LPDWORD) lpCallerNumberPlusCompanyName;
  282. MsgPtr[2] = NULL;
  283. if ( ! ( lenBranding = FormatMessage(
  284. FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  285. NULL,
  286. MSG_BRANDING_SHORT,
  287. 0,
  288. lpBranding,
  289. sizeof(lpBranding),
  290. (va_list *) MsgPtr
  291. ) ) ) {
  292. DebugPrint(( TEXT("FaxSendThread() MSG_BRANDING_SHORT failed NO BRANDING job\n") ));
  293. goto lPostBranding;
  294. }
  295. if (lenBranding + lenBrandingEnd + 8 <= BrandingMaxLen) {
  296. goto lDoBranding;
  297. }
  298. //
  299. // We need to truncate CallerNumberPlusCompanyName and re-format the message.
  300. //
  301. delta = lenBranding + lenBrandingEnd + 8 - BrandingMaxLen;
  302. lenCallerNumberPlusCompanyName = _tcslen (lpCallerNumberPlusCompanyName);
  303. if (lenCallerNumberPlusCompanyName <= delta) {
  304. DebugPrint(( TEXT("FaxSendThread() DELTA logical error NO BRANDING job\n") ));
  305. goto lPostBranding;
  306. }
  307. lpCallerNumberPlusCompanyName[ lenCallerNumberPlusCompanyName - delta] = TEXT('\0');
  308. MsgPtr[0] = (LPDWORD) lpDateTime;
  309. MsgPtr[1] = (LPDWORD) lpCallerNumberPlusCompanyName;
  310. MsgPtr[2] = NULL;
  311. if ( ! ( lenBranding = FormatMessage(
  312. FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  313. NULL,
  314. MSG_BRANDING_SHORT,
  315. 0,
  316. lpBranding,
  317. sizeof(lpBranding),
  318. (va_list *) MsgPtr
  319. ) ) ) {
  320. DebugPrint(( TEXT("FaxSendThread() 2nd MSG_BRANDING_SHORT failed NO BRANDING job\n") ));
  321. goto lPostBranding;
  322. }
  323. if (lenBranding + lenBrandingEnd + 8 > BrandingMaxLen) {
  324. DebugPrint(( TEXT("FaxSendThread() DELTA 2 logical error NO BRANDING job\n") ));
  325. goto lPostBranding;
  326. }
  327. lDoBranding:
  328. __try {
  329. if (! MmrAddBranding( FaxSend.FileName, lpBranding, lpBrandingEnd, BrandingHeight) ) {
  330. DebugPrint(( TEXT("FaxSendThread() could not ADD Branding\n") ));
  331. }
  332. } __except (EXCEPTION_EXECUTE_HANDLER) {
  333. DebugPrint(( TEXT("MmrAddBranding() failed: 0x%08x"), GetExceptionCode() ));
  334. }
  335. }
  336. lPostBranding:
  337. if (!HandoffJob) {
  338. FaxSendItem->JobEntry->LineInfo->State = FPS_INITIALIZING;
  339. }
  340. else {
  341. //
  342. // We need to wait for TapiWorkerThread to get an existing CallHandle and put it in the lineinfo structure
  343. //
  344. WaitForSingleObject(FaxSendItem->JobEntry->hCallHandleEvent,INFINITE);
  345. if (!FaxSendItem->JobEntry->LineInfo->HandoffCallHandle) {
  346. //
  347. // somehow the call handoff failed, we can't send the fax
  348. //
  349. FaxSendItem->JobEntry->LineInfo->State = FPS_ABORTING;
  350. __try {
  351. Rslt = FaxSendItem->JobEntry->LineInfo->Provider->FaxDevAbortOperation(
  352. (HANDLE) FaxSendItem->JobEntry->InstanceData);
  353. } __except (EXCEPTION_EXECUTE_HANDLER) {
  354. FaxSendItem->JobEntry->ErrorCode = GetExceptionCode();
  355. }
  356. }
  357. else {
  358. //
  359. // Set the call handle, we're ready to send the fax
  360. //
  361. FaxSend.CallHandle = FaxSendItem->JobEntry->LineInfo->HandoffCallHandle;
  362. FaxSendItem->JobEntry->LineInfo->State = FPS_INITIALIZING;
  363. }
  364. }
  365. DebugPrint((TEXT("Started FAX send - File [%s] - Number [%s]"), FaxSend.FileName, FaxSendItem->JobEntry->PhoneNumber ));
  366. __try {
  367. Rslt = FaxSendItem->JobEntry->LineInfo->Provider->FaxDevSend(
  368. (HANDLE) FaxSendItem->JobEntry->InstanceData,
  369. &FaxSend,
  370. FaxSendCallback
  371. );
  372. } __except (EXCEPTION_EXECUTE_HANDLER) {
  373. FaxSendItem->JobEntry->ErrorCode = GetExceptionCode();
  374. }
  375. __try {
  376. FaxStatus->SizeOfStruct = sizeof(FAX_DEV_STATUS);
  377. FaxSendItem->JobEntry->LineInfo->Provider->FaxDevReportStatus(
  378. (HANDLE) FaxSendItem->JobEntry->InstanceData,
  379. FaxStatus,
  380. StatusSize,
  381. &BytesNeeded
  382. );
  383. } __except (EXCEPTION_EXECUTE_HANDLER) {
  384. DebugPrint(( TEXT("FaxDevReportStatus() failed: 0x%08x"), GetExceptionCode() ));
  385. }
  386. DebugPrint(( TEXT("Send status: 0x%08x, string: 0x%08x File %s"), FaxStatus->StatusId, FaxStatus->StringId, FaxSend.FileName ));
  387. //
  388. // enter critical section to block out FaxStatusThread
  389. //
  390. EnterCriticalSection( &CsJob );
  391. GetSystemTimeAsFileTime( (FILETIME*) &FaxSendItem->JobEntry->EndTime );
  392. FaxSendItem->JobEntry->ElapsedTime = FaxSendItem->JobEntry->EndTime - FaxSendItem->JobEntry->StartTime;
  393. PageCount = FaxStatus->PageCount;
  394. if (!Rslt) {
  395. switch (FaxStatus->StatusId) {
  396. case FS_LINE_UNAVAILABLE:
  397. //
  398. // this is the glare condition
  399. //
  400. if (PerfCounters) {
  401. InterlockedIncrement( (PLONG)&PerfCounters->OutboundFailedXmit );
  402. }
  403. Retrying = TRUE;
  404. break;
  405. case FS_NO_ANSWER:
  406. case FS_NO_DIAL_TONE:
  407. case FS_DISCONNECTED:
  408. case FS_BUSY:
  409. case FS_NOT_FAX_CALL:
  410. case FS_FATAL_ERROR:
  411. if (PerfCounters){
  412. InterlockedIncrement( (PLONG)&PerfCounters->OutboundFailedConnections );
  413. }
  414. FaxSendItem->JobQueue->SendRetries++;
  415. if (FaxSendItem->JobQueue->SendRetries <= FaxSendRetries) {
  416. Retrying = TRUE;
  417. } else {
  418. //
  419. // retries exceeded, mark job as expired
  420. //
  421. FILETIME CurrentFileTime;
  422. LARGE_INTEGER NewTime;
  423. FaxSendItem->JobQueue->JobStatus = JS_RETRIES_EXCEEDED ;
  424. GetSystemTimeAsFileTime( &CurrentFileTime );
  425. NewTime.LowPart = CurrentFileTime.dwLowDateTime;
  426. NewTime.HighPart = CurrentFileTime.dwHighDateTime;
  427. FaxSendItem->JobQueue->ScheduleTime = NewTime.QuadPart;
  428. }
  429. FaxLogSend(
  430. FaxSendItem,
  431. Rslt,
  432. FaxStatus,
  433. Retrying
  434. );
  435. break ;
  436. case FS_USER_ABORT:
  437. FaxLogSend(
  438. FaxSendItem,
  439. Rslt,
  440. FaxStatus,
  441. FALSE
  442. );
  443. break ;
  444. default:
  445. if (PerfCounters){
  446. InterlockedIncrement( (PLONG)&PerfCounters->OutboundFailedXmit );
  447. }
  448. }
  449. //
  450. // clean up the job queue entry
  451. //
  452. EnterCriticalSection ( &CsQueue );
  453. FaxSendItem->JobQueue->RefCount -= 1;
  454. //
  455. // don't retry a handoff job
  456. //
  457. if (
  458. FaxSendItem->JobQueue->JobEntry &&
  459. (FaxSendItem->JobQueue->JobEntry->HandoffJob ||
  460. FaxSendItem->JobQueue->JobEntry->Aborting)
  461. ) {
  462. RemoveJobQueueEntry( FaxSendItem->JobQueue );
  463. FaxSendItem->JobQueue = NULL;
  464. } else if (Retrying) {
  465. FaxSendItem->JobQueue->JobStatus = JS_RETRYING;
  466. FaxSendItem->JobQueue->JobEntry = NULL;
  467. RescheduleJobQueueEntry( FaxSendItem->JobQueue );
  468. }
  469. LeaveCriticalSection ( &CsQueue );
  470. //
  471. // send the negative delivery report
  472. //
  473. if (!Retrying &&
  474. ((FaxSendItem->JobEntry->DeliveryReportType == DRT_INBOX &&
  475. FaxSendItem->JobEntry->DeliveryReportProfile) ||
  476. (FaxSendItem->JobEntry->DeliveryReportType == DRT_EMAIL)))
  477. {
  478. SYSTEMTIME SystemTime;
  479. FileTimeToLocalFileTime( (FILETIME*) &FaxSendItem->JobEntry->StartTime, &LocalTime );
  480. FileTimeToSystemTime( &LocalTime, &SystemTime );
  481. GetTimeFormat(
  482. LOCALE_SYSTEM_DEFAULT,
  483. LOCALE_NOUSEROVERRIDE,
  484. &SystemTime,
  485. NULL,
  486. TimeStr,
  487. sizeof(TimeStr)
  488. );
  489. MsgPtr[0] = (LPDWORD) FaxSendItem->SenderName;
  490. MsgPtr[1] = (LPDWORD) FaxSendItem->RecipientName;
  491. MsgPtr[2] = (LPDWORD) FaxSendItem->JobEntry->PhoneNumber;
  492. MsgPtr[3] = (LPDWORD) TimeStr;
  493. MsgPtr[4] = (LPDWORD) FaxSendItem->JobEntry->LineInfo->DeviceName;
  494. MsgPtr[5] = (LPDWORD) GetString( FaxStatus->StatusId );
  495. MsgCount = FormatMessage(
  496. FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  497. NULL,
  498. MSG_NDR,
  499. MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
  500. MsgStr,
  501. sizeof(MsgStr),
  502. (va_list *) MsgPtr
  503. );
  504. if (FaxSendItem->JobEntry->DeliveryReportType == DRT_INBOX) {
  505. StoreMapiMessage(
  506. FaxSendItem->JobEntry->DeliveryReportProfile,
  507. GetString( IDS_SERVER_NAME ),
  508. GetString( IDS_NDR_SUBJECT ),
  509. MsgStr,
  510. FaxSend.FileName,
  511. GetString( IDS_NDR_FILENAME ),
  512. IMPORTANCE_HIGH,
  513. NULL,
  514. &BytesNeeded
  515. );
  516. } else if (FaxSendItem->JobEntry->DeliveryReportType == DRT_EMAIL && InboundProfileInfo) {
  517. MailMapiMessage(
  518. InboundProfileInfo,
  519. FaxSendItem->JobEntry->DeliveryReportAddress,
  520. GetString( IDS_NDR_SUBJECT ),
  521. MsgStr,
  522. FaxSend.FileName,
  523. GetString( IDS_NDR_FILENAME ),
  524. IMPORTANCE_HIGH,
  525. &BytesNeeded
  526. );
  527. }
  528. }
  529. } else {
  530. //
  531. // add MS tiff tags to the sent fax
  532. // the tiff file is a temp file, so we add the tags to the source file
  533. //
  534. AddTiffTags(FaxSend.FileName,
  535. FaxSendItem->JobEntry->StartTime,
  536. FaxStatus,
  537. &FaxSend
  538. );
  539. //
  540. // if the send was successful, archive the file
  541. //
  542. Archived = ArchivePrintJob(
  543. FaxSend.FileName
  544. );
  545. FaxLogSend(
  546. FaxSendItem,
  547. Rslt,
  548. FaxStatus,
  549. TRUE
  550. );
  551. //
  552. // Increment counters for Performance Monitor
  553. //
  554. if (PerfCounters){
  555. SYSTEMTIME SystemTime ;
  556. DWORD Seconds ;
  557. HANDLE FileHandle ;
  558. DWORD Bytes = 0 ; /// Compute #bytes in the file FaxSend.FileName and stick it here!
  559. FileHandle = CreateFile(
  560. FaxSend.FileName,
  561. GENERIC_READ,
  562. FILE_SHARE_READ,
  563. NULL,
  564. OPEN_EXISTING,
  565. FILE_ATTRIBUTE_NORMAL,
  566. NULL
  567. );
  568. if(FileHandle != INVALID_HANDLE_VALUE){
  569. Bytes = GetFileSize( FileHandle, NULL );
  570. CloseHandle( FileHandle );
  571. }
  572. FileTimeToSystemTime(
  573. (FILETIME*)&FaxSendItem->JobEntry->ElapsedTime,
  574. &SystemTime
  575. );
  576. Seconds = (DWORD)( SystemTime.wSecond + 60 * ( SystemTime.wMinute + 60 * SystemTime.wHour ));
  577. InterlockedIncrement( (PLONG)&PerfCounters->OutboundFaxes );
  578. InterlockedIncrement( (PLONG)&PerfCounters->TotalFaxes );
  579. InterlockedExchangeAdd( (PLONG)&PerfCounters->OutboundPages, (LONG)FaxStatus->PageCount );
  580. InterlockedExchangeAdd( (PLONG)&PerfCounters->TotalPages, (LONG)FaxStatus->PageCount );
  581. EnterCriticalSection( &CsPerfCounters );
  582. OutboundSeconds += Seconds;
  583. TotalSeconds += Seconds;
  584. PerfCounters->OutboundMinutes = OutboundSeconds / 60 ;
  585. PerfCounters->TotalMinutes = TotalSeconds / 60 ;
  586. PerfCounters->OutboundBytes += Bytes;
  587. PerfCounters->TotalBytes += Bytes;
  588. LeaveCriticalSection( &CsPerfCounters );
  589. }
  590. //
  591. // send the positive delivery report
  592. //
  593. if ((FaxSendItem->JobEntry->DeliveryReportType == DRT_INBOX &&
  594. FaxSendItem->JobEntry->DeliveryReportProfile) ||
  595. (FaxSendItem->JobEntry->DeliveryReportType == DRT_EMAIL)) {
  596. SYSTEMTIME SystemTime;
  597. _ltot( (LONG) PageCount, PageCountStr, 10 );
  598. FileTimeToLocalFileTime( (FILETIME*) &FaxSendItem->JobEntry->StartTime, &LocalTime );
  599. FileTimeToSystemTime( &LocalTime, &SystemTime );
  600. GetTimeFormat(
  601. LOCALE_SYSTEM_DEFAULT,
  602. LOCALE_NOUSEROVERRIDE,
  603. &SystemTime,
  604. NULL,
  605. TimeStr,
  606. sizeof(TimeStr)
  607. );
  608. MsgPtr[0] = (LPDWORD) FaxSendItem->SenderName;
  609. MsgPtr[1] = (LPDWORD) FaxSendItem->RecipientName;
  610. MsgPtr[2] = (LPDWORD) FaxSendItem->JobEntry->PhoneNumber;
  611. MsgPtr[3] = (LPDWORD) PageCountStr;
  612. MsgPtr[4] = (LPDWORD) TimeStr;
  613. MsgPtr[5] = (LPDWORD) FaxSendItem->JobEntry->LineInfo->DeviceName;
  614. MsgPtr[6] = NULL;
  615. MsgCount = FormatMessage(
  616. FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  617. NULL,
  618. MSG_DR,
  619. MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
  620. MsgStr,
  621. sizeof(MsgStr),
  622. (va_list *) MsgPtr
  623. );
  624. if (FaxSendItem->JobEntry->DeliveryReportType == DRT_INBOX) {
  625. StoreMapiMessage(
  626. FaxSendItem->JobEntry->DeliveryReportProfile,
  627. GetString( IDS_SERVICE_NAME ),
  628. GetString( IDS_DR_SUBJECT ),
  629. MsgStr,
  630. FaxSend.FileName,
  631. GetString( IDS_DR_FILENAME ),
  632. IMPORTANCE_NORMAL,
  633. NULL,
  634. &BytesNeeded
  635. );
  636. } else if (FaxSendItem->JobEntry->DeliveryReportType == DRT_EMAIL && InboundProfileInfo) {
  637. MailMapiMessage(
  638. InboundProfileInfo,
  639. FaxSendItem->JobEntry->DeliveryReportAddress,
  640. GetString( IDS_DR_SUBJECT ),
  641. MsgStr,
  642. FaxSend.FileName,
  643. GetString( IDS_DR_FILENAME ),
  644. IMPORTANCE_NORMAL,
  645. &BytesNeeded
  646. );
  647. }
  648. }
  649. //
  650. // remove this queue entry from the queue list
  651. //
  652. EnterCriticalSection ( &CsQueue );
  653. FaxSendItem->JobQueue->RefCount -= 1;
  654. RemoveJobQueueEntry( FaxSendItem->JobQueue );
  655. FaxSendItem->JobQueue = NULL;
  656. LeaveCriticalSection ( &CsQueue );
  657. }
  658. //
  659. // do any special work for a broadcast job
  660. //
  661. if (FaxSendItem->JobEntry->BroadcastJob) {
  662. DeleteFile( FaxSendItem->FileName );
  663. }
  664. FaxSendItem->JobEntry->ErrorCode = FaxStatus->StatusId;
  665. FaxSendItem->JobEntry->RefCount -= 1;
  666. FaxSendItem->JobEntry->LineInfo->State = FPS_AVAILABLE;
  667. if (FaxSendItem->JobEntry->RefCount == 0 && FaxSendItem->JobEntry->hEventEnd) {
  668. SetEvent( FaxSendItem->JobEntry->hEventEnd );
  669. EndJob( FaxSendItem->JobEntry );
  670. EnterCriticalSection ( &CsQueue );
  671. // JobQueue may already be NULL for an aborted job
  672. if (FaxSendItem->JobQueue) {
  673. if (!Retrying && (FaxSendItem->JobQueue->JobStatus != JS_RETRIES_EXCEEDED)) {
  674. FaxSendItem->JobQueue->JobStatus = JS_DELETING;
  675. }
  676. FaxSendItem->JobQueue->JobEntry = NULL;
  677. }
  678. LeaveCriticalSection ( &CsQueue );
  679. }
  680. LeaveCriticalSection( &CsJob );
  681. if (!Retrying && (!ArchiveOutgoingFaxes || Archived)) {
  682. DeleteFile( FaxSend.FileName );
  683. }
  684. MemFree( FaxSendItem->FileName );
  685. MemFree( FaxSendItem->PhoneNumber );
  686. MemFree( FaxSendItem->Tsid );
  687. MemFree( FaxSendItem->RecipientName );
  688. MemFree( FaxSendItem->SenderName );
  689. MemFree( FaxSendItem->SenderDept );
  690. MemFree( FaxSendItem->SenderCompany );
  691. MemFree( FaxSendItem->BillingCode );
  692. MemFree( FaxSendItem->DocumentName );
  693. MemFree( FaxSendItem );
  694. MemFree( FaxStatus );
  695. ReleaseSemaphore( JobQueueSemaphore, 1, NULL );
  696. SetThreadExecutionState(ES_CONTINUOUS);
  697. return 0;
  698. }
  699. PJOB_ENTRY
  700. StartJob(
  701. DWORD DeviceId,
  702. DWORD JobType,
  703. LPWSTR FaxNumber
  704. )
  705. /*++
  706. Routine Description:
  707. This fuction calls the device provider's StartJob function.
  708. Arguments:
  709. DeviceId - Device Id to start the job on, or USE_SERVER_DEVICE.
  710. JobType - type of job
  711. FaxNumber - phone number for outbound jobs
  712. Return Value:
  713. Pointer to a JOB_ENTRY, or NULL for failure.
  714. --*/
  715. {
  716. BOOL Failure = TRUE;
  717. PJOB_ENTRY JobEntry = NULL;
  718. PLINE_INFO LineInfo;
  719. JobEntry = (PJOB_ENTRY) MemAlloc( sizeof(JOB_ENTRY) );
  720. if (!JobEntry) {
  721. goto exit;
  722. }
  723. if (FaxNumber) {
  724. //
  725. // get a cannonical phone number
  726. //
  727. LPLINETRANSLATEOUTPUT LineTranslateOutput = NULL;
  728. if (MyLineTranslateAddress( FaxNumber, 0, &LineTranslateOutput ) == 0) {
  729. wcsncpy(
  730. JobEntry->PhoneNumber,
  731. (LPWSTR) ((LPBYTE)LineTranslateOutput + LineTranslateOutput->dwDisplayableStringOffset),
  732. SIZEOF_PHONENO
  733. );
  734. MemFree( LineTranslateOutput );
  735. } else {
  736. wcsncpy( JobEntry->PhoneNumber, FaxNumber, SIZEOF_PHONENO );
  737. }
  738. }
  739. //
  740. // assume send job without use_server_device is a handoff job
  741. //
  742. if (JobType == JT_SEND && DeviceId != USE_SERVER_DEVICE) {
  743. LineInfo = GetTapiLineForFaxOperation( DeviceId, JobType, JobEntry->PhoneNumber, TRUE );
  744. }
  745. else {
  746. LineInfo = GetTapiLineForFaxOperation( DeviceId, JobType, JobEntry->PhoneNumber, FALSE );
  747. }
  748. if (!LineInfo) {
  749. goto exit;
  750. }
  751. JobEntry->JobType = JT_UNKNOWN;
  752. JobEntry->CallHandle = 0;
  753. JobEntry->InstanceData = 0;
  754. JobEntry->ErrorCode = 0;
  755. JobEntry->LineInfo = LineInfo;
  756. JobEntry->SendIdx = -1;
  757. JobEntry->Released = FALSE;
  758. JobEntry->HandoffJob = (JobType == JT_SEND && DeviceId != USE_SERVER_DEVICE);
  759. JobEntry->hEventEnd = CreateEvent( NULL, FALSE, FALSE, NULL );
  760. JobEntry->hCallHandleEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  761. GetSystemTimeAsFileTime( (FILETIME*) &JobEntry->StartTime );
  762. __try {
  763. if ((!(LineInfo->Flags & FPF_VIRTUAL)) && (!LineInfo->hLine) && (!OpenTapiLine( LineInfo ))) {
  764. DebugPrint(( TEXT("Could not get an open tapi line, StartJob() failed") ));
  765. goto exit;
  766. }
  767. if (LineInfo->Provider->FaxDevStartJob(
  768. LineInfo->hLine,
  769. LineInfo->DeviceId,
  770. (PHANDLE) &JobEntry->InstanceData,
  771. StatusCompletionPortHandle,
  772. (ULONG_PTR) LineInfo ))
  773. {
  774. EnterCriticalSection( &CsJob );
  775. InsertTailList( &JobListHead, &JobEntry->ListEntry );
  776. LeaveCriticalSection( &CsJob );
  777. Failure = FALSE;
  778. } else {
  779. DebugPrint((TEXT("FaxDevStartJob failed")));
  780. }
  781. } __except (EXCEPTION_EXECUTE_HANDLER) {
  782. }
  783. LineInfo->JobEntry = JobEntry;
  784. exit:
  785. if (Failure) {
  786. if (LineInfo) {
  787. ReleaseTapiLine( LineInfo, LineInfo->JobEntry ? LineInfo->JobEntry->CallHandle : 0 );
  788. }
  789. if (JobEntry) {
  790. CloseHandle (JobEntry->hEventEnd);
  791. CloseHandle (JobEntry->hCallHandleEvent);
  792. MemFree( JobEntry );
  793. }
  794. JobEntry = NULL;
  795. }
  796. return JobEntry;
  797. }
  798. BOOL
  799. EndJob(
  800. IN PJOB_ENTRY JobEntry
  801. )
  802. /*++
  803. Routine Description:
  804. This fuction calls the device provider's EndJob function.
  805. Arguments:
  806. None.
  807. Return Value:
  808. Error code.
  809. --*/
  810. {
  811. BOOL rVal;
  812. PJOB_INFO_1 JobInfo = NULL;
  813. if (!FindJobByJob( JobEntry )) {
  814. //
  815. // if we get here then it means we hit a race
  816. // condition where the FaxSendThread called EndJob
  817. // at the same time that a client app did.
  818. //
  819. return ERROR_SUCCESS;
  820. }
  821. if (JobEntry->RefCount) {
  822. HANDLE hEventEnd;
  823. DWORD Result;
  824. EnterCriticalSection( &CsJob );
  825. hEventEnd = JobEntry->hEventEnd;
  826. LeaveCriticalSection( &CsJob );
  827. while (TRUE) {
  828. Result = WaitForSingleObject( hEventEnd, 1000 );
  829. // if the wait timed out and FAX_SendDocument() has been called
  830. // (SendIdx != -1), then check for a job status change
  831. if (Result != WAIT_TIMEOUT) {
  832. //
  833. // if the event has been signaled or deleted, then return
  834. //
  835. break;
  836. }
  837. }
  838. return ERROR_SUCCESS;
  839. }
  840. EnterCriticalSection( &CsJob );
  841. if (!JobEntry->Released) {
  842. __try {
  843. rVal = JobEntry->LineInfo->Provider->FaxDevEndJob(
  844. (HANDLE) JobEntry->InstanceData
  845. );
  846. if (!rVal) {
  847. DebugPrint(( TEXT("FaxDevEndJob() failed") ));
  848. }
  849. } __except (EXCEPTION_EXECUTE_HANDLER) {
  850. DebugPrint(( TEXT("FaxDevEndJob() crashed, ec=0x%08x"), GetExceptionCode() ));
  851. }
  852. }
  853. if (!JobEntry->Released) {
  854. if (JobEntry->LineInfo->State != FPS_NOT_FAX_CALL) {
  855. ReleaseTapiLine( JobEntry->LineInfo, JobEntry->CallHandle );
  856. JobEntry->CallHandle = 0;
  857. }
  858. }
  859. RemoveEntryList( &JobEntry->ListEntry );
  860. EnterCriticalSection( &CsLine );
  861. JobEntry->LineInfo->JobEntry = NULL;
  862. LeaveCriticalSection( &CsLine );
  863. CloseHandle( JobEntry->hEventEnd );
  864. CloseHandle( JobEntry->hCallHandleEvent );
  865. MemFree( (LPBYTE) JobEntry->JobParam.RecipientNumber );
  866. MemFree( (LPBYTE) JobEntry->JobParam.RecipientName );
  867. MemFree( (LPBYTE) JobEntry->JobParam.Tsid );
  868. MemFree( (LPBYTE) JobEntry->JobParam.SenderName );
  869. MemFree( (LPBYTE) JobEntry->JobParam.SenderCompany );
  870. MemFree( (LPBYTE) JobEntry->JobParam.SenderDept );
  871. MemFree( (LPBYTE) JobEntry->JobParam.BillingCode );
  872. MemFree( JobEntry->FaxStatus.CSI );
  873. MemFree( JobEntry->FaxStatus.CallerId );
  874. MemFree( JobEntry->FaxStatus.RoutingInfo );
  875. MemFree( JobEntry->DeliveryReportAddress );
  876. MemFree( JobEntry->DocumentName );
  877. MemFree( JobEntry->UserName );
  878. //
  879. // There could have been a request to change the port status while we were handling this job.
  880. // We allow the caller to modify a few of these requests to succeed, like the ring count for instance.
  881. // While we still have the job critical section, let's make sure that we commit any requested changes to the
  882. // registry. This should be a fairly quick operation.
  883. //
  884. CommitDeviceChanges();
  885. LeaveCriticalSection( &CsJob );
  886. MemFree( JobEntry );
  887. return rVal;
  888. }
  889. BOOL
  890. ReleaseJob(
  891. IN PJOB_ENTRY JobEntry
  892. )
  893. {
  894. BOOL rVal;
  895. if (!FindJobByJob( JobEntry )) {
  896. return ERROR_SUCCESS;
  897. }
  898. EnterCriticalSection( &CsJob );
  899. __try {
  900. rVal = JobEntry->LineInfo->Provider->FaxDevEndJob(
  901. (HANDLE) JobEntry->InstanceData
  902. );
  903. if (!rVal) {
  904. DebugPrint(( TEXT("FaxDevEndJob() failed") ));
  905. }
  906. } __except (EXCEPTION_EXECUTE_HANDLER) {
  907. DebugPrint(( TEXT("FaxDevEndJob() crashed, ec=0x%08x"), GetExceptionCode() ));
  908. }
  909. if (JobEntry->LineInfo->State != FPS_NOT_FAX_CALL) {
  910. ReleaseTapiLine( JobEntry->LineInfo, JobEntry->CallHandle );
  911. JobEntry->CallHandle = 0;
  912. }
  913. JobEntry->Released = TRUE;
  914. LeaveCriticalSection( &CsJob );
  915. return TRUE;
  916. }
  917. DWORD
  918. SendDocument(
  919. PJOB_ENTRY JobEntry,
  920. LPTSTR FileName,
  921. PFAX_JOB_PARAM JobParam,
  922. PJOB_QUEUE JobQueue
  923. )
  924. /*++
  925. Routine Description:
  926. This fuction queues a new item that requests a
  927. FAX document be sent.
  928. Arguments:
  929. JobEntry - Pointer to a JOB_ENTRY created by StartJob.
  930. FileName - File name containing the TIFF data
  931. JobParam - Pointer to FAX_JOB_PARAM struct
  932. Return Value:
  933. Error code.
  934. --*/
  935. {
  936. PFAX_SEND_ITEM FaxSendItem;
  937. DWORD ThreadId;
  938. HANDLE hThread;
  939. if (JobEntry->RefCount) {
  940. //
  941. // only one operation per job
  942. //
  943. return ERROR_IO_PENDING;
  944. }
  945. FaxSendItem = (PFAX_SEND_ITEM) MemAlloc(sizeof(FAX_SEND_ITEM));
  946. if (!FaxSendItem) {
  947. return ERROR_NOT_ENOUGH_MEMORY;
  948. }
  949. FaxSendItem->JobEntry = JobEntry;
  950. FaxSendItem->FileName = StringDup( FileName );
  951. FaxSendItem->PhoneNumber = StringDup( JobParam->RecipientNumber );
  952. if (JobParam->Tsid == NULL || JobParam->Tsid[0] == 0 || FaxUseDeviceTsid) {
  953. FaxSendItem->Tsid = StringDup( JobEntry->LineInfo->Tsid );
  954. }
  955. else {
  956. FaxSendItem->Tsid = StringDup( JobParam->Tsid );
  957. }
  958. FaxSendItem->RecipientName = StringDup( JobParam->RecipientName );
  959. FaxSendItem->SenderName = StringDup( JobParam->SenderName );
  960. FaxSendItem->SenderDept = StringDup( JobParam->SenderDept );
  961. FaxSendItem->SenderCompany = StringDup( JobParam->SenderCompany );
  962. FaxSendItem->BillingCode = StringDup( JobParam->BillingCode );
  963. FaxSendItem->DocumentName = StringDup( JobParam->DocumentName );
  964. FaxSendItem->JobQueue = JobQueue;
  965. JobQueue->RefCount += 1;
  966. JobEntry->RefCount += 1;
  967. hThread = CreateThread(
  968. NULL,
  969. 1024*100,
  970. (LPTHREAD_START_ROUTINE) FaxSendThread,
  971. (LPVOID) FaxSendItem,
  972. 0,
  973. &ThreadId
  974. );
  975. if (!hThread) {
  976. MemFree( FaxSendItem->FileName );
  977. MemFree( FaxSendItem->PhoneNumber );
  978. MemFree( FaxSendItem->Tsid );
  979. MemFree( FaxSendItem->RecipientName );
  980. MemFree( FaxSendItem->SenderName );
  981. MemFree( FaxSendItem->SenderDept );
  982. MemFree( FaxSendItem->SenderCompany );
  983. MemFree( FaxSendItem->BillingCode );
  984. MemFree( FaxSendItem );
  985. CloseHandle( hThread );
  986. return GetLastError();
  987. }
  988. CloseHandle( hThread );
  989. return ERROR_SUCCESS;
  990. }
  991. DWORD
  992. FaxStatusThread(
  993. LPVOID UnUsed
  994. )
  995. /*++
  996. Routine Description:
  997. This fuction runs asychronously as a separate thread to
  998. query the status of all outstanding fax jobs. The status
  999. is updated in the JOB_ENTRY structure and the print job
  1000. is updated with a explanitory string.
  1001. Arguments:
  1002. UnUsed - UnUsed pointer
  1003. Return Value:
  1004. Always zero.
  1005. --*/
  1006. {
  1007. PJOB_ENTRY JobEntry;
  1008. PFAX_DEV_STATUS FaxStatus;
  1009. BOOL Rval;
  1010. DWORD Bytes;
  1011. ULONG_PTR CompletionKey;
  1012. INT PageCount;
  1013. while( TRUE ) {
  1014. Rval = GetQueuedCompletionStatus(
  1015. StatusCompletionPortHandle,
  1016. &Bytes,
  1017. &CompletionKey,
  1018. (LPOVERLAPPED*) &FaxStatus,
  1019. INFINITE
  1020. );
  1021. if (!Rval) {
  1022. DebugPrint(( TEXT("GetQueuedCompletionStatus() failed, ec=0x%08x"), GetLastError() ));
  1023. continue;
  1024. }
  1025. if (CompletionKey == EVENT_COMPLETION_KEY) {
  1026. //
  1027. // Let each registered client know about the fax event
  1028. //
  1029. PLIST_ENTRY Next;
  1030. PFAX_CLIENT_DATA ClientData;
  1031. PFAX_EVENT FaxEvent = (PFAX_EVENT) FaxStatus;
  1032. EnterCriticalSection( &CsClients );
  1033. Next = ClientsListHead.Flink;
  1034. if (Next) {
  1035. while ((ULONG_PTR)Next != (ULONG_PTR)&ClientsListHead) {
  1036. DWORD i;
  1037. BOOL fMessageSent = FALSE;
  1038. ClientData = CONTAINING_RECORD( Next, FAX_CLIENT_DATA, ListEntry );
  1039. DebugPrint(( TEXT("%d: Current : %08x\t Handle : %08x\t Next : %08x Head: %08x : \n"),
  1040. GetTickCount(),
  1041. (ULONG_PTR)Next,
  1042. ClientData->hWnd? (ULONG_PTR) ClientData->hWnd : (ULONG_PTR) ClientData->FaxClientHandle,
  1043. (ULONG_PTR)ClientData->ListEntry.Flink,
  1044. (ULONG_PTR)&ClientsListHead ));
  1045. Next = ClientData->ListEntry.Flink;
  1046. //
  1047. // only send the started message once to each client
  1048. //
  1049. if ((FaxEvent->EventId == FEI_FAXSVC_STARTED) && ClientData->StartedMsg) {
  1050. fMessageSent = TRUE;
  1051. goto next_client;
  1052. }
  1053. if (ClientData->hWnd) {
  1054. fMessageSent = PostClientMessage(ClientData,FaxEvent);
  1055. ClientData->StartedMsg = (FaxEvent->EventId == FEI_FAXSVC_STARTED) ?
  1056. TRUE :
  1057. ClientData->StartedMsg;
  1058. goto next_client;
  1059. }
  1060. if (!ClientData->FaxClientHandle) {
  1061. for(i = 0; i < 10; i++){
  1062. __try {
  1063. Rval = FAX_OpenConnection( ClientData->FaxHandle, ClientData->Context, &ClientData->FaxClientHandle );
  1064. if (Rval) {
  1065. DebugPrint(( TEXT("FAX_OpenConnection() failed, ec=0x%08x"), Rval ));
  1066. continue;
  1067. } else {
  1068. break;
  1069. }
  1070. } __except (EXCEPTION_EXECUTE_HANDLER) {
  1071. DebugPrint(( TEXT("FAX_OpenConnection() crashed: 0x%08x"), GetExceptionCode() ));
  1072. }
  1073. Sleep( 1000 );
  1074. }
  1075. }
  1076. //
  1077. // if we don't have a handle at this point, forget it
  1078. //
  1079. if (!ClientData->FaxClientHandle) {
  1080. goto next_client;
  1081. }
  1082. for(i = 0; i < 10; i++){
  1083. __try {
  1084. Rval = FAX_ClientEventQueue( ClientData->FaxClientHandle, *FaxEvent );
  1085. if (Rval) {
  1086. DebugPrint(( TEXT("FAX_ClientEventQueue() failed, ec=0x%08x"), Rval ));
  1087. continue;
  1088. } else {
  1089. fMessageSent = TRUE;
  1090. ClientData->StartedMsg = (FaxEvent->EventId == FEI_FAXSVC_STARTED) ?
  1091. TRUE :
  1092. ClientData->StartedMsg;
  1093. break;
  1094. }
  1095. } __except (EXCEPTION_EXECUTE_HANDLER) {
  1096. DebugPrint(( TEXT("FAX_ClientEventQueue() crashed: 0x%08x"), GetExceptionCode() ));
  1097. }
  1098. Sleep( 1000 );
  1099. }
  1100. next_client:
  1101. if (!fMessageSent) {
  1102. //
  1103. // stale list entry, remove the client from our list.
  1104. //
  1105. if (ClientData->hWnd && ClientData->hClientToken) {
  1106. CloseHandle( ClientData->hClientToken );
  1107. MemFree( (LPBYTE) ClientData->WindowStation );
  1108. MemFree( (LPBYTE) ClientData->Desktop );
  1109. }
  1110. RemoveEntryList( &ClientData->ListEntry );
  1111. MemFree( ClientData );
  1112. }
  1113. }
  1114. }
  1115. LeaveCriticalSection( &CsClients );
  1116. //
  1117. // signal event if fax service ended, so we can terminate the process
  1118. //
  1119. if (FaxEvent->EventId == FEI_FAXSVC_ENDED && hServiceEndEvent != INVALID_HANDLE_VALUE) {
  1120. SetEvent( hServiceEndEvent ) ;
  1121. }
  1122. MemFree( FaxEvent );
  1123. FaxStatus = NULL;
  1124. continue;
  1125. }
  1126. //
  1127. // (else we're dealing with a status update from an FSP)
  1128. //
  1129. EnterCriticalSection( &CsJob );
  1130. JobEntry = ((PLINE_INFO) CompletionKey)->JobEntry;
  1131. if (!JobEntry) {
  1132. //
  1133. // this code path exposes a memory leak.
  1134. // the completion packed is not freed if this
  1135. // path is taken. the problem is that the
  1136. // memory cannot be freed if we don't have
  1137. // access to the job structure.
  1138. //
  1139. LeaveCriticalSection( &CsJob );
  1140. DebugPrint(( TEXT("FaxStatusThread - NULL JobEntry got StatusId 0x%08x"), FaxStatus->StatusId ));
  1141. continue;
  1142. }
  1143. JobEntry->LineInfo->State = FaxStatus->StatusId;
  1144. CreateFaxEvent( JobEntry->LineInfo->PermanentLineID, MapStatusIdToEventId( FaxStatus->StatusId ), JobEntry->JobId );
  1145. PageCount = FaxStatus->PageCount ? FaxStatus->PageCount : -1;
  1146. MemFree( JobEntry->FaxStatus.CSI );
  1147. MemFree( JobEntry->FaxStatus.CallerId );
  1148. MemFree( JobEntry->FaxStatus.RoutingInfo );
  1149. JobEntry->FaxStatus.SizeOfStruct = FaxStatus->SizeOfStruct;
  1150. JobEntry->FaxStatus.StatusId = FaxStatus->StatusId;
  1151. JobEntry->FaxStatus.StringId = FaxStatus->StringId;
  1152. JobEntry->FaxStatus.PageCount = FaxStatus->PageCount;
  1153. JobEntry->FaxStatus.CSI = StringDup( FaxStatus->CSI );
  1154. JobEntry->FaxStatus.CallerId = StringDup( FaxStatus->CallerId );
  1155. JobEntry->FaxStatus.RoutingInfo = StringDup( FaxStatus->RoutingInfo );
  1156. JobEntry->FaxStatus.Reserved[0] = 0;
  1157. JobEntry->FaxStatus.Reserved[1] = 0;
  1158. JobEntry->FaxStatus.Reserved[2] = 0;
  1159. HeapFree( JobEntry->LineInfo->Provider->HeapHandle, 0, FaxStatus );
  1160. LeaveCriticalSection( &CsJob );
  1161. }
  1162. return 0;
  1163. }
  1164. BOOL
  1165. InitializeJobManager(
  1166. PREG_FAX_SERVICE FaxReg
  1167. )
  1168. /*++
  1169. Routine Description:
  1170. This fuction initializes the thread pool and
  1171. FAX service queues.
  1172. Arguments:
  1173. ThreadHint - Number of threads to create in the initial pool.
  1174. Return Value:
  1175. Thread return value.
  1176. --*/
  1177. {
  1178. HANDLE hThread;
  1179. DWORD ThreadId;
  1180. DWORD i;
  1181. InitializeListHead( &JobListHead );
  1182. InitializeCriticalSection( &CsJob );
  1183. InitializeListHead( &QueueListHead );
  1184. InitializeCriticalSection( &CsQueue );
  1185. SetRetryValues( FaxReg );
  1186. if (GetFileAttributes( FaxReceiveDir ) == 0xffffffff) {
  1187. MakeDirectory( FaxReceiveDir );
  1188. }
  1189. if (GetFileAttributes( FaxQueueDir ) == 0xffffffff) {
  1190. MakeDirectory( FaxQueueDir );
  1191. }
  1192. StatusCompletionPortHandle = CreateIoCompletionPort(
  1193. INVALID_HANDLE_VALUE,
  1194. NULL,
  1195. 0,
  1196. MAX_STATUS_THREADS
  1197. );
  1198. if (!StatusCompletionPortHandle) {
  1199. DebugPrint(( TEXT("CreateIoCompletionPort() failed, ec=0x%08x"), GetLastError() ));
  1200. return FALSE;
  1201. }
  1202. hThread = CreateThread(
  1203. NULL,
  1204. 1024*100,
  1205. (LPTHREAD_START_ROUTINE) JobQueueThread,
  1206. NULL,
  1207. 0,
  1208. &ThreadId
  1209. );
  1210. if (!hThread) {
  1211. return FALSE;
  1212. }
  1213. CloseHandle( hThread );
  1214. for (i=0; i<MAX_STATUS_THREADS; i++) {
  1215. hThread = CreateThread(
  1216. NULL,
  1217. 1024*100,
  1218. (LPTHREAD_START_ROUTINE) FaxStatusThread,
  1219. NULL,
  1220. 0,
  1221. &ThreadId
  1222. );
  1223. if (!hThread) {
  1224. return FALSE;
  1225. }
  1226. CloseHandle( hThread );
  1227. }
  1228. return TRUE;
  1229. }
  1230. VOID
  1231. SetRetryValues(
  1232. PREG_FAX_SERVICE FaxReg
  1233. )
  1234. {
  1235. FaxSendRetries = FaxReg->Retries;
  1236. FaxSendRetryDelay = (INT) FaxReg->RetryDelay;
  1237. FaxDirtyDays = FaxReg->DirtyDays;
  1238. QueuePaused = FaxReg->QueuePaused;
  1239. NextJobId = FaxReg->NextJobNumber;
  1240. ForceReceive = FaxReg->ForceReceive;
  1241. TerminationDelay = FaxReg->TerminationDelay == 0 ? 30 : FaxReg->TerminationDelay;
  1242. FaxUseDeviceTsid = FaxReg->UseDeviceTsid;
  1243. FaxUseBranding = FaxReg->Branding;
  1244. ServerCp = FaxReg->ServerCp;
  1245. StartCheapTime = FaxReg->StartCheapTime;
  1246. StopCheapTime = FaxReg->StopCheapTime;
  1247. ArchiveOutgoingFaxes = FaxReg->ArchiveOutgoingFaxes;
  1248. ArchiveDirectory = StringDup( FaxReg->ArchiveDirectory );
  1249. }
  1250. LPTSTR
  1251. ExtractFaxTag(
  1252. LPTSTR pTagKeyword,
  1253. LPTSTR pTaggedStr,
  1254. INT *pcch
  1255. )
  1256. /*++
  1257. Routine Description:
  1258. Find the value of for the specified tag in a tagged string.
  1259. Arguments:
  1260. pTagKeyword - specifies the interested tag keyword
  1261. pTaggedStr - points to the tagged string to be searched
  1262. pcch - returns the length of the specified tag value (if found)
  1263. Return Value:
  1264. Points to the value for the specified tag.
  1265. NULL if the specified tag is not found
  1266. NOTE:
  1267. Tagged strings have the following form:
  1268. <tag>value<tag>value
  1269. The format of tags is defined as:
  1270. <$FAXTAG$ tag-name>
  1271. There is exactly one space between the tag keyword and the tag name.
  1272. Characters in a tag are case-sensitive.
  1273. --*/
  1274. {
  1275. LPTSTR pValue;
  1276. if (pValue = _tcsstr(pTaggedStr, pTagKeyword)) {
  1277. pValue += _tcslen(pTagKeyword);
  1278. if (pTaggedStr = _tcsstr(pValue, FAXTAG_PREFIX))
  1279. *pcch = (INT)(pTaggedStr - pValue);
  1280. else
  1281. *pcch = _tcslen(pValue);
  1282. }
  1283. return pValue;
  1284. }
  1285. BOOL
  1286. AddTiffTags(
  1287. LPTSTR FaxFileName,
  1288. DWORDLONG SendTime,
  1289. PFAX_DEV_STATUS FaxStatus,
  1290. PFAX_SEND FaxSend
  1291. )
  1292. /*++
  1293. Routine Description:
  1294. Add Ms Tiff Tags to a sent fax. Wraps TiffAddMsTags...
  1295. Arguments:
  1296. FaxFileName - Name of the file to archive
  1297. SendTime - time the fax was sent
  1298. FaxStatus - job status
  1299. FaxSend - FAX_SEND structure for sent fax, includes CSID.
  1300. Return Value:
  1301. TRUE - The tags were added.
  1302. FALSE - The tags were not added.
  1303. --*/
  1304. {
  1305. MS_TAG_INFO MsTagInfo;
  1306. WCHAR wcZero = L'\0';
  1307. MsTagInfo.RecipName = NULL;
  1308. if (FaxSend->ReceiverName && (FaxSend->ReceiverName[0] != wcZero) ) {
  1309. MsTagInfo.RecipName = FaxSend->ReceiverName;
  1310. }
  1311. MsTagInfo.RecipNumber = NULL;
  1312. if (FaxSend->ReceiverNumber && (FaxSend->ReceiverNumber[0] != wcZero) ) {
  1313. MsTagInfo.RecipNumber = FaxSend->ReceiverNumber;
  1314. }
  1315. MsTagInfo.SenderName = NULL;
  1316. if (FaxSend->CallerName && (FaxSend->CallerName[0] != wcZero) ) {
  1317. MsTagInfo.SenderName = FaxSend->CallerName;
  1318. }
  1319. MsTagInfo.Routing = NULL;
  1320. if (FaxStatus->RoutingInfo && (FaxStatus->RoutingInfo[0] != wcZero) ) {
  1321. MsTagInfo.Routing = FaxStatus->RoutingInfo;
  1322. }
  1323. MsTagInfo.CallerId = NULL;
  1324. if (FaxStatus->CallerId && (FaxStatus->CallerId[0] != wcZero) ) {
  1325. MsTagInfo.CallerId = FaxStatus->CallerId;
  1326. }
  1327. MsTagInfo.Csid = NULL;
  1328. if (FaxStatus->CSI && (FaxStatus->CSI[0] != wcZero) ) {
  1329. MsTagInfo.Csid = FaxStatus->CSI;
  1330. }
  1331. MsTagInfo.Tsid = NULL;
  1332. if (FaxSend->CallerNumber && (FaxSend->CallerNumber[0] != wcZero) ) {
  1333. MsTagInfo.Tsid = FaxSend->CallerNumber;
  1334. }
  1335. MsTagInfo.FaxTime = SendTime;
  1336. return TiffAddMsTags( FaxFileName, &MsTagInfo );
  1337. }
  1338. BOOL
  1339. ArchivePrintJob(
  1340. LPTSTR FaxFileName
  1341. )
  1342. /*++
  1343. Routine Description:
  1344. Archive a tiff file that has been sent by copying the file to an archive
  1345. directory.
  1346. Arguments:
  1347. FaxFileName - Name of the file to archive
  1348. Return Value:
  1349. TRUE - The copy was made.
  1350. FALSE - The copy was not made.
  1351. --*/
  1352. {
  1353. BOOL rVal = FALSE;
  1354. WCHAR ArchiveFileName[MAX_PATH];
  1355. if (!ArchiveOutgoingFaxes) {
  1356. return FALSE;
  1357. }
  1358. //
  1359. // be sure that the dir exists
  1360. //
  1361. MakeDirectory( ArchiveDirectory );
  1362. //
  1363. // get the file name
  1364. //
  1365. if (GenerateUniqueFileName( ArchiveDirectory, NULL, ArchiveFileName, sizeof(ArchiveFileName)/sizeof(WCHAR)) != 0) {
  1366. rVal = TRUE;
  1367. }
  1368. if (rVal) {
  1369. rVal = CopyFile( FaxFileName, ArchiveFileName, FALSE );
  1370. }
  1371. if (rVal) {
  1372. FaxLog(
  1373. FAXLOG_CATEGORY_OUTBOUND,
  1374. FAXLOG_LEVEL_MAX,
  1375. 2,
  1376. MSG_FAX_ARCHIVE_SUCCESS,
  1377. FaxFileName,
  1378. ArchiveFileName
  1379. );
  1380. } else {
  1381. FaxLog(
  1382. FAXLOG_CATEGORY_OUTBOUND,
  1383. FAXLOG_LEVEL_MIN,
  1384. 3,
  1385. MSG_FAX_ARCHIVE_FAILED,
  1386. FaxFileName,
  1387. ArchiveFileName,
  1388. GetLastErrorText(GetLastError())
  1389. );
  1390. }
  1391. return rVal;
  1392. }
  1393. PVOID
  1394. MyGetJob(
  1395. HANDLE hPrinter,
  1396. DWORD level,
  1397. DWORD jobId
  1398. )
  1399. /*++
  1400. Routine Description:
  1401. Wrapper function for spooler API GetJob
  1402. Arguments:
  1403. hPrinter - Handle to the printer object
  1404. level - Level of JOB_INFO structure interested
  1405. jobId - Specifies the job ID
  1406. Return Value:
  1407. Pointer to a JOB_INFO structure, NULL if there is an error
  1408. --*/
  1409. {
  1410. PBYTE pJobInfo = NULL;
  1411. DWORD cbNeeded;
  1412. if (!GetJob(hPrinter, jobId, level, NULL, 0, &cbNeeded) &&
  1413. GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
  1414. (pJobInfo = MemAlloc(cbNeeded)) &&
  1415. GetJob(hPrinter, jobId, level, pJobInfo, cbNeeded, &cbNeeded))
  1416. {
  1417. return pJobInfo;
  1418. }
  1419. MemFree(pJobInfo);
  1420. return NULL;
  1421. }